changeset 14000:958c99d0790c

Split convert node into separate nodes for different conversions.
author Roland Schatz <roland.schatz@oracle.com>
date Fri, 21 Feb 2014 11:53:48 +0100
parents f2b300c6e621
children 79114edb5130
files graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Constant.java graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLoweringProvider.java graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXWrapperBuilder.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ArrayCopyCallNode.java graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java graal/com.oracle.graal.loop/src/com/oracle/graal/loop/BasicInductionVariable.java graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedOffsetInductionVariable.java graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedScaledInductionVariable.java graal/com.oracle.graal.loop/src/com/oracle/graal/loop/InductionVariable.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/NarrowNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ReinterpretNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SignExtendNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ZeroExtendNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/ArithmeticLIRGenerator.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/PrimitiveStamp.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampTool.java graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64ConvertNode.java graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64ConvertSnippets.java graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64FloatConvertNode.java graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ObjectAccessTest.java graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTest.java graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java
diffstat 33 files changed, 1315 insertions(+), 730 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Constant.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Constant.java	Fri Feb 21 11:53:48 2014 +0100
@@ -424,6 +424,18 @@
     }
 
     /**
+     * Creates a {@link Constant} from a primitive integer of a certain width.
+     */
+    public static Constant forPrimitiveInt(int bits, long i) {
+        assert bits <= 64;
+        if (bits > 32) {
+            return new Constant(Kind.Long, null, i);
+        } else {
+            return new Constant(Kind.Int, null, (int) i);
+        }
+    }
+
+    /**
      * Creates a boxed constant for the given kind from an Object. The object needs to be of the
      * Java boxed type corresponding to the kind.
      * 
--- a/graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java	Fri Feb 21 11:53:48 2014 +0100
@@ -312,9 +312,7 @@
         emitString(prefix + " $c0, " + mapRegOrConstToString(src0) + ", " + mapRegOrConstToString(src1) + ";" + comment);
     }
 
-    public void emitConvert(Value dest, Value src, Kind destKind, Kind srcKind) {
-        String destType = getArgTypeFromKind(destKind);
-        String srcType = getArgTypeFromKind(srcKind);
+    public void emitConvert(Value dest, Value src, String destType, String srcType) {
         String prefix = (destType.equals("f32") && srcType.equals("f64")) ? "cvt_near_" : "cvt_";
         emitString(prefix + destType + "_" + srcType + " " + HSAIL.mapRegister(dest) + ", " + HSAIL.mapRegister(src) + ";");
     }
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Fri Feb 21 11:53:48 2014 +0100
@@ -64,6 +64,7 @@
 import com.oracle.graal.lir.amd64.AMD64Move.StackLeaOp;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.calc.FloatConvertNode.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.phases.util.*;
 
@@ -173,7 +174,7 @@
 
             } else if (scaleEnum == null) {
                 /* Scale value that architecture cannot handle, so scale manually. */
-                Value longIndex = index.getKind().getStackKind() == Kind.Int ? emitConvert(Kind.Int, Kind.Long, index) : index;
+                Value longIndex = index.getKind() == Kind.Long ? index : emitSignExtend(index, 32, 64);
                 if (CodeUtil.isPowerOf2(scale)) {
                     indexRegister = emitShl(longIndex, Constant.forLong(CodeUtil.log2(scale)));
                 } else {
@@ -707,12 +708,6 @@
         }
     }
 
-    private AllocatableValue emitConvertMove(Kind kind, AllocatableValue input) {
-        Variable result = newVariable(kind);
-        emitMove(result, input);
-        return result;
-    }
-
     private AllocatableValue emitConvert1Op(Kind kind, AMD64Arithmetic op, AllocatableValue input) {
         Variable result = newVariable(kind);
         append(new Unary1Op(op, result, input));
@@ -725,111 +720,6 @@
         return result;
     }
 
-    @Override
-    public AllocatableValue emitConvert(Kind from, Kind to, Value inputVal) {
-        assert inputVal.getKind() == from.getStackKind();
-
-        AllocatableValue input = asAllocatable(inputVal);
-        if (from == to) {
-            return input;
-        }
-        switch (to) {
-            case Byte:
-                switch (from) {
-                    case Short:
-                    case Char:
-                    case Int:
-                    case Long:
-                        return emitConvert2Op(to, I2B, input);
-                    case Float:
-                    case Double:
-                        AllocatableValue intVal = emitConvert(from, Kind.Int, inputVal);
-                        return emitConvert(Kind.Int, to, intVal);
-                }
-                break;
-            case Char:
-                switch (from) {
-                    case Byte:
-                    case Short:
-                    case Int:
-                    case Long:
-                        return emitConvert1Op(to, I2C, input);
-                    case Float:
-                    case Double:
-                        AllocatableValue intVal = emitConvert(from, Kind.Int, inputVal);
-                        return emitConvert(Kind.Int, to, intVal);
-                }
-                break;
-            case Short:
-                switch (from) {
-                    case Byte:
-                    case Char:
-                    case Int:
-                    case Long:
-                        return emitConvert2Op(to, I2S, input);
-                    case Float:
-                    case Double:
-                        AllocatableValue intVal = emitConvert(from, Kind.Int, inputVal);
-                        return emitConvert(Kind.Int, to, intVal);
-                }
-                break;
-            case Int:
-                switch (from) {
-                    case Byte:
-                    case Short:
-                    case Char:
-                        return emitConvertMove(to, input);
-                    case Long:
-                        return emitConvert1Op(to, L2I, input);
-                    case Float:
-                        return emitConvert2Op(to, F2I, input);
-                    case Double:
-                        return emitConvert2Op(to, D2I, input);
-                }
-                break;
-            case Long:
-                switch (from) {
-                    case Byte:
-                    case Short:
-                    case Char:
-                    case Int:
-                        return emitConvert2Op(to, I2L, input);
-                    case Float:
-                        return emitConvert2Op(to, F2L, input);
-                    case Double:
-                        return emitConvert2Op(to, D2L, input);
-                }
-                break;
-            case Float:
-                switch (from) {
-                    case Byte:
-                    case Short:
-                    case Char:
-                    case Int:
-                        return emitConvert2Op(to, I2F, input);
-                    case Long:
-                        return emitConvert2Op(to, L2F, input);
-                    case Double:
-                        return emitConvert2Op(to, D2F, input);
-                }
-                break;
-            case Double:
-                switch (from) {
-                    case Byte:
-                    case Short:
-                    case Char:
-                    case Int:
-                        return emitConvert2Op(to, I2D, input);
-                    case Long:
-                        return emitConvert2Op(to, L2D, input);
-                    case Float:
-                        return emitConvert2Op(to, F2D, input);
-                }
-                break;
-        }
-        throw GraalInternalError.shouldNotReachHere();
-    }
-
     public AllocatableValue emitReinterpret(Kind to, Value inputVal) {
         Kind from = inputVal.getKind();
         AllocatableValue input = asAllocatable(inputVal);
@@ -842,40 +732,23 @@
             case Int:
                 switch (from) {
                     case Float:
-                    case Double:
                         return emitConvert2Op(to, MOV_F2I, input);
                 }
                 break;
             case Long:
                 switch (from) {
-                    case Float:
                     case Double:
                         return emitConvert2Op(to, MOV_D2L, input);
-                    case Int:
-                        /*
-                         * Unsigned int-to-long conversion: In theory, instructions that move or
-                         * generate 32-bit register values also set the upper 32 bits of the
-                         * register to zero. However, we cannot rely that the value was really
-                         * generated by an instruction, it could come from an untrusted source such
-                         * as native code. Therefore, make sure the high bits are really cleared.
-                         */
-                        Variable temp = newVariable(Kind.Int);
-                        Variable result = newVariable(Kind.Long);
-                        append(new BinaryRegConst(AMD64Arithmetic.IAND, temp, input, Constant.forInt(0xFFFFFFFF)));
-                        emitMove(result, temp);
-                        return result;
                 }
                 break;
             case Float:
                 switch (from) {
                     case Int:
-                    case Long:
                         return emitConvert2Op(to, MOV_I2F, input);
                 }
                 break;
             case Double:
                 switch (from) {
-                    case Int:
                     case Long:
                         return emitConvert2Op(to, MOV_L2D, input);
                 }
@@ -884,6 +757,101 @@
         throw GraalInternalError.shouldNotReachHere();
     }
 
+    public Value emitFloatConvert(FloatConvert op, Value inputVal) {
+        AllocatableValue input = asAllocatable(inputVal);
+        switch (op) {
+            case D2F:
+                return emitConvert2Op(Kind.Float, D2F, input);
+            case D2I:
+                return emitConvert2Op(Kind.Int, D2I, input);
+            case D2L:
+                return emitConvert2Op(Kind.Long, D2L, input);
+            case F2D:
+                return emitConvert2Op(Kind.Double, F2D, input);
+            case F2I:
+                return emitConvert2Op(Kind.Int, F2I, input);
+            case F2L:
+                return emitConvert2Op(Kind.Long, F2L, input);
+            case I2D:
+                return emitConvert2Op(Kind.Double, I2D, input);
+            case I2F:
+                return emitConvert2Op(Kind.Float, I2F, input);
+            case L2D:
+                return emitConvert2Op(Kind.Double, L2D, input);
+            case L2F:
+                return emitConvert2Op(Kind.Float, L2F, input);
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
+    @Override
+    public Value emitNarrow(Value inputVal, int bits) {
+        if (inputVal.getKind() == Kind.Long && bits <= 32) {
+            // TODO make it possible to reinterpret Long as Int in LIR without move
+            return emitConvert1Op(Kind.Int, L2I, asAllocatable(inputVal));
+        } else {
+            return inputVal;
+        }
+    }
+
+    @Override
+    public Value emitSignExtend(Value inputVal, int fromBits, int toBits) {
+        assert fromBits <= toBits && toBits <= 64;
+        if (fromBits == toBits) {
+            return inputVal;
+        } else if (toBits > 32) {
+            // sign extend to 64 bits
+            if (fromBits == 32) {
+                return emitConvert2Op(Kind.Long, I2L, asAllocatable(inputVal));
+            } else if (fromBits < 32) {
+                // TODO implement direct x2L sign extension conversions
+                Value intVal = emitSignExtend(inputVal, fromBits, 32);
+                return emitSignExtend(intVal, 32, toBits);
+            } else {
+                throw GraalInternalError.unimplemented("unsupported sign extension (" + fromBits + " bit -> " + toBits + " bit)");
+            }
+        } else {
+            // sign extend to 32 bits (smaller values are internally represented as 32 bit values)
+            switch (fromBits) {
+                case 8:
+                    return emitConvert2Op(Kind.Int, I2B, asAllocatable(inputVal));
+                case 16:
+                    return emitConvert2Op(Kind.Int, I2S, asAllocatable(inputVal));
+                case 32:
+                    return inputVal;
+                default:
+                    throw GraalInternalError.unimplemented("unsupported sign extension (" + fromBits + " bit -> " + toBits + " bit)");
+            }
+        }
+    }
+
+    @Override
+    public Value emitZeroExtend(Value inputVal, int fromBits, int toBits) {
+        assert fromBits <= toBits && toBits <= 64;
+        if (fromBits == toBits) {
+            return inputVal;
+        } else if (fromBits > 32) {
+            assert inputVal.getKind() == Kind.Long;
+            Variable result = newVariable(Kind.Long);
+            long mask = IntegerStamp.defaultMask(fromBits);
+            append(new BinaryRegConst(AMD64Arithmetic.LAND, result, asAllocatable(inputVal), Constant.forLong(mask)));
+            return result;
+        } else {
+            assert inputVal.getKind() == Kind.Int;
+            Variable result = newVariable(Kind.Int);
+            int mask = (int) IntegerStamp.defaultMask(fromBits);
+            append(new BinaryRegConst(AMD64Arithmetic.IAND, result, asAllocatable(inputVal), Constant.forInt(mask)));
+            if (toBits > 32) {
+                Variable longResult = newVariable(Kind.Long);
+                emitMove(longResult, result);
+                return longResult;
+            } else {
+                return result;
+            }
+        }
+    }
+
     @Override
     public void emitMembar(int barriers) {
         int necessaryBarriers = target().arch.requiredBarriers(barriers);
--- a/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Fri Feb 21 11:53:48 2014 +0100
@@ -53,6 +53,8 @@
 import com.oracle.graal.lir.hsail.HSAILMove.MoveToRegOp;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.calc.FloatConvertNode.FloatConvert;
+import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.phases.util.*;
 
 /**
@@ -145,7 +147,7 @@
             } else {
                 Value indexRegister;
                 Value convertedIndex;
-                convertedIndex = this.emitConvert(Kind.Int, Kind.Long, index);
+                convertedIndex = this.emitSignExtend(index, 32, 64);
                 if (scale != 1) {
                     indexRegister = emitUMul(convertedIndex, Constant.forInt(scale));
                 } else {
@@ -592,14 +594,91 @@
     }
 
     @Override
-    public Variable emitConvert(Kind from, Kind to, Value inputVal) {
+    public Value emitFloatConvert(FloatConvert op, Value inputVal) {
         Variable input = load(inputVal);
-        Variable result = newVariable(to);
+
+        String from;
+        switch (op) {
+            case D2F:
+            case D2I:
+            case D2L:
+                from = "f64";
+                break;
+            case F2D:
+            case F2I:
+            case F2L:
+                from = "f32";
+                break;
+            case I2D:
+            case I2F:
+                from = "s32";
+                break;
+            case L2D:
+            case L2F:
+                from = "s64";
+                break;
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+
+        Variable result;
+        String to;
+        switch (op) {
+            case D2I:
+            case F2I:
+                to = "s32";
+                result = newVariable(Kind.Int);
+                break;
+            case D2L:
+            case F2L:
+                to = "s64";
+                result = newVariable(Kind.Long);
+                break;
+            case F2D:
+            case I2D:
+            case L2D:
+                to = "f64";
+                result = newVariable(Kind.Double);
+                break;
+            case D2F:
+            case I2F:
+            case L2F:
+                to = "f32";
+                result = newVariable(Kind.Float);
+                break;
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+
         append(new ConvertOp(result, input, to, from));
         return result;
     }
 
     @Override
+    public Value emitNarrow(Value inputVal, int bits) {
+        Variable input = load(inputVal);
+        Variable result = newVariable(bits > 32 ? Kind.Long : Kind.Int);
+        append(new ConvertOp(result, input, "s" + bits, input.getKind() == Kind.Long ? "s64" : "s32"));
+        return result;
+    }
+
+    @Override
+    public Value emitSignExtend(Value inputVal, int fromBits, int toBits) {
+        Variable input = load(inputVal);
+        Variable result = newVariable(toBits > 32 ? Kind.Long : Kind.Int);
+        append(new ConvertOp(result, input, "s" + toBits, "s" + fromBits));
+        return result;
+    }
+
+    @Override
+    public Value emitZeroExtend(Value inputVal, int fromBits, int toBits) {
+        Variable input = load(inputVal);
+        Variable result = newVariable(toBits > 32 ? Kind.Long : Kind.Int);
+        append(new ConvertOp(result, input, "u" + toBits, "u" + fromBits));
+        return result;
+    }
+
+    @Override
     public Value emitReinterpret(Kind to, Value inputVal) {
         Variable result = newVariable(to);
         emitMove(result, inputVal);
@@ -816,7 +895,7 @@
 
     @Override
     public void emitNullCheck(ValueNode v, DeoptimizingNode deopting) {
-        assert v.kind() == Kind.Object;
+        assert v.stamp() instanceof ObjectStamp;
         Variable obj = newVariable(Kind.Object);
         emitMove(obj, operand(v));
         append(new HSAILMove.NullCheckOp(obj, state(deopting)));
--- a/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Fri Feb 21 11:53:48 2014 +0100
@@ -62,8 +62,10 @@
 import com.oracle.graal.lir.ptx.PTXMove.MoveToRegOp;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.calc.FloatConvertNode.FloatConvert;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.phases.util.*;
 
 /**
@@ -232,7 +234,7 @@
                 Value convertedIndex;
                 Value indexRegister;
 
-                convertedIndex = emitConvert(Kind.Int, Kind.Long, index);
+                convertedIndex = emitSignExtend(index, 32, 64);
                 if (scale != 1) {
                     if (CodeUtil.isPowerOf2(scale)) {
                         indexRegister = emitShl(convertedIndex, Constant.forInt(CodeUtil.log2(scale)));
@@ -681,14 +683,110 @@
         return result;
     }
 
-    @Override
-    public Variable emitConvert(Kind from, Kind to, Value inputVal) {
+    public Variable emitConvertOp(Kind from, Kind to, Value inputVal) {
         Variable input = load(inputVal);
         Variable result = newVariable(to);
         append(new ConvertOp(result, input, to, from));
         return result;
     }
 
+    @Override
+    public Value emitFloatConvert(FloatConvert op, Value inputVal) {
+        switch (op) {
+            case D2F:
+                return emitConvertOp(Kind.Double, Kind.Float, inputVal);
+            case D2I:
+                return emitConvertOp(Kind.Double, Kind.Int, inputVal);
+            case D2L:
+                return emitConvertOp(Kind.Double, Kind.Long, inputVal);
+            case F2D:
+                return emitConvertOp(Kind.Float, Kind.Double, inputVal);
+            case F2I:
+                return emitConvertOp(Kind.Float, Kind.Int, inputVal);
+            case F2L:
+                return emitConvertOp(Kind.Float, Kind.Long, inputVal);
+            case I2D:
+                return emitConvertOp(Kind.Int, Kind.Double, inputVal);
+            case I2F:
+                return emitConvertOp(Kind.Int, Kind.Float, inputVal);
+            case L2D:
+                return emitConvertOp(Kind.Long, Kind.Double, inputVal);
+            case L2F:
+                return emitConvertOp(Kind.Long, Kind.Float, inputVal);
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
+    @Override
+    public Value emitNarrow(Value inputVal, int bits) {
+        if (inputVal.getKind() == Kind.Long && bits <= 32) {
+            return emitConvertOp(Kind.Long, Kind.Int, inputVal);
+        } else {
+            return inputVal;
+        }
+    }
+
+    @Override
+    public Value emitSignExtend(Value inputVal, int fromBits, int toBits) {
+        assert fromBits <= toBits && toBits <= 64;
+        if (fromBits == toBits) {
+            return inputVal;
+        } else if (toBits > 32) {
+            // sign extend to 64 bits
+            switch (fromBits) {
+                case 8:
+                    return emitConvertOp(Kind.Byte, Kind.Long, inputVal);
+                case 16:
+                    return emitConvertOp(Kind.Short, Kind.Long, inputVal);
+                case 32:
+                    return emitConvertOp(Kind.Int, Kind.Long, inputVal);
+                case 64:
+                    return inputVal;
+                default:
+                    throw GraalInternalError.unimplemented("unsupported sign extension (" + fromBits + " bit -> " + toBits + " bit)");
+            }
+        } else {
+            // sign extend to 32 bits (smaller values are internally represented as 32 bit values)
+            switch (fromBits) {
+                case 8:
+                    return emitConvertOp(Kind.Byte, Kind.Int, inputVal);
+                case 16:
+                    return emitConvertOp(Kind.Short, Kind.Int, inputVal);
+                case 32:
+                    return inputVal;
+                default:
+                    throw GraalInternalError.unimplemented("unsupported sign extension (" + fromBits + " bit -> " + toBits + " bit)");
+            }
+        }
+    }
+
+    @Override
+    public Value emitZeroExtend(Value inputVal, int fromBits, int toBits) {
+        assert fromBits <= toBits && toBits <= 64;
+        if (fromBits == toBits) {
+            return inputVal;
+        } else if (fromBits > 32) {
+            assert inputVal.getKind() == Kind.Long;
+            Variable result = newVariable(Kind.Long);
+            long mask = IntegerStamp.defaultMask(fromBits);
+            append(new Op2Stack(LAND, result, inputVal, Constant.forLong(mask)));
+            return result;
+        } else {
+            assert inputVal.getKind() == Kind.Int;
+            Variable result = newVariable(Kind.Int);
+            int mask = (int) IntegerStamp.defaultMask(fromBits);
+            append(new Op2Stack(IAND, result, inputVal, Constant.forInt(mask)));
+            if (toBits > 32) {
+                Variable longResult = newVariable(Kind.Long);
+                emitMove(longResult, result);
+                return longResult;
+            } else {
+                return result;
+            }
+        }
+    }
+
     public Value emitReinterpret(Kind to, Value inputVal) {
         Variable result = newVariable(to);
         emitMove(result, inputVal);
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Fri Feb 21 11:53:48 2014 +0100
@@ -60,7 +60,9 @@
 import com.oracle.graal.lir.sparc.SPARCMove.StackLoadAddressOp;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.calc.FloatConvertNode.FloatConvert;
 import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.phases.util.*;
 
 /**
@@ -153,8 +155,7 @@
                 indexRegister = Value.ILLEGAL;
             } else {
                 if (scale != 1) {
-                    // Variable longIndex = newVariable(Kind.Long);
-                    AllocatableValue longIndex = emitConvert(Kind.Int, Kind.Long, index);
+                    Value longIndex = emitSignExtend(index, 32, 64);
                     if (CodeUtil.isPowerOf2(scale)) {
                         indexRegister = emitShl(longIndex, Constant.forLong(CodeUtil.log2(scale)));
                     } else {
@@ -766,108 +767,98 @@
     }
 
     @Override
-    public AllocatableValue emitConvert(Kind from, Kind to, Value inputVal) {
-        assert inputVal.getKind() == from.getStackKind();
+    public Value emitFloatConvert(FloatConvert op, Value inputVal) {
+        AllocatableValue input = asAllocatable(inputVal);
+        switch (op) {
+            case D2F:
+                return emitConvert2Op(Kind.Float, D2F, input);
+            case D2I:
+                return emitConvert2Op(Kind.Int, D2I, input);
+            case D2L:
+                return emitConvert2Op(Kind.Long, D2L, input);
+            case F2D:
+                return emitConvert2Op(Kind.Double, F2D, input);
+            case F2I:
+                return emitConvert2Op(Kind.Int, F2I, input);
+            case F2L:
+                return emitConvert2Op(Kind.Long, F2L, input);
+            case I2D:
+                return emitConvert2Op(Kind.Double, I2D, input);
+            case I2F:
+                return emitConvert2Op(Kind.Float, I2F, input);
+            case L2D:
+                return emitConvert2Op(Kind.Double, L2D, input);
+            case L2F:
+                return emitConvert2Op(Kind.Float, L2F, input);
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+    }
 
-        AllocatableValue input = asAllocatable(inputVal);
-        if (from == to) {
-            return input;
+    @Override
+    public Value emitNarrow(Value inputVal, int bits) {
+        if (inputVal.getKind() == Kind.Long && bits <= 32) {
+            return emitConvert2Op(Kind.Int, L2I, asAllocatable(inputVal));
+        } else {
+            return inputVal;
         }
-        switch (to) {
-            case Byte:
-                switch (from) {
-                    case Short:
-                    case Char:
-                    case Int:
-                    case Long:
-                        return emitConvert2Op(to, I2B, input);
-                    case Float:
-                    case Double:
-                        AllocatableValue intVal = emitConvert(from, Kind.Int, inputVal);
-                        return emitConvert(Kind.Int, to, intVal);
-                }
-                break;
-            case Char:
-                switch (from) {
-                    case Byte:
-                    case Short:
-                    case Int:
-                    case Long:
-                        return emitConvert2Op(to, I2C, input);
-                    case Float:
-                    case Double:
-                        AllocatableValue intVal = emitConvert(from, Kind.Int, inputVal);
-                        return emitConvert(Kind.Int, to, intVal);
-                }
-                break;
-            case Short:
-                switch (from) {
-                    case Byte:
-                    case Char:
-                    case Int:
-                    case Long:
-                        return emitConvert2Op(to, I2S, input);
-                    case Float:
-                    case Double:
-                        AllocatableValue intVal = emitConvert(from, Kind.Int, inputVal);
-                        return emitConvert(Kind.Int, to, intVal);
-                }
-                break;
-            case Int:
-                switch (from) {
-                    case Byte:
-                    case Short:
-                    case Char:
-                        return emitConvertMove(to, input);
-                    case Long:
-                        return emitConvert2Op(to, L2I, input);
-                    case Float:
-                        return emitConvert2Op(to, F2I, input);
-                    case Double:
-                        return emitConvert2Op(to, D2I, input);
-                }
-                break;
-            case Long:
-                switch (from) {
-                    case Byte:
-                    case Short:
-                    case Char:
-                    case Int:
-                        return emitConvert2Op(to, I2L, input);
-                    case Float:
-                        return emitConvert2Op(to, F2L, input);
-                    case Double:
-                        return emitConvert2Op(to, D2L, input);
-                }
-                break;
-            case Float:
-                switch (from) {
-                    case Byte:
-                    case Short:
-                    case Char:
-                    case Int:
-                        return emitConvert2Op(to, I2F, input);
-                    case Long:
-                        return emitConvert2Op(to, L2F, input);
-                    case Double:
-                        return emitConvert2Op(to, D2F, input);
-                }
-                break;
-            case Double:
-                switch (from) {
-                    case Byte:
-                    case Short:
-                    case Char:
-                    case Int:
-                        return emitConvert2Op(to, I2D, input);
-                    case Long:
-                        return emitConvert2Op(to, L2D, input);
-                    case Float:
-                        return emitConvert2Op(to, F2D, input);
-                }
-                break;
+    }
+
+    @Override
+    public Value emitSignExtend(Value inputVal, int fromBits, int toBits) {
+        assert fromBits <= toBits && toBits <= 64;
+        if (fromBits == toBits) {
+            return inputVal;
+        } else if (toBits > 32) {
+            // sign extend to 64 bits
+            if (fromBits == 32) {
+                return emitConvert2Op(Kind.Long, I2L, asAllocatable(inputVal));
+            } else if (fromBits < 32) {
+                // TODO implement direct x2L sign extension conversions
+                Value intVal = emitSignExtend(inputVal, fromBits, 32);
+                return emitSignExtend(intVal, 32, toBits);
+            } else {
+                throw GraalInternalError.unimplemented("unsupported sign extension (" + fromBits + " bit -> " + toBits + " bit)");
+            }
+        } else {
+            // sign extend to 32 bits (smaller values are internally represented as 32 bit values)
+            switch (fromBits) {
+                case 8:
+                    return emitConvert2Op(Kind.Int, I2B, asAllocatable(inputVal));
+                case 16:
+                    return emitConvert2Op(Kind.Int, I2S, asAllocatable(inputVal));
+                case 32:
+                    return inputVal;
+                default:
+                    throw GraalInternalError.unimplemented("unsupported sign extension (" + fromBits + " bit -> " + toBits + " bit)");
+            }
         }
-        throw GraalInternalError.shouldNotReachHere();
+    }
+
+    @Override
+    public Value emitZeroExtend(Value inputVal, int fromBits, int toBits) {
+        assert fromBits <= toBits && toBits <= 64;
+        if (fromBits == toBits) {
+            return inputVal;
+        } else if (fromBits > 32) {
+            assert inputVal.getKind() == Kind.Long;
+            Variable result = newVariable(Kind.Long);
+            long mask = IntegerStamp.defaultMask(fromBits);
+            append(new BinaryRegConst(SPARCArithmetic.LAND, result, asAllocatable(inputVal), Constant.forLong(mask)));
+            return result;
+        } else {
+            assert inputVal.getKind() == Kind.Int;
+            Variable result = newVariable(Kind.Int);
+            int mask = (int) IntegerStamp.defaultMask(fromBits);
+            append(new BinaryRegConst(SPARCArithmetic.IAND, result, asAllocatable(inputVal), Constant.forInt(mask)));
+            if (toBits > 32) {
+                Variable longResult = newVariable(Kind.Long);
+                emitMove(longResult, result);
+                return longResult;
+            } else {
+                return result;
+            }
+        }
     }
 
     public AllocatableValue emitReinterpret(Kind to, Value inputVal) {
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLoweringProvider.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLoweringProvider.java	Fri Feb 21 11:53:48 2014 +0100
@@ -47,8 +47,8 @@
 
     @Override
     public void lower(Node n, LoweringTool tool) {
-        if (n instanceof ConvertNode) {
-            convertSnippets.lower((ConvertNode) n, tool);
+        if (n instanceof FloatConvertNode) {
+            convertSnippets.lower((FloatConvertNode) n, tool);
         } else {
             super.lower(n, tool);
         }
--- a/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXWrapperBuilder.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXWrapperBuilder.java	Fri Feb 21 11:53:48 2014 +0100
@@ -264,18 +264,18 @@
             case Short:
             case Char:
             case Int:
-                returnValue = unique(new ConvertNode(Kind.Long, Kind.Int, result));
+                returnValue = unique(new NarrowNode(result, 32));
                 break;
             case Long:
                 returnValue = result;
                 break;
             case Float: {
-                ValueNode asInt = unique(new ConvertNode(Kind.Long, Kind.Int, result));
-                returnValue = unique(new ReinterpretNode(Kind.Float, asInt));
+                ValueNode asInt = unique(new NarrowNode(result, 32));
+                returnValue = ReinterpretNode.reinterpret(Kind.Float, asInt);
                 break;
             }
             case Double:
-                returnValue = unique(new ReinterpretNode(returnKind, result));
+                returnValue = ReinterpretNode.reinterpret(Kind.Double, result);
                 break;
             case Object:
                 getObjectResult = createInvoke(getClass(), "getObjectResult", args.get(Thread));
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ArrayCopyCallNode.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ArrayCopyCallNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -140,8 +140,8 @@
             ValueNode srcAddr = computeBase(getSource(), getSourcePosition());
             ValueNode destAddr = computeBase(getDestination(), getDestinationPosition());
             ValueNode len = getLength();
-            if (len.kind() != Kind.Long) {
-                len = graph().unique(new ConvertNode(len.kind(), Kind.Long, len));
+            if (len.stamp().getStackKind() != Kind.Long) {
+                len = IntegerConvertNode.convert(len, StampFactory.forKind(Kind.Long));
             }
             ForeignCallNode call = graph.add(new ForeignCallNode(Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getForeignCalls(), desc, srcAddr, destAddr, len));
             call.setStateAfter(stateAfter());
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri Feb 21 11:53:48 2014 +0100
@@ -44,6 +44,7 @@
 import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.calc.FloatConvertNode.FloatConvert;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
@@ -708,9 +709,30 @@
             frameState.ipush(append(new NormalizeCompareNode(x, y, isUnorderedLess)));
         }
 
-        private void genConvert(Kind from, Kind to) {
+        private void genFloatConvert(FloatConvert op, Kind from, Kind to) {
+            ValueNode input = frameState.pop(from.getStackKind());
+            frameState.push(to.getStackKind(), append(new FloatConvertNode(op, input)));
+        }
+
+        private void genSignExtend(Kind from, Kind to) {
             ValueNode input = frameState.pop(from.getStackKind());
-            frameState.push(to.getStackKind(), append(new ConvertNode(from, to, input)));
+            if (from != from.getStackKind()) {
+                input = append(new NarrowNode(input, from.getBitCount()));
+            }
+            frameState.push(to.getStackKind(), append(new SignExtendNode(input, to.getBitCount())));
+        }
+
+        private void genZeroExtend(Kind from, Kind to) {
+            ValueNode input = frameState.pop(from.getStackKind());
+            if (from != from.getStackKind()) {
+                input = append(new NarrowNode(input, from.getBitCount()));
+            }
+            frameState.push(to.getStackKind(), append(new ZeroExtendNode(input, to.getBitCount())));
+        }
+
+        private void genNarrow(Kind from, Kind to) {
+            ValueNode input = frameState.pop(from.getStackKind());
+            frameState.push(to.getStackKind(), append(new NarrowNode(input, to.getBitCount())));
         }
 
         private void genIncrement() {
@@ -1991,21 +2013,21 @@
             case LOR            : // fall through
             case LXOR           : genLogicOp(Kind.Long, opcode); break;
             case IINC           : genIncrement(); break;
-            case I2L            : genConvert(Kind.Int, Kind.Long); break;
-            case I2F            : genConvert(Kind.Int, Kind.Float); break;
-            case I2D            : genConvert(Kind.Int, Kind.Double); break;
-            case L2I            : genConvert(Kind.Long, Kind.Int); break;
-            case L2F            : genConvert(Kind.Long, Kind.Float); break;
-            case L2D            : genConvert(Kind.Long, Kind.Double); break;
-            case F2I            : genConvert(Kind.Float, Kind.Int); break;
-            case F2L            : genConvert(Kind.Float, Kind.Long); break;
-            case F2D            : genConvert(Kind.Float, Kind.Double); break;
-            case D2I            : genConvert(Kind.Double, Kind.Int); break;
-            case D2L            : genConvert(Kind.Double, Kind.Long); break;
-            case D2F            : genConvert(Kind.Double, Kind.Float); break;
-            case I2B            : genConvert(Kind.Int, Kind.Byte); break;
-            case I2C            : genConvert(Kind.Int, Kind.Char); break;
-            case I2S            : genConvert(Kind.Int, Kind.Short); break;
+            case I2F            : genFloatConvert(FloatConvert.I2F, Kind.Int, Kind.Float); break;
+            case I2D            : genFloatConvert(FloatConvert.I2D, Kind.Int, Kind.Double); break;
+            case L2F            : genFloatConvert(FloatConvert.L2F, Kind.Long, Kind.Float); break;
+            case L2D            : genFloatConvert(FloatConvert.L2D, Kind.Long, Kind.Double); break;
+            case F2I            : genFloatConvert(FloatConvert.F2I, Kind.Float, Kind.Int); break;
+            case F2L            : genFloatConvert(FloatConvert.F2L, Kind.Float, Kind.Long); break;
+            case F2D            : genFloatConvert(FloatConvert.F2D, Kind.Float, Kind.Double); break;
+            case D2I            : genFloatConvert(FloatConvert.D2I, Kind.Double, Kind.Int); break;
+            case D2L            : genFloatConvert(FloatConvert.D2L, Kind.Double, Kind.Long); break;
+            case D2F            : genFloatConvert(FloatConvert.D2F, Kind.Double, Kind.Float); break;
+            case L2I            : genNarrow(Kind.Long, Kind.Int); break;
+            case I2L            : genSignExtend(Kind.Int, Kind.Long); break;
+            case I2B            : genSignExtend(Kind.Byte, Kind.Int); break;
+            case I2S            : genSignExtend(Kind.Short, Kind.Int); break;
+            case I2C            : genZeroExtend(Kind.Char, Kind.Int); break;
             case LCMP           : genCompareOp(Kind.Long, false); break;
             case FCMPL          : genCompareOp(Kind.Float, true); break;
             case FCMPG          : genCompareOp(Kind.Float, false); break;
--- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java	Fri Feb 21 11:53:48 2014 +0100
@@ -105,12 +105,12 @@
     UNDEF;
 
     public static class ConvertOp extends HSAILLIRInstruction {
-        private final Kind from;
-        private final Kind to;
+        private final String from;
+        private final String to;
         @Def({REG}) protected AllocatableValue result;
         @Use({REG, STACK}) protected AllocatableValue x;
 
-        public ConvertOp(AllocatableValue result, AllocatableValue x, Kind to, Kind from) {
+        public ConvertOp(AllocatableValue result, AllocatableValue x, String to, String from) {
             this.from = from;
             this.to = to;
             this.result = result;
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/BasicInductionVariable.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/BasicInductionVariable.java	Fri Feb 21 11:53:48 2014 +0100
@@ -22,7 +22,6 @@
  */
 package com.oracle.graal.loop;
 
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
@@ -119,28 +118,28 @@
     }
 
     @Override
-    public ValueNode extremumNode(boolean assumePositiveTripCount, Kind kind) {
-        Kind fromKind = phi.kind();
+    public ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp) {
+        Stamp fromStamp = phi.stamp();
         StructuredGraph graph = graph();
         ValueNode stride = strideNode();
         ValueNode initNode = this.initNode();
-        if (fromKind != kind) {
-            stride = graph.unique(new ConvertNode(fromKind, kind, stride));
-            initNode = graph.unique(new ConvertNode(fromKind, kind, initNode));
+        if (!fromStamp.isCompatible(stamp)) {
+            stride = IntegerConvertNode.convert(stride, stamp);
+            initNode = IntegerConvertNode.convert(initNode, stamp);
         }
         ValueNode maxTripCount = loop.counted().maxTripCountNode(assumePositiveTripCount);
-        if (maxTripCount.kind() != kind) {
-            maxTripCount = graph.unique(new ConvertNode(maxTripCount.kind(), kind, maxTripCount));
+        if (!maxTripCount.stamp().isCompatible(stamp)) {
+            maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp);
         }
-        return IntegerArithmeticNode.add(graph, IntegerArithmeticNode.mul(graph, stride, IntegerArithmeticNode.sub(graph, maxTripCount, ConstantNode.forIntegerKind(kind, 1, graph))), initNode);
+        return IntegerArithmeticNode.add(graph, IntegerArithmeticNode.mul(graph, stride, IntegerArithmeticNode.sub(graph, maxTripCount, ConstantNode.forIntegerStamp(stamp, 1, graph))), initNode);
     }
 
     @Override
     public ValueNode exitValueNode() {
-        Kind kind = phi.kind();
+        Stamp stamp = phi.stamp();
         ValueNode maxTripCount = loop.counted().maxTripCountNode(false);
-        if (maxTripCount.kind() != kind) {
-            maxTripCount = graph().unique(new ConvertNode(maxTripCount.kind(), kind, maxTripCount));
+        if (!maxTripCount.stamp().isCompatible(stamp)) {
+            maxTripCount = IntegerConvertNode.convert(maxTripCount, stamp);
         }
         return IntegerArithmeticNode.add(graph(), IntegerArithmeticNode.mul(graph(), strideNode(), maxTripCount), initNode());
     }
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedOffsetInductionVariable.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedOffsetInductionVariable.java	Fri Feb 21 11:53:48 2014 +0100
@@ -22,10 +22,10 @@
  */
 package com.oracle.graal.loop;
 
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.type.*;
 
 public class DerivedOffsetInductionVariable extends InductionVariable {
 
@@ -92,8 +92,8 @@
     }
 
     @Override
-    public ValueNode extremumNode(boolean assumePositiveTripCount, Kind kind) {
-        return op(base.extremumNode(assumePositiveTripCount, kind), ConvertNode.convert(graph(), kind, offset));
+    public ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp) {
+        return op(base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(offset, stamp));
     }
 
     @Override
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedScaledInductionVariable.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/DerivedScaledInductionVariable.java	Fri Feb 21 11:53:48 2014 +0100
@@ -22,7 +22,6 @@
  */
 package com.oracle.graal.loop;
 
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.type.*;
@@ -102,8 +101,8 @@
     }
 
     @Override
-    public ValueNode extremumNode(boolean assumePositiveTripCount, Kind kind) {
-        return IntegerArithmeticNode.mul(graph(), base.extremumNode(assumePositiveTripCount, kind), ConvertNode.convert(graph(), kind, scale));
+    public ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp) {
+        return IntegerArithmeticNode.mul(graph(), base.extremumNode(assumePositiveTripCount, stamp), IntegerConvertNode.convert(scale, stamp));
     }
 
     @Override
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/InductionVariable.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/InductionVariable.java	Fri Feb 21 11:53:48 2014 +0100
@@ -22,9 +22,9 @@
  */
 package com.oracle.graal.loop;
 
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.type.*;
 
 /**
  * This class describes a value node that is an induction variable in a counted loop.
@@ -89,10 +89,10 @@
      * induction variable in the loop body of the last iteration.
      */
     public ValueNode extremumNode() {
-        return extremumNode(false, valueNode().kind());
+        return extremumNode(false, valueNode().stamp());
     }
 
-    public abstract ValueNode extremumNode(boolean assumePositiveTripCount, Kind kind);
+    public abstract ValueNode extremumNode(boolean assumePositiveTripCount, Stamp stamp);
 
     public abstract boolean isConstantExtremum();
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -152,7 +152,7 @@
     }
 
     /**
-     * Returns a node for a primitive constant.
+     * Returns a node for a Java primitive.
      */
     public static ConstantNode forPrimitive(Constant constant, StructuredGraph graph) {
         assert constant.getKind() != Kind.Object;
@@ -255,6 +255,33 @@
         return graph.unique(node);
     }
 
+    /**
+     * Returns a node for a constant integer that's not directly representable as Java primitive
+     * (e.g. short).
+     */
+    public static ConstantNode forIntegerBits(int bits, boolean unsigned, long value, StructuredGraph graph) {
+        Constant constant = Constant.forPrimitiveInt(bits, value);
+        long bounds;
+        if (unsigned) {
+            bounds = ZeroExtendNode.zeroExtend(value, bits);
+        } else {
+            bounds = SignExtendNode.signExtend(value, bits);
+        }
+        return unique(graph, new ConstantNode(constant, StampFactory.forInteger(bits, unsigned, bounds, bounds)));
+    }
+
+    /**
+     * Returns a node for a constant integer that's compatible to a given stamp.
+     */
+    public static ConstantNode forIntegerStamp(Stamp stamp, long value, StructuredGraph graph) {
+        if (stamp instanceof IntegerStamp) {
+            IntegerStamp intStamp = (IntegerStamp) stamp;
+            return forIntegerBits(intStamp.getBits(), intStamp.isUnsigned(), value, graph);
+        } else {
+            return forIntegerKind(stamp.getStackKind(), value, graph);
+        }
+    }
+
     public static ConstantNode forIntegerKind(Kind kind, long value, StructuredGraph graph) {
         switch (kind) {
             case Byte:
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -136,15 +136,15 @@
         if (x() instanceof ConvertNode && y() instanceof ConvertNode) {
             ConvertNode convertX = (ConvertNode) x();
             ConvertNode convertY = (ConvertNode) y();
-            if (convertX.isLossless() && convertY.isLossless() && convertX.getFromKind() == convertY.getFromKind()) {
-                setX(convertX.value());
-                setY(convertY.value());
+            if (convertX.isLossless() && convertY.isLossless() && convertX.getInput().stamp().isCompatible(convertY.getInput().stamp())) {
+                setX(convertX.getInput());
+                setY(convertY.getInput());
             }
         } else if (x() instanceof ConvertNode && y().isConstant()) {
             ConvertNode convertX = (ConvertNode) x();
             ConstantNode newY = canonicalConvertConstant(convertX, y().asConstant());
             if (newY != null) {
-                setX(convertX.value());
+                setX(convertX.getInput());
                 setY(newY);
             }
         } else if (y() instanceof ConvertNode && x().isConstant()) {
@@ -152,7 +152,7 @@
             ConstantNode newX = canonicalConvertConstant(convertY, x().asConstant());
             if (newX != null) {
                 setX(newX);
-                setY(convertY.value());
+                setY(convertY.getInput());
             }
         }
         return this;
@@ -160,9 +160,8 @@
 
     private static ConstantNode canonicalConvertConstant(ConvertNode convert, Constant constant) {
         if (convert.isLossless()) {
-            assert constant.getKind() == convert.getToKind();
-            Constant reverseConverted = ConvertNode.convert(convert.getToKind(), convert.getFromKind(), constant);
-            if (convert.evalConst(reverseConverted).equals(constant)) {
+            Constant reverseConverted = convert.reverse(constant);
+            if (convert.convert(reverseConverted).equals(constant)) {
                 return ConstantNode.forPrimitive(reverseConverted, convert.graph());
             }
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,277 +22,36 @@
  */
 package com.oracle.graal.nodes.calc;
 
+import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 
 /**
- * The {@code ConvertNode} class represents a conversion between primitive types.
+ * Represents a conversion between primitive types.
  */
-public class ConvertNode extends FloatingNode implements Canonicalizable, Lowerable, ArithmeticLIRLowerable {
-
-    @Input private ValueNode value;
-
-    private final Kind from;
-    private final Kind to;
-
-    public ValueNode value() {
-        return value;
-    }
-
-    /**
-     * Constructs a new Convert instance.
-     * 
-     * @param from the kind of the incoming value
-     * @param to the result kind
-     * @param value the instruction producing the input value
-     */
-    public ConvertNode(Kind from, Kind to, ValueNode value) {
-        super(StampFactory.forKind(to.getStackKind()));
-        assert value.kind() == from.getStackKind() : "convert(" + from + ", " + to + ") : " + value.kind() + " != " + from;
-        this.from = from;
-        this.to = to;
-        this.value = value;
-    }
+public abstract class ConvertNode extends FloatingNode implements ArithmeticOperation {
 
-    public Kind getFromKind() {
-        return from;
-    }
-
-    public Kind getToKind() {
-        return to;
-    }
+    @Input private ValueNode input;
 
-    public boolean isLossless() {
-        if (from == to) {
-            return true;
-        }
-        switch (from) {
-            case Byte:
-                return true;
-            case Short:
-            case Char:
-                return to != Kind.Byte;
-            case Int:
-                return to == Kind.Long || to == Kind.Double;
-            case Float:
-                return to == Kind.Double;
-            case Long:
-            case Double:
-                return false;
-        }
-        throw GraalInternalError.shouldNotReachHere();
+    protected ConvertNode(Stamp stamp, ValueNode input) {
+        super(stamp);
+        this.input = input;
     }
 
-    public static Constant convert(Kind from, Kind to, Constant c) {
-        switch (from) {
-            case Byte:
-                byte byteVal = (byte) c.asInt();
-                switch (to) {
-                    case Byte:
-                        return Constant.forByte(byteVal);
-                    case Short:
-                        return Constant.forShort(byteVal);
-                    case Char:
-                        return Constant.forChar((char) byteVal);
-                    case Int:
-                        return Constant.forInt(byteVal);
-                    case Long:
-                        return Constant.forLong(byteVal);
-                    case Float:
-                        return Constant.forFloat(byteVal);
-                    case Double:
-                        return Constant.forDouble(byteVal);
-                }
-                break;
-            case Char:
-                char charVal = (char) c.asInt();
-                switch (to) {
-                    case Byte:
-                        return Constant.forByte((byte) charVal);
-                    case Short:
-                        return Constant.forShort((short) charVal);
-                    case Char:
-                        return Constant.forChar(charVal);
-                    case Int:
-                        return Constant.forInt(charVal);
-                    case Long:
-                        return Constant.forLong(charVal);
-                    case Float:
-                        return Constant.forFloat(charVal);
-                    case Double:
-                        return Constant.forDouble(charVal);
-                }
-                break;
-            case Short:
-                short shortVal = (short) c.asInt();
-                switch (to) {
-                    case Byte:
-                        return Constant.forByte((byte) shortVal);
-                    case Short:
-                        return Constant.forShort(shortVal);
-                    case Char:
-                        return Constant.forChar((char) shortVal);
-                    case Int:
-                        return Constant.forInt(shortVal);
-                    case Long:
-                        return Constant.forLong(shortVal);
-                    case Float:
-                        return Constant.forFloat(shortVal);
-                    case Double:
-                        return Constant.forDouble(shortVal);
-                }
-                break;
-            case Int:
-                int intVal = c.asInt();
-                switch (to) {
-                    case Byte:
-                        return Constant.forByte((byte) intVal);
-                    case Short:
-                        return Constant.forShort((short) intVal);
-                    case Char:
-                        return Constant.forChar((char) intVal);
-                    case Int:
-                        return Constant.forInt(intVal);
-                    case Long:
-                        return Constant.forLong(intVal);
-                    case Float:
-                        return Constant.forFloat(intVal);
-                    case Double:
-                        return Constant.forDouble(intVal);
-                }
-                break;
-            case Long:
-                long longVal = c.asLong();
-                switch (to) {
-                    case Byte:
-                        return Constant.forByte((byte) longVal);
-                    case Short:
-                        return Constant.forShort((short) longVal);
-                    case Char:
-                        return Constant.forChar((char) longVal);
-                    case Int:
-                        return Constant.forInt((int) longVal);
-                    case Long:
-                        return Constant.forLong(longVal);
-                    case Float:
-                        return Constant.forFloat(longVal);
-                    case Double:
-                        return Constant.forDouble(longVal);
-                }
-                break;
-            case Float:
-                float floatVal = c.asFloat();
-                switch (to) {
-                    case Byte:
-                        return Constant.forByte((byte) floatVal);
-                    case Short:
-                        return Constant.forShort((short) floatVal);
-                    case Char:
-                        return Constant.forChar((char) floatVal);
-                    case Int:
-                        return Constant.forInt((int) floatVal);
-                    case Long:
-                        return Constant.forLong((long) floatVal);
-                    case Float:
-                        return Constant.forFloat(floatVal);
-                    case Double:
-                        return Constant.forDouble(floatVal);
-                }
-                break;
-            case Double:
-                double doubleVal = c.asDouble();
-                switch (to) {
-                    case Byte:
-                        return Constant.forByte((byte) doubleVal);
-                    case Short:
-                        return Constant.forShort((short) doubleVal);
-                    case Char:
-                        return Constant.forChar((char) doubleVal);
-                    case Int:
-                        return Constant.forInt((int) doubleVal);
-                    case Long:
-                        return Constant.forLong((long) doubleVal);
-                    case Float:
-                        return Constant.forFloat((float) doubleVal);
-                    case Double:
-                        return Constant.forDouble(doubleVal);
-                }
-                break;
-        }
-        throw GraalInternalError.shouldNotReachHere();
+    public ValueNode getInput() {
+        return input;
     }
 
-    public Constant evalConst(Constant... inputs) {
-        assert inputs.length == 1;
-        return convert(from, to, inputs[0]);
-    }
+    public abstract Constant convert(Constant c);
 
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
-        if (from == to) {
-            return value;
-        } else if (value.isConstant()) {
-            return ConstantNode.forPrimitive(evalConst(value.asConstant()), graph());
-        } else if (value instanceof ConvertNode) {
-            ConvertNode other = (ConvertNode) value;
-            if (other.isLossless() && other.to != Kind.Char) {
-                if (other.from == this.to) {
-                    return other.value();
-                } else {
-                    return graph().unique(new ConvertNode(other.from, this.to, other.value()));
-                }
-            }
-        }
-        return this;
-    }
+    public abstract Constant reverse(Constant c);
+
+    public abstract boolean isLossless();
 
     @Override
-    public boolean inferStamp() {
-        Stamp stamp = value.stamp();
-        if (!(stamp instanceof IntegerStamp)) {
-            if (stamp instanceof FloatStamp) {
-                return false;
-            }
-            assert stamp instanceof IllegalStamp;
-            return updateStamp(stamp);
-        }
-        Stamp newStamp;
-        IntegerStamp integerStamp = (IntegerStamp) stamp;
-        switch (to) {
-            case Byte:
-            case Short:
-            case Char:
-            case Int:
-                newStamp = StampTool.narrowingKindConversion(integerStamp, to);
-                break;
-            case Long:
-                newStamp = StampTool.intToLong(integerStamp);
-                break;
-            default:
-                return false;
-        }
-        return updateStamp(newStamp);
-    }
-
-    @Override
-    public void lower(LoweringTool tool) {
-        tool.getLowerer().lower(this, tool);
-    }
-
-    @Override
-    public void generate(ArithmeticLIRGenerator gen) {
-        gen.setResult(this, gen.emitConvert(from, to, gen.operand(value())));
-    }
-
-    public static ValueNode convert(StructuredGraph graph, Kind toKind, ValueNode value) {
-        Kind fromKind = value.kind();
-        if (fromKind == toKind) {
-            return value;
-        }
-        return graph.unique(new ConvertNode(fromKind, toKind, value));
+    public Constant evalConst(Constant... inputs) {
+        assert inputs.length == 1;
+        return convert(inputs[0]);
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.calc;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * A {@code FloatConvert} converts between integers and floating point numbers according to Java
+ * semantics.
+ */
+public class FloatConvertNode extends ConvertNode implements Lowerable, ArithmeticLIRLowerable {
+
+    public enum FloatConvert {
+        F2I, D2I, F2L, D2L, I2F, L2F, D2F, I2D, L2D, F2D;
+
+        public FloatConvert reverse() {
+            switch (this) {
+                case D2F:
+                    return F2D;
+                case D2I:
+                    return I2D;
+                case D2L:
+                    return L2D;
+                case F2D:
+                    return D2F;
+                case F2I:
+                    return I2F;
+                case F2L:
+                    return L2F;
+                case I2D:
+                    return D2I;
+                case I2F:
+                    return F2I;
+                case L2D:
+                    return D2L;
+                case L2F:
+                    return F2L;
+                default:
+                    throw GraalInternalError.shouldNotReachHere();
+            }
+        }
+    }
+
+    private final FloatConvert op;
+
+    public FloatConvertNode(FloatConvert op, ValueNode input) {
+        super(createStamp(op, input), input);
+        this.op = op;
+    }
+
+    private static Stamp createStamp(FloatConvert op, ValueNode input) {
+        switch (op) {
+            case I2F:
+            case I2D:
+                assert input.stamp() instanceof IntegerStamp && ((IntegerStamp) input.stamp()).getBits() == 32;
+                break;
+            case L2F:
+            case L2D:
+                assert input.stamp() instanceof IntegerStamp && ((IntegerStamp) input.stamp()).getBits() == 64;
+                break;
+            case F2I:
+            case F2L:
+            case F2D:
+                assert input.stamp() instanceof FloatStamp && ((FloatStamp) input.stamp()).getBits() == 32;
+                break;
+            case D2I:
+            case D2L:
+            case D2F:
+                assert input.stamp() instanceof FloatStamp && ((FloatStamp) input.stamp()).getBits() == 64;
+                break;
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+
+        switch (op) {
+            case F2I:
+            case D2I:
+                return StampFactory.forKind(Kind.Int);
+            case F2L:
+            case D2L:
+                return StampFactory.forKind(Kind.Long);
+            case I2F:
+            case L2F:
+            case D2F:
+                return StampFactory.forKind(Kind.Float);
+            case I2D:
+            case L2D:
+            case F2D:
+                return StampFactory.forKind(Kind.Double);
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
+    public FloatConvert getOp() {
+        return op;
+    }
+
+    @Override
+    public boolean inferStamp() {
+        return updateStamp(createStamp(op, getInput()));
+    }
+
+    private static Constant convert(FloatConvert op, Constant value) {
+        switch (op) {
+            case F2I:
+                return Constant.forInt((int) value.asFloat());
+            case D2I:
+                return Constant.forInt((int) value.asDouble());
+            case F2L:
+                return Constant.forLong((long) value.asFloat());
+            case D2L:
+                return Constant.forLong((long) value.asDouble());
+            case I2F:
+                return Constant.forFloat(value.asInt());
+            case L2F:
+                return Constant.forFloat(value.asLong());
+            case D2F:
+                return Constant.forFloat((float) value.asDouble());
+            case I2D:
+                return Constant.forDouble(value.asInt());
+            case L2D:
+                return Constant.forDouble(value.asLong());
+            case F2D:
+                return Constant.forDouble(value.asFloat());
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
+    @Override
+    public Constant convert(Constant c) {
+        return convert(op, c);
+    }
+
+    @Override
+    public Constant reverse(Constant c) {
+        return convert(op.reverse(), c);
+    }
+
+    @Override
+    public boolean isLossless() {
+        switch (op) {
+            case F2D:
+            case I2D:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    public void lower(LoweringTool tool) {
+        tool.getLowerer().lower(this, tool);
+    }
+
+    public void generate(ArithmeticLIRGenerator gen) {
+        gen.setResult(this, gen.emitFloatConvert(op, gen.operand(getInput())));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.calc;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * An {@code IntegerConvert} converts an integer to an integer of different width.
+ */
+public abstract class IntegerConvertNode extends ConvertNode implements Canonicalizable, ArithmeticLIRLowerable {
+
+    private final int resultBits;
+
+    protected IntegerConvertNode(Stamp stamp, ValueNode input, int resultBits) {
+        super(stamp, input);
+        this.resultBits = resultBits;
+    }
+
+    public int getResultBits() {
+        return resultBits;
+    }
+
+    public int getInputBits() {
+        if (getInput().stamp() instanceof IntegerStamp) {
+            return ((IntegerStamp) getInput().stamp()).getBits();
+        } else {
+            return 0;
+        }
+    }
+
+    public static long convert(long value, int bits, boolean unsigned) {
+        if (unsigned) {
+            return ZeroExtendNode.zeroExtend(value, bits);
+        } else {
+            return SignExtendNode.signExtend(value, bits);
+        }
+    }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (getInput().stamp() instanceof IntegerStamp) {
+            int inputBits = ((IntegerStamp) getInput().stamp()).getBits();
+            if (inputBits == resultBits) {
+                return getInput();
+            } else if (getInput().isConstant()) {
+                Constant ret = evalConst(getInput().asConstant());
+                return ConstantNode.forIntegerBits(resultBits, false, ret.asLong(), graph());
+            }
+        }
+
+        return this;
+    }
+
+    public static ValueNode convert(ValueNode input, Stamp stamp) {
+        StructuredGraph graph = input.graph();
+        IntegerStamp fromStamp = (IntegerStamp) input.stamp();
+        IntegerStamp toStamp = (IntegerStamp) stamp;
+
+        ValueNode result;
+        if (toStamp.getBits() == fromStamp.getBits()) {
+            result = input;
+        } else if (toStamp.getBits() < fromStamp.getBits()) {
+            result = graph.unique(new NarrowNode(input, toStamp.getBits()));
+        } else {
+            // toStamp.getBits() > fromStamp.getBits()
+            if (fromStamp.isUnsigned()) {
+                result = graph.unique(new ZeroExtendNode(input, toStamp.getBits()));
+            } else {
+                result = graph.unique(new SignExtendNode(input, toStamp.getBits()));
+            }
+        }
+
+        IntegerStamp resultStamp = (IntegerStamp) result.stamp();
+        assert toStamp.getBits() == resultStamp.getBits();
+        if (toStamp.isUnsigned() == resultStamp.isUnsigned()) {
+            return result;
+        } else {
+            return graph.unique(new ReinterpretNode(toStamp, result));
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/NarrowNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.calc;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * The {@code NarrowNode} converts an integer to a narrower integer.
+ */
+public class NarrowNode extends IntegerConvertNode {
+
+    public NarrowNode(ValueNode input, int resultBits) {
+        super(StampTool.narrowingConversion(input.stamp(), resultBits), input, resultBits);
+    }
+
+    public static long narrow(long value, int resultBits) {
+        return value & IntegerStamp.defaultMask(resultBits);
+    }
+
+    @Override
+    public Constant convert(Constant c) {
+        return Constant.forPrimitiveInt(getResultBits(), narrow(c.asLong(), getResultBits()));
+    }
+
+    @Override
+    public Constant reverse(Constant input) {
+        IntegerStamp stamp = (IntegerStamp) stamp();
+        long result;
+        if (stamp.isUnsigned()) {
+            result = ZeroExtendNode.zeroExtend(input.asLong(), getResultBits());
+        } else {
+            result = SignExtendNode.signExtend(input.asLong(), getResultBits());
+        }
+        return Constant.forPrimitiveInt(getInputBits(), result);
+    }
+
+    @Override
+    public boolean isLossless() {
+        return false;
+    }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (getInput() instanceof NarrowNode) {
+            NarrowNode other = (NarrowNode) getInput();
+            return graph().unique(new NarrowNode(other.getInput(), getResultBits()));
+        } else if (getInput() instanceof IntegerConvertNode) {
+            // SignExtendNode or ZeroExtendNode
+            IntegerConvertNode other = (IntegerConvertNode) getInput();
+            if (getResultBits() == other.getInputBits()) {
+                return other.getInput();
+            } else if (getResultBits() < other.getInputBits()) {
+                return graph().unique(new NarrowNode(other.getInput(), getResultBits()));
+            } else {
+                if (other instanceof SignExtendNode) {
+                    return graph().unique(new SignExtendNode(other.getInput(), getResultBits()));
+                } else if (other instanceof ZeroExtendNode) {
+                    return graph().unique(new ZeroExtendNode(other.getInput(), getResultBits()));
+                }
+            }
+        }
+
+        return super.canonical(tool);
+    }
+
+    @Override
+    public boolean inferStamp() {
+        return updateStamp(StampTool.narrowingConversion(getInput().stamp(), getResultBits()));
+    }
+
+    @Override
+    public void generate(ArithmeticLIRGenerator gen) {
+        gen.setResult(this, gen.emitNarrow(gen.operand(getInput()), getResultBits()));
+    }
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ReinterpretNode.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ReinterpretNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -30,7 +30,9 @@
 import com.oracle.graal.nodes.type.*;
 
 /**
- * The {@code ReinterpretNode} class represents a reinterpreting conversion between primitive types.
+ * The {@code ReinterpretNode} class represents a reinterpreting conversion that changes the stamp
+ * of a primitive value to some other incompatible stamp. The new stamp must have the same width as
+ * the old stamp.
  */
 public class ReinterpretNode extends FloatingNode implements Canonicalizable, ArithmeticLIRLowerable {
 
@@ -40,64 +42,45 @@
         return value;
     }
 
-    public ReinterpretNode(Kind to, ValueNode value) {
-        super(StampFactory.forKind(to.getStackKind()));
+    private ReinterpretNode(Kind to, ValueNode value) {
+        this(StampFactory.forKind(to), value);
+    }
+
+    public ReinterpretNode(Stamp to, ValueNode value) {
+        super(to);
+        assert to instanceof PrimitiveStamp;
         this.value = value;
     }
 
     public Constant evalConst(Constant... inputs) {
         assert inputs.length == 1;
         Constant c = inputs[0];
-        assert c.getKind() == value.kind();
+        assert c.getKind().getBitCount() == ((PrimitiveStamp) stamp()).getBits();
         switch (c.getKind()) {
             case Int:
-                switch (kind()) {
-                    case Int:
-                        return c;
-                    case Long:
-                        return Constant.forLong(c.asInt() & 0xFFFFFFFFL);
-                    case Float:
-                        return Constant.forFloat(Float.intBitsToFloat(c.asInt()));
-                    case Double:
-                        return Constant.forDouble(Double.longBitsToDouble(c.asInt() & 0xFFFFFFFFL));
+                if (stamp() instanceof FloatStamp) {
+                    return Constant.forFloat(Float.intBitsToFloat(c.asInt()));
+                } else {
+                    return c;
                 }
-                break;
             case Long:
-                switch (kind()) {
-                    case Int:
-                        return Constant.forInt((int) c.asLong());
-                    case Long:
-                        return c;
-                    case Float:
-                        return Constant.forFloat(Float.intBitsToFloat((int) c.asLong()));
-                    case Double:
-                        return Constant.forDouble(Double.longBitsToDouble(c.asLong()));
+                if (stamp() instanceof FloatStamp) {
+                    return Constant.forDouble(Double.longBitsToDouble(c.asLong()));
+                } else {
+                    return c;
                 }
-                break;
             case Float:
-                switch (kind()) {
-                    case Int:
-                        return Constant.forInt(Float.floatToRawIntBits(c.asFloat()));
-                    case Long:
-                        return Constant.forLong(Float.floatToRawIntBits(c.asFloat()) & 0xFFFFFFFFL);
-                    case Float:
-                        return c;
-                    case Double:
-                        return Constant.forDouble(Double.longBitsToDouble(Float.floatToRawIntBits(c.asFloat()) & 0xFFFFFFFFL));
+                if (stamp() instanceof IntegerStamp) {
+                    return Constant.forInt(Float.floatToRawIntBits(c.asFloat()));
+                } else {
+                    return c;
                 }
-                break;
             case Double:
-                switch (kind()) {
-                    case Int:
-                        return Constant.forInt((int) Double.doubleToRawLongBits(c.asDouble()));
-                    case Long:
-                        return Constant.forLong(Double.doubleToRawLongBits(c.asDouble()));
-                    case Float:
-                        return Constant.forFloat(Float.intBitsToFloat((int) Double.doubleToRawLongBits(c.asDouble())));
-                    case Double:
-                        return c;
+                if (stamp() instanceof IntegerStamp) {
+                    return Constant.forLong(Double.doubleToRawLongBits(c.asDouble()));
+                } else {
+                    return c;
                 }
-                break;
         }
         throw GraalInternalError.shouldNotReachHere();
     }
@@ -107,6 +90,9 @@
         if (value.isConstant()) {
             return ConstantNode.forPrimitive(evalConst(value.asConstant()), graph());
         }
+        if (stamp().isCompatible(value.stamp())) {
+            return value;
+        }
         return this;
     }
 
@@ -116,10 +102,6 @@
     }
 
     public static ValueNode reinterpret(Kind toKind, ValueNode value) {
-        Kind fromKind = value.kind();
-        if (fromKind == toKind) {
-            return value;
-        }
         return value.graph().unique(new ReinterpretNode(toKind, value));
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SignExtendNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.calc;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * The {@code SignExtendNode} converts an integer to a wider integer using sign extension.
+ */
+public class SignExtendNode extends IntegerConvertNode {
+
+    public SignExtendNode(ValueNode input, int resultBits) {
+        super(StampTool.signExtend(input.stamp(), resultBits), input, resultBits);
+    }
+
+    public static long signExtend(long value, int inputBits) {
+        if (inputBits < 64) {
+            if ((value >>> (inputBits - 1) & 1) == 1) {
+                return value | (-1L << inputBits);
+            } else {
+                return value & ~(-1L << inputBits);
+            }
+        } else {
+            return value;
+        }
+    }
+
+    @Override
+    public Constant convert(Constant c) {
+        return Constant.forPrimitiveInt(getResultBits(), signExtend(c.asLong(), getInputBits()));
+    }
+
+    @Override
+    public Constant reverse(Constant c) {
+        return Constant.forPrimitiveInt(getInputBits(), NarrowNode.narrow(c.asLong(), getInputBits()));
+    }
+
+    @Override
+    public boolean isLossless() {
+        return true;
+    }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (getInput() instanceof SignExtendNode) {
+            SignExtendNode other = (SignExtendNode) getInput();
+            return graph().unique(new SignExtendNode(other.getInput(), getResultBits()));
+        } else if (getInput() instanceof ZeroExtendNode) {
+            ZeroExtendNode other = (ZeroExtendNode) getInput();
+            if (other.getResultBits() > other.getInputBits()) {
+                return graph().unique(new ZeroExtendNode(other.getInput(), getResultBits()));
+            }
+        }
+
+        if (getInput().stamp() instanceof IntegerStamp) {
+            IntegerStamp inputStamp = (IntegerStamp) getInput().stamp();
+            if ((inputStamp.upMask() & (1L << (getInputBits() - 1))) == 0L) {
+                return graph().unique(new ZeroExtendNode(getInput(), getResultBits()));
+            }
+        }
+
+        return super.canonical(tool);
+    }
+
+    @Override
+    public boolean inferStamp() {
+        return updateStamp(StampTool.signExtend(getInput().stamp(), getResultBits()));
+    }
+
+    @Override
+    public void generate(ArithmeticLIRGenerator gen) {
+        gen.setResult(this, gen.emitSignExtend(gen.operand(getInput()), getInputBits(), getResultBits()));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ZeroExtendNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.calc;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * The {@code ZeroExtendNode} converts an integer to a wider integer using zero extension.
+ */
+public class ZeroExtendNode extends IntegerConvertNode {
+
+    public ZeroExtendNode(ValueNode input, int resultBits) {
+        super(StampTool.zeroExtend(input.stamp(), resultBits), input, resultBits);
+    }
+
+    public static long zeroExtend(long value, int inputBits) {
+        if (inputBits < 64) {
+            return value & ~(-1L << inputBits);
+        } else {
+            return value;
+        }
+    }
+
+    @Override
+    public Constant convert(Constant c) {
+        return Constant.forPrimitiveInt(getResultBits(), zeroExtend(c.asLong(), getInputBits()));
+    }
+
+    @Override
+    public Constant reverse(Constant c) {
+        return Constant.forPrimitiveInt(getInputBits(), NarrowNode.narrow(c.asLong(), getInputBits()));
+    }
+
+    @Override
+    public boolean isLossless() {
+        return true;
+    }
+
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (getInput() instanceof ZeroExtendNode) {
+            ZeroExtendNode other = (ZeroExtendNode) getInput();
+            return graph().unique(new ZeroExtendNode(other.getInput(), getResultBits()));
+        }
+
+        return super.canonical(tool);
+    }
+
+    @Override
+    public boolean inferStamp() {
+        return updateStamp(StampTool.zeroExtend(getInput().stamp(), getResultBits()));
+    }
+
+    @Override
+    public void generate(ArithmeticLIRGenerator gen) {
+        gen.setResult(this, gen.emitZeroExtend(gen.operand(getInput()), getInputBits(), getResultBits()));
+    }
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/ArithmeticLIRGenerator.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/ArithmeticLIRGenerator.java	Fri Feb 21 11:53:48 2014 +0100
@@ -24,6 +24,7 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.FloatConvertNode.FloatConvert;
 
 /**
  * This interface can be used to generate LIR for arithmetic operations (@see
@@ -65,10 +66,16 @@
 
     Value emitUShr(Value a, Value b);
 
-    Value emitConvert(Kind from, Kind to, Value inputVal);
+    Value emitFloatConvert(FloatConvert op, Value inputVal);
 
     Value emitReinterpret(Kind to, Value inputVal);
 
+    Value emitNarrow(Value inputVal, int bits);
+
+    Value emitSignExtend(Value inputVal, int fromBits, int toBits);
+
+    Value emitZeroExtend(Value inputVal, int fromBits, int toBits);
+
     Value emitMathAbs(Value input);
 
     Value emitMathSqrt(Value input);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/PrimitiveStamp.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/PrimitiveStamp.java	Fri Feb 21 11:53:48 2014 +0100
@@ -42,6 +42,14 @@
         return bits;
     }
 
+    public static int getBits(Stamp stamp) {
+        if (stamp instanceof PrimitiveStamp) {
+            return ((PrimitiveStamp) stamp).getBits();
+        } else {
+            return 0;
+        }
+    }
+
     @Override
     public int hashCode() {
         final int prime = 31;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampTool.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampTool.java	Fri Feb 21 11:53:48 2014 +0100
@@ -25,6 +25,7 @@
 import java.util.*;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.nodes.calc.*;
 
 /**
  * Helper class that is used to keep all stamp-related operations in one place.
@@ -178,15 +179,15 @@
             lowerBound = (stamp1.lowerBound() + stamp2.lowerBound()) & defaultMask;
             upperBound = (stamp1.upperBound() + stamp2.upperBound()) & defaultMask;
             if (!unsigned) {
-                lowerBound = signExtend(lowerBound, bits);
-                upperBound = signExtend(upperBound, bits);
+                lowerBound = SignExtendNode.signExtend(lowerBound, bits);
+                upperBound = SignExtendNode.signExtend(upperBound, bits);
             }
         }
         IntegerStamp limit = StampFactory.forInteger(bits, unsigned, lowerBound, upperBound);
         newUpMask &= limit.upMask();
         upperBound &= newUpMask;
         if (!unsigned) {
-            upperBound = signExtend(upperBound, bits);
+            upperBound = SignExtendNode.signExtend(upperBound, bits);
         }
         newDownMask |= limit.downMask();
         lowerBound |= newDownMask;
@@ -213,6 +214,8 @@
             lowerBound = downMask | (-1L << (bits - 1));
             upperBound = IntegerStamp.defaultMaxValue(bits, false) & upMask;
         }
+        lowerBound = IntegerConvertNode.convert(lowerBound, bits, false);
+        upperBound = IntegerConvertNode.convert(upperBound, bits, false);
         return new IntegerStamp(bits, false, lowerBound, upperBound, downMask, upMask);
     }
 
@@ -316,16 +319,85 @@
         return value.unrestricted();
     }
 
+    public static Stamp signExtend(Stamp input, int resultBits) {
+        if (input instanceof IntegerStamp) {
+            IntegerStamp inputStamp = (IntegerStamp) input;
+            int inputBits = inputStamp.getBits();
+            assert inputBits <= resultBits;
+
+            long defaultMask = IntegerStamp.defaultMask(resultBits);
+            long downMask = SignExtendNode.signExtend(inputStamp.downMask(), inputBits) & defaultMask;
+            long upMask = SignExtendNode.signExtend(inputStamp.upMask(), inputBits) & defaultMask;
+
+            return new IntegerStamp(resultBits, inputStamp.isUnsigned(), inputStamp.lowerBound(), inputStamp.upperBound(), downMask, upMask);
+        } else {
+            return input.illegal();
+        }
+    }
+
+    public static Stamp zeroExtend(Stamp input, int resultBits) {
+        if (input instanceof IntegerStamp) {
+            IntegerStamp inputStamp = (IntegerStamp) input;
+            int inputBits = inputStamp.getBits();
+            assert inputBits <= resultBits;
+
+            long downMask = ZeroExtendNode.zeroExtend(inputStamp.downMask(), inputBits);
+            long upMask = ZeroExtendNode.zeroExtend(inputStamp.upMask(), inputBits);
+
+            return new IntegerStamp(resultBits, inputStamp.isUnsigned(), inputStamp.lowerBound(), inputStamp.upperBound(), downMask, upMask);
+        } else {
+            return input.illegal();
+        }
+    }
+
     public static Stamp intToLong(IntegerStamp intStamp) {
         long downMask = intStamp.downMask();
         long upMask = intStamp.upMask();
         if (!intStamp.isUnsigned()) {
-            downMask = signExtend(downMask, intStamp.getBits());
-            upMask = signExtend(upMask, intStamp.getBits());
+            downMask = SignExtendNode.signExtend(downMask, intStamp.getBits());
+            upMask = SignExtendNode.signExtend(upMask, intStamp.getBits());
         }
         return new IntegerStamp(64, intStamp.isUnsigned(), intStamp.lowerBound(), intStamp.upperBound(), downMask, upMask);
     }
 
+    public static Stamp narrowingConversion(Stamp input, int resultBits) {
+        if (input instanceof IntegerStamp) {
+            IntegerStamp inputStamp = (IntegerStamp) input;
+            boolean unsigned = inputStamp.isUnsigned();
+            int inputBits = inputStamp.getBits();
+            assert resultBits <= inputBits;
+            if (resultBits == inputBits) {
+                return inputStamp;
+            }
+
+            final long upperBound;
+            if (inputStamp.lowerBound() < IntegerStamp.defaultMinValue(resultBits, unsigned)) {
+                upperBound = IntegerStamp.defaultMaxValue(resultBits, unsigned);
+            } else {
+                upperBound = saturate(inputStamp.upperBound(), resultBits, unsigned);
+            }
+            final long lowerBound;
+            if (inputStamp.upperBound() > IntegerStamp.defaultMaxValue(resultBits, unsigned)) {
+                lowerBound = IntegerStamp.defaultMinValue(resultBits, unsigned);
+            } else {
+                lowerBound = saturate(inputStamp.lowerBound(), resultBits, unsigned);
+            }
+
+            long defaultMask = IntegerStamp.defaultMask(resultBits);
+            long newDownMask = inputStamp.downMask() & defaultMask;
+            long newUpMask = inputStamp.upMask() & defaultMask;
+            long newLowerBound = (lowerBound | newDownMask) & newUpMask;
+            long newUpperBound = (upperBound | newDownMask) & newUpMask;
+            if (!unsigned) {
+                newLowerBound = SignExtendNode.signExtend(newLowerBound, resultBits);
+                newUpperBound = SignExtendNode.signExtend(newUpperBound, resultBits);
+            }
+            return new IntegerStamp(resultBits, unsigned, newLowerBound, newUpperBound, newDownMask, newUpMask);
+        } else {
+            return input.illegal();
+        }
+    }
+
     public static IntegerStamp narrowingKindConversion(IntegerStamp fromStamp, Kind toKind) {
         assert toKind == Kind.Byte || toKind == Kind.Char || toKind == Kind.Short || toKind == Kind.Int;
         final long upperBound;
@@ -348,14 +420,6 @@
         return new IntegerStamp(toKind.getStackKind().getBitCount(), false, (int) ((lowerBound | newDownMask) & newUpMask), (int) ((upperBound | newDownMask) & newUpMask), newDownMask, newUpMask);
     }
 
-    private static long signExtend(long value, int bits) {
-        if (bits < 64 && (value >>> (bits - 1) & 1) == 1) {
-            return value | (-1L << bits);
-        } else {
-            return value;
-        }
-    }
-
     private static long signExtend(long value, Kind valueKind) {
         if (valueKind != Kind.Char && valueKind != Kind.Long && (value >>> (valueKind.getBitCount() - 1) & 1) == 1) {
             return value | (-1L << valueKind.getBitCount());
@@ -364,7 +428,21 @@
         }
     }
 
-    public static long saturate(long v, Kind kind) {
+    private static long saturate(long v, int bits, boolean unsigned) {
+        if (bits < 64) {
+            long max = IntegerStamp.defaultMaxValue(bits, unsigned);
+            if (v > max) {
+                return max;
+            }
+            long min = IntegerStamp.defaultMinValue(bits, unsigned);
+            if (v < min) {
+                return min;
+            }
+        }
+        return v;
+    }
+
+    private static long saturate(long v, Kind kind) {
         long max = kind.getMaxValue();
         if (v > max) {
             return max;
--- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64ConvertNode.java	Thu Feb 20 14:42:01 2014 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,58 +0,0 @@
-/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.replacements.amd64;
-
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.calc.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.type.*;
-
-/**
- * This node has the semantics of the AMD64 conversions. It is used in the lowering of the
- * {@link ConvertNode} which, on AMD64 needs a {@link AMD64ConvertNode} plus some fixup code that
- * handles the corner cases that differ between AMD64 and Java.
- */
-public class AMD64ConvertNode extends FloatingNode implements ArithmeticLIRLowerable {
-
-    @Input private ValueNode value;
-    private final Kind from;
-    private final Kind to;
-
-    public AMD64ConvertNode(Kind from, Kind to, ValueNode value) {
-        super(StampFactory.forKind(to.getStackKind()));
-        this.value = value;
-        this.from = from;
-        this.to = to;
-    }
-
-    public Constant evalConst(Constant... inputs) {
-        // this node should never have been created if its input is constant
-        assert false;
-        return null;
-    }
-
-    public void generate(ArithmeticLIRGenerator gen) {
-        gen.setResult(this, gen.emitConvert(from, to, gen.operand(value)));
-    }
-}
--- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64ConvertSnippets.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64ConvertSnippets.java	Fri Feb 21 11:53:48 2014 +0100
@@ -158,42 +158,33 @@
             d2l = snippet(AMD64ConvertSnippets.class, "d2l");
         }
 
-        public void lower(ConvertNode convert, LoweringTool tool) {
-            SnippetInfo key = null;
-            switch (convert.getFromKind()) {
-                case Float:
-                    switch (convert.getToKind()) {
-                        case Int:
-                            key = f2i;
-                            break;
-                        case Long:
-                            key = f2l;
-                            break;
-                    }
+        public void lower(FloatConvertNode convert, LoweringTool tool) {
+            SnippetInfo key;
+            switch (convert.getOp()) {
+                case F2I:
+                    key = f2i;
+                    break;
+                case F2L:
+                    key = f2l;
                     break;
-                case Double:
-                    switch (convert.getToKind()) {
-                        case Int:
-                            key = d2i;
-                            break;
-                        case Long:
-                            key = d2l;
-                            break;
-                    }
+                case D2I:
+                    key = d2i;
                     break;
-            }
-            if (key == null) {
-                return;
+                case D2L:
+                    key = d2l;
+                    break;
+                default:
+                    return;
             }
 
             StructuredGraph graph = convert.graph();
 
             Arguments args = new Arguments(key, graph.getGuardsStage(), tool.getLoweringStage());
-            args.add("input", convert.value());
-            args.add("result", graph.unique(new AMD64ConvertNode(convert.getFromKind(), convert.getToKind(), convert.value())));
+            args.add("input", convert.getInput());
+            args.add("result", graph.unique(new AMD64FloatConvertNode(convert.stamp(), convert.getOp(), convert.getInput())));
 
             SnippetTemplate template = template(args);
-            Debug.log("Lowering %c2%c in %s: node=%s, template=%s, arguments=%s", convert.getFromKind().getTypeChar(), convert.getToKind().getTypeChar(), graph, convert, template, args);
+            Debug.log("Lowering %s in %s: node=%s, template=%s, arguments=%s", convert.getOp(), graph, convert, template, args);
             template.instantiate(providers.getMetaAccess(), convert, DEFAULT_REPLACER, tool, args);
             graph.removeFloating(convert);
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64FloatConvertNode.java	Fri Feb 21 11:53:48 2014 +0100
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2012, 2014, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.replacements.amd64;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.calc.FloatConvertNode.FloatConvert;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * This node has the semantics of the AMD64 floating point conversions. It is used in the lowering
+ * of the {@link FloatConvertNode} which, on AMD64 needs a {@link AMD64FloatConvertNode} plus some
+ * fixup code that handles the corner cases that differ between AMD64 and Java.
+ */
+public class AMD64FloatConvertNode extends FloatingNode implements ArithmeticLIRLowerable {
+
+    private final FloatConvert op;
+    @Input private ValueNode value;
+
+    public AMD64FloatConvertNode(Stamp stamp, FloatConvert op, ValueNode value) {
+        super(stamp);
+        this.op = op;
+        this.value = value;
+    }
+
+    public Constant evalConst(Constant... inputs) {
+        // this node should never have been created if its input is constant
+        throw GraalInternalError.shouldNotReachHere();
+    }
+
+    public void generate(ArithmeticLIRGenerator gen) {
+        gen.setResult(this, gen.emitFloatConvert(op, gen.operand(value)));
+    }
+}
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ObjectAccessTest.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ObjectAccessTest.java	Fri Feb 21 11:53:48 2014 +0100
@@ -102,7 +102,7 @@
 
     private static void assertRead(StructuredGraph graph, Kind kind, boolean indexConvert, LocationIdentity locationIdentity) {
         ReadNode read = (ReadNode) graph.start().next();
-        Assert.assertEquals(kind.getStackKind(), read.kind());
+        Assert.assertEquals(kind.getStackKind(), read.stamp().getStackKind());
         Assert.assertEquals(graph.getParameter(0), read.object());
 
         IndexedLocationNode location = (IndexedLocationNode) read.location();
@@ -111,10 +111,10 @@
         Assert.assertEquals(1, location.getIndexScaling());
 
         if (indexConvert) {
-            ConvertNode convert = (ConvertNode) location.getIndex();
-            Assert.assertEquals(Kind.Int, convert.getFromKind());
-            Assert.assertEquals(Kind.Long, convert.getToKind());
-            Assert.assertEquals(graph.getParameter(1), convert.value());
+            SignExtendNode convert = (SignExtendNode) location.getIndex();
+            Assert.assertEquals(convert.getInputBits(), 32);
+            Assert.assertEquals(convert.getResultBits(), 64);
+            Assert.assertEquals(graph.getParameter(1), convert.getInput());
         } else {
             Assert.assertEquals(graph.getParameter(1), location.getIndex());
         }
@@ -127,7 +127,6 @@
         WriteNode write = (WriteNode) graph.start().next();
         Assert.assertEquals(graph.getParameter(2), write.value());
         Assert.assertEquals(graph.getParameter(0), write.object());
-        Assert.assertEquals(Kind.Void, write.kind());
         Assert.assertEquals(FrameState.AFTER_BCI, write.stateAfter().bci);
 
         IndexedLocationNode location = (IndexedLocationNode) write.location();
@@ -136,10 +135,10 @@
         Assert.assertEquals(1, location.getIndexScaling());
 
         if (indexConvert) {
-            ConvertNode convert = (ConvertNode) location.getIndex();
-            Assert.assertEquals(Kind.Int, convert.getFromKind());
-            Assert.assertEquals(Kind.Long, convert.getToKind());
-            Assert.assertEquals(graph.getParameter(1), convert.value());
+            SignExtendNode convert = (SignExtendNode) location.getIndex();
+            Assert.assertEquals(convert.getInputBits(), 32);
+            Assert.assertEquals(convert.getResultBits(), 64);
+            Assert.assertEquals(graph.getParameter(1), convert.getInput());
         } else {
             Assert.assertEquals(graph.getParameter(1), location.getIndex());
         }
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTest.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTest.java	Fri Feb 21 11:53:48 2014 +0100
@@ -110,11 +110,11 @@
         WordCastNode cast = (WordCastNode) graph.start().next();
 
         ReadNode read = (ReadNode) cast.next();
-        Assert.assertEquals(kind.getStackKind(), read.kind());
+        Assert.assertEquals(kind.getStackKind(), read.stamp().getStackKind());
 
         Assert.assertEquals(cast, read.object());
         Assert.assertEquals(graph.getParameter(0), cast.getInput());
-        Assert.assertEquals(target.wordKind, cast.kind());
+        Assert.assertEquals(target.wordKind, cast.stamp().getStackKind());
 
         IndexedLocationNode location = (IndexedLocationNode) read.location();
         Assert.assertEquals(kind, location.getValueKind());
@@ -122,10 +122,10 @@
         Assert.assertEquals(1, location.getIndexScaling());
 
         if (indexConvert) {
-            ConvertNode convert = (ConvertNode) location.getIndex();
-            Assert.assertEquals(Kind.Int, convert.getFromKind());
-            Assert.assertEquals(Kind.Long, convert.getToKind());
-            Assert.assertEquals(graph.getParameter(1), convert.value());
+            SignExtendNode convert = (SignExtendNode) location.getIndex();
+            Assert.assertEquals(convert.getInputBits(), 32);
+            Assert.assertEquals(convert.getResultBits(), 64);
+            Assert.assertEquals(graph.getParameter(1), convert.getInput());
         } else {
             Assert.assertEquals(graph.getParameter(1), location.getIndex());
         }
@@ -139,12 +139,11 @@
 
         WriteNode write = (WriteNode) cast.next();
         Assert.assertEquals(graph.getParameter(2), write.value());
-        Assert.assertEquals(Kind.Void, write.kind());
         Assert.assertEquals(FrameState.AFTER_BCI, write.stateAfter().bci);
 
         Assert.assertEquals(cast, write.object());
         Assert.assertEquals(graph.getParameter(0), cast.getInput());
-        Assert.assertEquals(target.wordKind, cast.kind());
+        Assert.assertEquals(target.wordKind, cast.stamp().getStackKind());
 
         IndexedLocationNode location = (IndexedLocationNode) write.location();
         Assert.assertEquals(kind, location.getValueKind());
@@ -152,10 +151,10 @@
         Assert.assertEquals(1, location.getIndexScaling());
 
         if (indexConvert) {
-            ConvertNode convert = (ConvertNode) location.getIndex();
-            Assert.assertEquals(Kind.Int, convert.getFromKind());
-            Assert.assertEquals(Kind.Long, convert.getToKind());
-            Assert.assertEquals(graph.getParameter(1), convert.value());
+            SignExtendNode convert = (SignExtendNode) location.getIndex();
+            Assert.assertEquals(convert.getInputBits(), 32);
+            Assert.assertEquals(convert.getResultBits(), 64);
+            Assert.assertEquals(graph.getParameter(1), convert.getInput());
         } else {
             Assert.assertEquals(graph.getParameter(1), location.getIndex());
         }
--- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java	Thu Feb 20 14:42:01 2014 +0100
+++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java	Fri Feb 21 11:53:48 2014 +0100
@@ -301,14 +301,14 @@
 
         if (toKind == Kind.Int) {
             assert value.kind() == Kind.Long;
-            return graph.unique(new ConvertNode(Kind.Long, Kind.Int, value));
+            return graph.unique(new NarrowNode(value, 32));
         } else {
             assert toKind == Kind.Long;
             assert value.kind().getStackKind() == Kind.Int;
             if (unsigned) {
-                return graph.unique(new ReinterpretNode(Kind.Long, value));
+                return graph.unique(new ZeroExtendNode(value, 64));
             } else {
-                return graph.unique(new ConvertNode(Kind.Int, Kind.Long, value));
+                return graph.unique(new SignExtendNode(value, 64));
             }
         }
     }