changeset 17344:98e60bdf6d05

Use ArithmeticOpTable for primitive convert operations.
author Roland Schatz <roland.schatz@oracle.com>
date Mon, 06 Oct 2014 11:51:32 +0200
parents da1f5b9ccac6
children 3152f72f5cda
files graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/ArithmeticOpTable.java graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/FloatStamp.java graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IntegerStamp.java graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotLoweringProvider.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CompressionNode.java graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/IntegerStampTest.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/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/extended/IndexedLocationNode.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/AMD64ConvertSnippets.java graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64FloatConvertNode.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultJavaLoweringProvider.java
diffstat 18 files changed, 508 insertions(+), 364 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java	Mon Oct 06 11:51:32 2014 +0200
@@ -191,7 +191,7 @@
     }
 
     private Value emitFloatConvertMemory(FloatConvertNode op, Access access) {
-        switch (op.getOp()) {
+        switch (op.getFloatConvert()) {
             case D2F:
                 return emitConvert2MemoryOp(Kind.Float, D2F, access);
             case D2I:
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/ArithmeticOpTable.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/ArithmeticOpTable.java	Mon Oct 06 11:51:32 2014 +0200
@@ -24,6 +24,7 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.common.calc.*;
 
 /**
  * Information about arithmetic operations.
@@ -43,6 +44,12 @@
     private final BinaryOp or;
     private final BinaryOp xor;
 
+    private final IntegerConvertOp zeroExtend;
+    private final IntegerConvertOp signExtend;
+    private final IntegerConvertOp narrow;
+
+    private final FloatConvertOp[] floatConvert;
+
     public static ArithmeticOpTable forStamp(Stamp s) {
         if (s instanceof ArithmeticStamp) {
             return ((ArithmeticStamp) s).getOps();
@@ -67,26 +74,18 @@
         BinaryOp or = null;
         BinaryOp xor = null;
 
+        IntegerConvertOp zeroExtend = null;
+        IntegerConvertOp signExtend = null;
+        IntegerConvertOp narrow = null;
+
+        FloatConvertOp[] floatConvert = new FloatConvertOp[FloatConvert.values().length];
+
         for (Op op : ops) {
             if (op == null) {
                 continue;
             }
 
-            if (op instanceof UnaryOp) {
-                UnaryOp unary = (UnaryOp) op;
-                switch (unary.getOperator()) {
-                    case '-':
-                        assert neg == null;
-                        neg = unary;
-                        break;
-                    case '~':
-                        assert not == null;
-                        not = unary;
-                        break;
-                    default:
-                        throw GraalInternalError.shouldNotReachHere("unknown unary operator " + unary.getOperator());
-                }
-            } else {
+            if (op instanceof BinaryOp) {
                 BinaryOp binary = (BinaryOp) op;
                 switch (binary.getOperator()) {
                     case '+':
@@ -124,13 +123,53 @@
                     default:
                         throw GraalInternalError.shouldNotReachHere("unknown binary operator " + binary.getOperator());
                 }
+            } else if (op instanceof IntegerConvertOp) {
+                IntegerConvertOp convert = (IntegerConvertOp) op;
+                switch (convert.getOperator()) {
+                    case IntegerConvertOp.ZERO_EXTEND:
+                        assert zeroExtend == null;
+                        zeroExtend = convert;
+                        break;
+                    case IntegerConvertOp.SIGN_EXTEND:
+                        assert signExtend == null;
+                        signExtend = convert;
+                        break;
+                    case IntegerConvertOp.NARROW:
+                        assert narrow == null;
+                        narrow = convert;
+                        break;
+                    default:
+                        throw GraalInternalError.shouldNotReachHere("unknown integer conversion operator " + convert.getOperator());
+                }
+            } else if (op instanceof FloatConvertOp) {
+                FloatConvertOp convert = (FloatConvertOp) op;
+                int idx = convert.getFloatConvert().ordinal();
+                assert floatConvert[idx] == null;
+                floatConvert[idx] = convert;
+            } else if (op instanceof UnaryOp) {
+                UnaryOp unary = (UnaryOp) op;
+                switch (unary.getOperator()) {
+                    case '-':
+                        assert neg == null;
+                        neg = unary;
+                        break;
+                    case '~':
+                        assert not == null;
+                        not = unary;
+                        break;
+                    default:
+                        throw GraalInternalError.shouldNotReachHere("unknown unary operator " + unary.getOperator());
+                }
+            } else {
+                throw GraalInternalError.shouldNotReachHere("unknown Op subclass " + op);
             }
         }
 
-        return new ArithmeticOpTable(neg, add, sub, mul, div, rem, not, and, or, xor);
+        return new ArithmeticOpTable(neg, add, sub, mul, div, rem, not, and, or, xor, zeroExtend, signExtend, narrow, floatConvert);
     }
 
-    private ArithmeticOpTable(UnaryOp neg, BinaryOp add, BinaryOp sub, BinaryOp mul, BinaryOp div, BinaryOp rem, UnaryOp not, BinaryOp and, BinaryOp or, BinaryOp xor) {
+    private ArithmeticOpTable(UnaryOp neg, BinaryOp add, BinaryOp sub, BinaryOp mul, BinaryOp div, BinaryOp rem, UnaryOp not, BinaryOp and, BinaryOp or, BinaryOp xor, IntegerConvertOp zeroExtend,
+                    IntegerConvertOp signExtend, IntegerConvertOp narrow, FloatConvertOp[] floatConvert) {
         this.neg = neg;
         this.add = add;
         this.sub = sub;
@@ -141,6 +180,10 @@
         this.and = and;
         this.or = or;
         this.xor = xor;
+        this.zeroExtend = zeroExtend;
+        this.signExtend = signExtend;
+        this.narrow = narrow;
+        this.floatConvert = floatConvert;
     }
 
     /**
@@ -213,6 +256,22 @@
         return xor;
     }
 
+    public IntegerConvertOp getZeroExtend() {
+        return zeroExtend;
+    }
+
+    public IntegerConvertOp getSignExtend() {
+        return signExtend;
+    }
+
+    public IntegerConvertOp getNarrow() {
+        return narrow;
+    }
+
+    public FloatConvertOp getFloatConvert(FloatConvert op) {
+        return floatConvert[op.ordinal()];
+    }
+
     public abstract static class Op {
 
         private final char operator;
@@ -318,4 +377,52 @@
             return null;
         }
     }
-}
\ No newline at end of file
+
+    public abstract static class FloatConvertOp extends UnaryOp {
+
+        private final FloatConvert op;
+
+        protected FloatConvertOp(FloatConvert op) {
+            super('\0');
+            this.op = op;
+        }
+
+        public FloatConvert getFloatConvert() {
+            return op;
+        }
+
+        @Override
+        public String toString() {
+            return op.name();
+        }
+    }
+
+    public abstract static class IntegerConvertOp extends Op {
+
+        public static final char ZERO_EXTEND = 'z';
+        public static final char SIGN_EXTEND = 's';
+        public static final char NARROW = 'n';
+
+        protected IntegerConvertOp(char op) {
+            super(op);
+        }
+
+        public abstract Constant foldConstant(int inputBits, int resultBits, Constant value);
+
+        public abstract Stamp foldStamp(int resultBits, Stamp stamp);
+
+        @Override
+        public String toString() {
+            switch (getOperator()) {
+                case ZERO_EXTEND:
+                    return "ZeroExtend";
+                case SIGN_EXTEND:
+                    return "SignExtend";
+                case NARROW:
+                    return "Narrow";
+                default:
+                    throw GraalInternalError.shouldNotReachHere();
+            }
+        }
+    }
+}
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/FloatStamp.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/FloatStamp.java	Mon Oct 06 11:51:32 2014 +0200
@@ -22,12 +22,15 @@
  */
 package com.oracle.graal.compiler.common.type;
 
+import static com.oracle.graal.compiler.common.calc.FloatConvert.*;
+
 import java.util.function.*;
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.spi.*;
 import com.oracle.graal.compiler.common.type.ArithmeticOpTable.BinaryOp;
+import com.oracle.graal.compiler.common.type.ArithmeticOpTable.FloatConvertOp;
 import com.oracle.graal.compiler.common.type.ArithmeticOpTable.UnaryOp;
 
 public class FloatStamp extends PrimitiveStamp {
@@ -431,5 +434,89 @@
             // TODO
             return stamp1.unrestricted();
         }
+    },
+
+    new FloatConvertOp(F2I) {
+
+        @Override
+        public Constant foldConstant(Constant value) {
+            return Constant.forInt((int) value.asFloat());
+        }
+
+        @Override
+        public Stamp foldStamp(Stamp stamp) {
+            assert stamp instanceof FloatStamp && ((FloatStamp) stamp).getBits() == 32;
+            return StampFactory.forKind(Kind.Int);
+        }
+    },
+
+    new FloatConvertOp(F2L) {
+
+        @Override
+        public Constant foldConstant(Constant value) {
+            return Constant.forLong((long) value.asFloat());
+        }
+
+        @Override
+        public Stamp foldStamp(Stamp stamp) {
+            assert stamp instanceof FloatStamp && ((FloatStamp) stamp).getBits() == 32;
+            return StampFactory.forKind(Kind.Long);
+        }
+    },
+
+    new FloatConvertOp(D2I) {
+
+        @Override
+        public Constant foldConstant(Constant value) {
+            return Constant.forInt((int) value.asDouble());
+        }
+
+        @Override
+        public Stamp foldStamp(Stamp stamp) {
+            assert stamp instanceof FloatStamp && ((FloatStamp) stamp).getBits() == 64;
+            return StampFactory.forKind(Kind.Int);
+        }
+    },
+
+    new FloatConvertOp(D2L) {
+
+        @Override
+        public Constant foldConstant(Constant value) {
+            return Constant.forLong((long) value.asDouble());
+        }
+
+        @Override
+        public Stamp foldStamp(Stamp stamp) {
+            assert stamp instanceof FloatStamp && ((FloatStamp) stamp).getBits() == 64;
+            return StampFactory.forKind(Kind.Long);
+        }
+    },
+
+    new FloatConvertOp(F2D) {
+
+        @Override
+        public Constant foldConstant(Constant value) {
+            return Constant.forDouble(value.asFloat());
+        }
+
+        @Override
+        public Stamp foldStamp(Stamp stamp) {
+            assert stamp instanceof FloatStamp && ((FloatStamp) stamp).getBits() == 32;
+            return StampFactory.forKind(Kind.Double);
+        }
+    },
+
+    new FloatConvertOp(D2F) {
+
+        @Override
+        public Constant foldConstant(Constant value) {
+            return Constant.forFloat((float) value.asDouble());
+        }
+
+        @Override
+        public Stamp foldStamp(Stamp stamp) {
+            assert stamp instanceof FloatStamp && ((FloatStamp) stamp).getBits() == 64;
+            return StampFactory.forKind(Kind.Float);
+        }
     });
 }
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IntegerStamp.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/IntegerStamp.java	Mon Oct 06 11:51:32 2014 +0200
@@ -22,6 +22,9 @@
  */
 package com.oracle.graal.compiler.common.type;
 
+import static com.oracle.graal.compiler.common.calc.FloatConvert.*;
+import static com.oracle.graal.compiler.common.type.ArithmeticOpTable.IntegerConvertOp.*;
+
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
@@ -29,6 +32,8 @@
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.spi.*;
 import com.oracle.graal.compiler.common.type.ArithmeticOpTable.BinaryOp;
+import com.oracle.graal.compiler.common.type.ArithmeticOpTable.FloatConvertOp;
+import com.oracle.graal.compiler.common.type.ArithmeticOpTable.IntegerConvertOp;
 import com.oracle.graal.compiler.common.type.ArithmeticOpTable.UnaryOp;
 
 /**
@@ -350,6 +355,20 @@
         return (x + y) ^ x ^ y;
     }
 
+    private static long saturate(long v, int bits) {
+        if (bits < 64) {
+            long max = CodeUtil.maxValue(bits);
+            if (v > max) {
+                return max;
+            }
+            long min = CodeUtil.minValue(bits);
+            if (v < min) {
+                return min;
+            }
+        }
+        return v;
+    }
+
     public static final ArithmeticOpTable OPS = ArithmeticOpTable.create(
 
     new UnaryOp('-') {
@@ -633,5 +652,153 @@
             IntegerStamp stamp = (IntegerStamp) s;
             return Constant.forPrimitiveInt(stamp.getBits(), 0);
         }
+    },
+
+    new IntegerConvertOp(ZERO_EXTEND) {
+
+        @Override
+        public Constant foldConstant(int inputBits, int resultBits, Constant value) {
+            return Constant.forPrimitiveInt(resultBits, CodeUtil.zeroExtend(value.asLong(), inputBits));
+        }
+
+        @Override
+        public Stamp foldStamp(int resultBits, Stamp input) {
+            IntegerStamp stamp = (IntegerStamp) input;
+            int inputBits = stamp.getBits();
+            assert inputBits <= resultBits;
+
+            long downMask = CodeUtil.zeroExtend(stamp.downMask(), inputBits);
+            long upMask = CodeUtil.zeroExtend(stamp.upMask(), inputBits);
+
+            if (stamp.lowerBound() < 0 && stamp.upperBound() >= 0) {
+                // signed range including 0 and -1
+                // after sign extension, the whole range from 0 to MAX_INT is possible
+                return IntegerStamp.stampForMask(resultBits, downMask, upMask);
+            }
+
+            long lowerBound = CodeUtil.zeroExtend(stamp.lowerBound(), inputBits);
+            long upperBound = CodeUtil.zeroExtend(stamp.upperBound(), inputBits);
+
+            return new IntegerStamp(resultBits, lowerBound, upperBound, downMask, upMask);
+        }
+    },
+
+    new IntegerConvertOp(SIGN_EXTEND) {
+
+        @Override
+        public Constant foldConstant(int inputBits, int resultBits, Constant value) {
+            return Constant.forPrimitiveInt(resultBits, CodeUtil.signExtend(value.asLong(), inputBits));
+        }
+
+        @Override
+        public Stamp foldStamp(int resultBits, Stamp input) {
+            IntegerStamp stamp = (IntegerStamp) input;
+            int inputBits = stamp.getBits();
+            assert inputBits <= resultBits;
+
+            long defaultMask = CodeUtil.mask(resultBits);
+            long downMask = CodeUtil.signExtend(stamp.downMask(), inputBits) & defaultMask;
+            long upMask = CodeUtil.signExtend(stamp.upMask(), inputBits) & defaultMask;
+
+            return new IntegerStamp(resultBits, stamp.lowerBound(), stamp.upperBound(), downMask, upMask);
+        }
+    },
+
+    new IntegerConvertOp(NARROW) {
+
+        @Override
+        public Constant foldConstant(int inputBits, int resultBits, Constant value) {
+            return Constant.forPrimitiveInt(resultBits, CodeUtil.narrow(value.asLong(), resultBits));
+        }
+
+        @Override
+        public Stamp foldStamp(int resultBits, Stamp input) {
+            IntegerStamp stamp = (IntegerStamp) input;
+            int inputBits = stamp.getBits();
+            assert resultBits <= inputBits;
+            if (resultBits == inputBits) {
+                return stamp;
+            }
+
+            final long upperBound;
+            if (stamp.lowerBound() < CodeUtil.minValue(resultBits)) {
+                upperBound = CodeUtil.maxValue(resultBits);
+            } else {
+                upperBound = saturate(stamp.upperBound(), resultBits);
+            }
+            final long lowerBound;
+            if (stamp.upperBound() > CodeUtil.maxValue(resultBits)) {
+                lowerBound = CodeUtil.minValue(resultBits);
+            } else {
+                lowerBound = saturate(stamp.lowerBound(), resultBits);
+            }
+
+            long defaultMask = CodeUtil.mask(resultBits);
+            long newDownMask = stamp.downMask() & defaultMask;
+            long newUpMask = stamp.upMask() & defaultMask;
+            long newLowerBound = CodeUtil.signExtend((lowerBound | newDownMask) & newUpMask, resultBits);
+            long newUpperBound = CodeUtil.signExtend((upperBound | newDownMask) & newUpMask, resultBits);
+            return new IntegerStamp(resultBits, newLowerBound, newUpperBound, newDownMask, newUpMask);
+        }
+    },
+
+    new FloatConvertOp(I2F) {
+
+        @Override
+        public Constant foldConstant(Constant value) {
+            return Constant.forFloat(value.asInt());
+        }
+
+        @Override
+        public Stamp foldStamp(Stamp input) {
+            IntegerStamp stamp = (IntegerStamp) input;
+            assert stamp.getBits() == 32;
+            return StampFactory.forFloat(Kind.Float, stamp.lowerBound(), stamp.upperBound(), true);
+        }
+    },
+
+    new FloatConvertOp(L2F) {
+
+        @Override
+        public Constant foldConstant(Constant value) {
+            return Constant.forFloat(value.asLong());
+        }
+
+        @Override
+        public Stamp foldStamp(Stamp input) {
+            IntegerStamp stamp = (IntegerStamp) input;
+            assert stamp.getBits() == 64;
+            return StampFactory.forFloat(Kind.Float, stamp.lowerBound(), stamp.upperBound(), true);
+        }
+    },
+
+    new FloatConvertOp(I2D) {
+
+        @Override
+        public Constant foldConstant(Constant value) {
+            return Constant.forDouble(value.asInt());
+        }
+
+        @Override
+        public Stamp foldStamp(Stamp input) {
+            IntegerStamp stamp = (IntegerStamp) input;
+            assert stamp.getBits() == 32;
+            return StampFactory.forFloat(Kind.Double, stamp.lowerBound(), stamp.upperBound(), true);
+        }
+    },
+
+    new FloatConvertOp(L2D) {
+
+        @Override
+        public Constant foldConstant(Constant value) {
+            return Constant.forDouble(value.asLong());
+        }
+
+        @Override
+        public Stamp foldStamp(Stamp input) {
+            IntegerStamp stamp = (IntegerStamp) input;
+            assert stamp.getBits() == 64;
+            return StampFactory.forFloat(Kind.Double, stamp.lowerBound(), stamp.upperBound(), true);
+        }
     });
 }
--- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotLoweringProvider.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotLoweringProvider.java	Mon Oct 06 11:51:32 2014 +0200
@@ -106,7 +106,7 @@
     private HashMap<NodeClass, LoweringStrategy> strategyMap = new HashMap<>();
 
     void initStrategyMap() {
-        strategyMap.put(NodeClass.get(ConvertNode.class), PassThruStrategy);
+        strategyMap.put(NodeClass.get(IntegerConvertNode.class), PassThruStrategy);
         strategyMap.put(NodeClass.get(FloatConvertNode.class), PassThruStrategy);
         strategyMap.put(NodeClass.get(NewInstanceNode.class), NewObjectStrategy);
         strategyMap.put(NodeClass.get(NewArrayNode.class), NewObjectStrategy);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CompressionNode.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CompressionNode.java	Mon Oct 06 11:51:32 2014 +0200
@@ -40,7 +40,7 @@
  * Compress or uncompress an oop or metaspace pointer.
  */
 @NodeInfo(nameTemplate = "{p#op/s}")
-public class CompressionNode extends ConvertNode implements LIRLowerable {
+public class CompressionNode extends UnaryNode implements ConvertNode, LIRLowerable {
 
     enum CompressionOp {
         Compress,
--- a/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/IntegerStampTest.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/IntegerStampTest.java	Mon Oct 06 11:51:32 2014 +0200
@@ -27,6 +27,7 @@
 import org.junit.*;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.type.ArithmeticOpTable.IntegerConvertOp;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.type.*;
@@ -128,38 +129,41 @@
         assertEquals(new IntegerStamp(64, -10000, 1000, 0, 0xffffffffffffffffL), StampFactory.forInteger(Kind.Long, -10000, 1000));
     }
 
+    private static Stamp narrowingKindConversion(Stamp stamp, Kind kind) {
+        Stamp narrow = IntegerStamp.OPS.getNarrow().foldStamp(kind.getBitCount(), stamp);
+        IntegerConvertOp implicitExtend = kind.isUnsigned() ? IntegerStamp.OPS.getZeroExtend() : IntegerStamp.OPS.getSignExtend();
+        return implicitExtend.foldStamp(32, narrow);
+    }
+
     @Test
     public void testNarrowingConversions() {
         // byte cases
-        assertEquals(StampFactory.forInteger(Kind.Int, 0, 0), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, 0, 0), Kind.Byte));
-        assertEquals(StampFactory.forInteger(Kind.Int, 0, 10), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, 0, 10), Kind.Byte));
-        assertEquals(StampFactory.forInteger(Kind.Int, 10, 20), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, 10, 20), Kind.Byte));
-        assertEquals(StampFactory.forInteger(Kind.Int, -10, 0), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, -10, 0), Kind.Byte));
-        assertEquals(StampFactory.forInteger(Kind.Int, -20, -10), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, -20, -10), Kind.Byte));
-        assertEquals(StampFactory.forInteger(Kind.Int, Byte.MIN_VALUE, Byte.MAX_VALUE), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, 100, 200), Kind.Byte));
-        assertEquals(StampFactory.forInteger(Kind.Int, Byte.MIN_VALUE, Byte.MAX_VALUE), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, -100, 200), Kind.Byte));
-        assertEquals(StampFactory.forInteger(Kind.Int, Byte.MIN_VALUE, Byte.MAX_VALUE), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, -200, -100), Kind.Byte));
+        assertEquals(StampFactory.forInteger(Kind.Int, 0, 0), narrowingKindConversion(StampFactory.forInteger(Kind.Int, 0, 0), Kind.Byte));
+        assertEquals(StampFactory.forInteger(Kind.Int, 0, 10), narrowingKindConversion(StampFactory.forInteger(Kind.Int, 0, 10), Kind.Byte));
+        assertEquals(StampFactory.forInteger(Kind.Int, 10, 20), narrowingKindConversion(StampFactory.forInteger(Kind.Int, 10, 20), Kind.Byte));
+        assertEquals(StampFactory.forInteger(Kind.Int, -10, 0), narrowingKindConversion(StampFactory.forInteger(Kind.Int, -10, 0), Kind.Byte));
+        assertEquals(StampFactory.forInteger(Kind.Int, -20, -10), narrowingKindConversion(StampFactory.forInteger(Kind.Int, -20, -10), Kind.Byte));
+        assertEquals(StampFactory.forInteger(Kind.Int, Byte.MIN_VALUE, Byte.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Int, 100, 200), Kind.Byte));
+        assertEquals(StampFactory.forInteger(Kind.Int, Byte.MIN_VALUE, Byte.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Int, -100, 200), Kind.Byte));
+        assertEquals(StampFactory.forInteger(Kind.Int, Byte.MIN_VALUE, Byte.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Int, -200, -100), Kind.Byte));
         // char cases
-        assertEquals(StampFactory.forInteger(Kind.Int, 0, 10), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, 0, 10), Kind.Char));
-        assertEquals(StampFactory.forInteger(Kind.Int, 10, 20), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, 10, 20), Kind.Char));
-        assertEquals(StampFactory.forInteger(Kind.Int, Character.MIN_VALUE, Character.MAX_VALUE), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, 20000, 80000), Kind.Char));
-        assertEquals(StampFactory.forInteger(Kind.Int, Character.MIN_VALUE, Character.MAX_VALUE), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, -10000, 40000), Kind.Char));
-        assertEquals(StampFactory.forInteger(Kind.Int, Character.MIN_VALUE, Character.MAX_VALUE), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, -40000, -10000), Kind.Char));
+        assertEquals(StampFactory.forInteger(Kind.Int, 0, 10), narrowingKindConversion(StampFactory.forInteger(Kind.Int, 0, 10), Kind.Char));
+        assertEquals(StampFactory.forInteger(Kind.Int, 10, 20), narrowingKindConversion(StampFactory.forInteger(Kind.Int, 10, 20), Kind.Char));
+        assertEquals(StampFactory.forInteger(Kind.Int, Character.MIN_VALUE, Character.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Int, 20000, 80000), Kind.Char));
+        assertEquals(StampFactory.forInteger(Kind.Int, Character.MIN_VALUE, Character.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Int, -10000, 40000), Kind.Char));
+        assertEquals(StampFactory.forInteger(Kind.Int, Character.MIN_VALUE, Character.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Int, -40000, -10000), Kind.Char));
         // short cases
-        assertEquals(StampFactory.forInteger(Kind.Int, 0, 10), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, 0, 10), Kind.Short));
-        assertEquals(StampFactory.forInteger(Kind.Int, 10, 20), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, 10, 20), Kind.Short));
-        assertEquals(StampFactory.forInteger(Kind.Int, Short.MIN_VALUE, Short.MAX_VALUE), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, 20000, 40000), Kind.Short));
-        assertEquals(StampFactory.forInteger(Kind.Int, Short.MIN_VALUE, Short.MAX_VALUE), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, -10000, 40000), Kind.Short));
-        assertEquals(StampFactory.forInteger(Kind.Int, Short.MIN_VALUE, Short.MAX_VALUE), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Int, -40000, -10000), Kind.Short));
+        assertEquals(StampFactory.forInteger(Kind.Int, 0, 10), narrowingKindConversion(StampFactory.forInteger(Kind.Int, 0, 10), Kind.Short));
+        assertEquals(StampFactory.forInteger(Kind.Int, 10, 20), narrowingKindConversion(StampFactory.forInteger(Kind.Int, 10, 20), Kind.Short));
+        assertEquals(StampFactory.forInteger(Kind.Int, Short.MIN_VALUE, Short.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Int, 20000, 40000), Kind.Short));
+        assertEquals(StampFactory.forInteger(Kind.Int, Short.MIN_VALUE, Short.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Int, -10000, 40000), Kind.Short));
+        assertEquals(StampFactory.forInteger(Kind.Int, Short.MIN_VALUE, Short.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Int, -40000, -10000), Kind.Short));
         // int cases
-        assertEquals(StampFactory.forInteger(Kind.Int, 0, 10), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Long, 0, 10), Kind.Int));
-        assertEquals(StampFactory.forInteger(Kind.Int, 10, 20), StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Long, 10, 20), Kind.Int));
-        assertEquals(StampFactory.forInteger(Kind.Int, Integer.MIN_VALUE, Integer.MAX_VALUE),
-                        StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Long, 20000000000L, 40000000000L), Kind.Int));
-        assertEquals(StampFactory.forInteger(Kind.Int, Integer.MIN_VALUE, Integer.MAX_VALUE),
-                        StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Long, -10000000000L, 40000000000L), Kind.Int));
-        assertEquals(StampFactory.forInteger(Kind.Int, Integer.MIN_VALUE, Integer.MAX_VALUE),
-                        StampTool.narrowingKindConversion(StampFactory.forInteger(Kind.Long, -40000000000L, -10000000000L), Kind.Int));
+        assertEquals(StampFactory.forInteger(Kind.Int, 0, 10), narrowingKindConversion(StampFactory.forInteger(Kind.Long, 0, 10), Kind.Int));
+        assertEquals(StampFactory.forInteger(Kind.Int, 10, 20), narrowingKindConversion(StampFactory.forInteger(Kind.Long, 10, 20), Kind.Int));
+        assertEquals(StampFactory.forInteger(Kind.Int, Integer.MIN_VALUE, Integer.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Long, 20000000000L, 40000000000L), Kind.Int));
+        assertEquals(StampFactory.forInteger(Kind.Int, Integer.MIN_VALUE, Integer.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Long, -10000000000L, 40000000000L), Kind.Int));
+        assertEquals(StampFactory.forInteger(Kind.Int, Integer.MIN_VALUE, Integer.MAX_VALUE), narrowingKindConversion(StampFactory.forInteger(Kind.Long, -40000000000L, -10000000000L), Kind.Int));
     }
 
     @Test
@@ -269,7 +273,7 @@
 
     private static void testSignExtendShort(long lower, long upper) {
         Stamp shortStamp = StampFactory.forInteger(16, lower, upper);
-        Stamp intStamp = StampTool.signExtend(shortStamp, 32);
+        Stamp intStamp = IntegerStamp.OPS.getSignExtend().foldStamp(32, shortStamp);
         assertEquals(StampFactory.forInteger(32, lower, upper), intStamp);
     }
 
@@ -285,7 +289,7 @@
 
     private static void testZeroExtendShort(long lower, long upper, long newLower, long newUpper) {
         Stamp shortStamp = StampFactory.forInteger(16, lower, upper);
-        Stamp intStamp = StampTool.zeroExtend(shortStamp, 32);
+        Stamp intStamp = IntegerStamp.OPS.getZeroExtend().foldStamp(32, shortStamp);
         assertEquals(StampFactory.forInteger(32, newLower, newUpper), intStamp);
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java	Mon Oct 06 11:51:32 2014 +0200
@@ -25,30 +25,26 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.calc.*;
-import com.oracle.graal.compiler.common.type.*;
-import com.oracle.graal.nodeinfo.*;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 
 /**
  * Represents a conversion between primitive types.
  */
-@NodeInfo
-public abstract class ConvertNode extends UnaryNode implements ArithmeticOperation {
+public interface ConvertNode extends ArithmeticOperation, NodeInterface {
+
+    ValueNode getValue();
 
-    protected ConvertNode(Stamp stamp, ValueNode value) {
-        super(stamp, value);
-    }
+    Constant convert(Constant c);
 
-    public abstract Constant convert(Constant c);
-
-    public abstract Constant reverse(Constant c);
+    Constant reverse(Constant c);
 
     /**
      * Check whether a conversion is lossless.
      *
      * @return true iff reverse(convert(c)) == c for all c
      */
-    public abstract boolean isLossless();
+    boolean isLossless();
 
     /**
      * Check whether a conversion preserves comparison order.
@@ -56,13 +52,14 @@
      * @param op a comparison operator
      * @return true iff (c1 op c2) == (convert(c1) op convert(c2)) for all c1, c2
      */
-    public boolean preservesOrder(Condition op) {
+    default boolean preservesOrder(Condition op) {
         return isLossless();
     }
 
-    @Override
-    public Constant evalConst(Constant... inputs) {
+    default Constant evalConst(Constant... inputs) {
         assert inputs.length == 1;
         return convert(inputs[0]);
     }
+
+    ValueNode asNode();
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java	Mon Oct 06 11:51:32 2014 +0200
@@ -23,9 +23,9 @@
 package com.oracle.graal.nodes.calc;
 
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.compiler.common.type.ArithmeticOpTable.FloatConvertOp;
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.nodeinfo.*;
@@ -37,114 +37,41 @@
  * semantics.
  */
 @NodeInfo
-public class FloatConvertNode extends ConvertNode implements Lowerable, ArithmeticLIRLowerable {
+public class FloatConvertNode extends UnaryArithmeticNode implements ConvertNode, Lowerable, ArithmeticLIRLowerable {
 
-    protected final FloatConvert op;
+    protected final FloatConvertOp reverseOp;
 
     public static FloatConvertNode create(FloatConvert op, ValueNode input) {
         return USE_GENERATED_NODES ? new FloatConvertNodeGen(op, input) : new FloatConvertNode(op, input);
     }
 
-    protected FloatConvertNode(FloatConvert op, ValueNode input) {
-        super(createStamp(op, input), input);
-        this.op = op;
+    private FloatConvertNode(ArithmeticOpTable table, FloatConvert op, ValueNode input) {
+        super(table.getFloatConvert(op), input);
+        ArithmeticOpTable revTable = ArithmeticOpTable.forStamp(stamp());
+        reverseOp = revTable.getFloatConvert(op.reverse());
     }
 
-    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:
-                return StampFactory.forFloat(Kind.Float, ((IntegerStamp) input.stamp()).lowerBound(), ((IntegerStamp) input.stamp()).upperBound(), true);
-            case D2F:
-                return StampFactory.forKind(Kind.Float);
-            case I2D:
-            case L2D:
-                return StampFactory.forFloat(Kind.Double, ((IntegerStamp) input.stamp()).lowerBound(), ((IntegerStamp) input.stamp()).upperBound(), true);
-            case F2D:
-                return StampFactory.forKind(Kind.Double);
-            default:
-                throw GraalInternalError.shouldNotReachHere();
-        }
+    protected FloatConvertNode(FloatConvert op, ValueNode input) {
+        this(ArithmeticOpTable.forStamp(input.stamp()), op, input);
     }
 
-    public FloatConvert getOp() {
-        return op;
-    }
-
-    @Override
-    public boolean inferStamp() {
-        return updateStamp(createStamp(op, getValue()));
-    }
-
-    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();
-        }
+    public FloatConvert getFloatConvert() {
+        return ((FloatConvertOp) getOp()).getFloatConvert();
     }
 
     @Override
     public Constant convert(Constant c) {
-        return convert(op, c);
+        return op.foldConstant(c);
     }
 
     @Override
     public Constant reverse(Constant c) {
-        return convert(op.reverse(), c);
+        return reverseOp.foldConstant(c);
     }
 
     @Override
     public boolean isLossless() {
-        switch (op) {
+        switch (getFloatConvert()) {
             case F2D:
             case I2D:
                 return true;
@@ -155,11 +82,14 @@
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
-        if (forValue.isConstant()) {
-            return ConstantNode.forConstant(evalConst(forValue.asConstant()), null);
-        } else if (forValue instanceof FloatConvertNode) {
+        ValueNode ret = super.canonical(tool, forValue);
+        if (ret != this) {
+            return ret;
+        }
+
+        if (forValue instanceof FloatConvertNode) {
             FloatConvertNode other = (FloatConvertNode) forValue;
-            if (other.isLossless() && other.op == this.op.reverse()) {
+            if (other.isLossless() && other.op == this.reverseOp) {
                 return other.getValue();
             }
         }
@@ -171,6 +101,6 @@
     }
 
     public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) {
-        builder.setResult(this, gen.emitFloatConvert(op, builder.operand(getValue())));
+        builder.setResult(this, gen.emitFloatConvert(getFloatConvert(), builder.operand(getValue())));
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java	Mon Oct 06 11:51:32 2014 +0200
@@ -22,7 +22,10 @@
  */
 package com.oracle.graal.nodes.calc;
 
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.type.ArithmeticOpTable.IntegerConvertOp;
 import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
@@ -31,12 +34,17 @@
  * An {@code IntegerConvert} converts an integer to an integer of different width.
  */
 @NodeInfo
-public abstract class IntegerConvertNode extends ConvertNode implements ArithmeticLIRLowerable {
+public abstract class IntegerConvertNode extends UnaryNode implements ConvertNode, ArithmeticLIRLowerable {
+
+    protected final IntegerConvertOp op;
+    protected final IntegerConvertOp reverseOp;
 
     protected final int resultBits;
 
-    protected IntegerConvertNode(Stamp stamp, ValueNode input, int resultBits) {
-        super(stamp, input);
+    protected IntegerConvertNode(IntegerConvertOp op, IntegerConvertOp reverseOp, int resultBits, ValueNode input) {
+        super(op.foldStamp(resultBits, input.stamp()), input);
+        this.op = op;
+        this.reverseOp = reverseOp;
         this.resultBits = resultBits;
     }
 
@@ -52,13 +60,29 @@
         }
     }
 
-    protected ValueNode canonicalConvert(@SuppressWarnings("hiding") ValueNode value) {
+    @Override
+    public Constant convert(Constant c) {
+        return op.foldConstant(getInputBits(), getResultBits(), c);
+    }
+
+    @Override
+    public Constant reverse(Constant c) {
+        return reverseOp.foldConstant(getResultBits(), getInputBits(), c);
+    }
+
+    @Override
+    public boolean inferStamp() {
+        return updateStamp(op.foldStamp(resultBits, getValue().stamp()));
+    }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
         if (value.stamp() instanceof IntegerStamp) {
             int inputBits = ((IntegerStamp) value.stamp()).getBits();
             if (inputBits == resultBits) {
                 return value;
             } else if (value.isConstant()) {
-                return ConstantNode.forIntegerBits(resultBits, evalConst(value.asConstant()).asLong());
+                return ConstantNode.forPrimitive(stamp(), convert(forValue.asConstant()));
             }
         }
         return this;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/NarrowNode.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/NarrowNode.java	Mon Oct 06 11:51:32 2014 +0200
@@ -22,14 +22,12 @@
  */
 package com.oracle.graal.nodes.calc;
 
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.nodeinfo.*;
 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.
@@ -41,19 +39,12 @@
         return USE_GENERATED_NODES ? new NarrowNodeGen(input, resultBits) : new NarrowNode(input, resultBits);
     }
 
-    protected NarrowNode(ValueNode input, int resultBits) {
-        super(StampTool.narrowingConversion(input.stamp(), resultBits), input, resultBits);
+    private NarrowNode(ArithmeticOpTable ops, ValueNode input, int resultBits) {
+        super(ops.getNarrow(), ops.getSignExtend(), resultBits, input);
     }
 
-    @Override
-    public Constant convert(Constant c) {
-        return Constant.forPrimitiveInt(getResultBits(), CodeUtil.narrow(c.asLong(), getResultBits()));
-    }
-
-    @Override
-    public Constant reverse(Constant input) {
-        long result = CodeUtil.signExtend(input.asLong(), getResultBits());
-        return Constant.forPrimitiveInt(getInputBits(), result);
+    protected NarrowNode(ValueNode input, int resultBits) {
+        this(ArithmeticOpTable.forStamp(input.stamp()), input, resultBits);
     }
 
     @Override
@@ -63,7 +54,7 @@
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
-        ValueNode ret = canonicalConvert(forValue);
+        ValueNode ret = super.canonical(tool, forValue);
         if (ret != this) {
             return ret;
         }
@@ -100,11 +91,6 @@
     }
 
     @Override
-    public boolean inferStamp() {
-        return updateStamp(StampTool.narrowingConversion(getValue().stamp(), getResultBits()));
-    }
-
-    @Override
     public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) {
         builder.setResult(this, gen.emitNarrow(builder.operand(getValue()), getResultBits()));
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SignExtendNode.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SignExtendNode.java	Mon Oct 06 11:51:32 2014 +0200
@@ -22,15 +22,12 @@
  */
 package com.oracle.graal.nodes.calc;
 
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.nodeinfo.*;
 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.
@@ -42,18 +39,12 @@
         return USE_GENERATED_NODES ? new SignExtendNodeGen(input, resultBits) : new SignExtendNode(input, resultBits);
     }
 
-    protected SignExtendNode(ValueNode input, int resultBits) {
-        super(StampTool.signExtend(input.stamp(), resultBits), input, resultBits);
+    private SignExtendNode(ArithmeticOpTable ops, ValueNode input, int resultBits) {
+        super(ops.getSignExtend(), ops.getNarrow(), resultBits, input);
     }
 
-    @Override
-    public Constant convert(Constant c) {
-        return Constant.forPrimitiveInt(getResultBits(), CodeUtil.signExtend(c.asLong(), getInputBits()));
-    }
-
-    @Override
-    public Constant reverse(Constant c) {
-        return Constant.forPrimitiveInt(getInputBits(), CodeUtil.narrow(c.asLong(), getInputBits()));
+    protected SignExtendNode(ValueNode input, int resultBits) {
+        this(ArithmeticOpTable.forStamp(input.stamp()), input, resultBits);
     }
 
     @Override
@@ -63,7 +54,7 @@
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
-        ValueNode ret = canonicalConvert(forValue);
+        ValueNode ret = super.canonical(tool, forValue);
         if (ret != this) {
             return ret;
         }
@@ -95,11 +86,6 @@
     }
 
     @Override
-    public boolean inferStamp() {
-        return updateStamp(StampTool.signExtend(getValue().stamp(), getResultBits()));
-    }
-
-    @Override
     public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) {
         builder.setResult(this, gen.emitSignExtend(builder.operand(getValue()), getInputBits(), getResultBits()));
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ZeroExtendNode.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ZeroExtendNode.java	Mon Oct 06 11:51:32 2014 +0200
@@ -23,7 +23,6 @@
 package com.oracle.graal.nodes.calc;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.spi.*;
@@ -31,7 +30,6 @@
 import com.oracle.graal.nodeinfo.*;
 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.
@@ -43,18 +41,12 @@
         return USE_GENERATED_NODES ? new ZeroExtendNodeGen(input, resultBits) : new ZeroExtendNode(input, resultBits);
     }
 
-    protected ZeroExtendNode(ValueNode input, int resultBits) {
-        super(StampTool.zeroExtend(input.stamp(), resultBits), input, resultBits);
+    private ZeroExtendNode(ArithmeticOpTable ops, ValueNode input, int resultBits) {
+        super(ops.getZeroExtend(), ops.getNarrow(), resultBits, input);
     }
 
-    @Override
-    public Constant convert(Constant c) {
-        return Constant.forPrimitiveInt(getResultBits(), CodeUtil.zeroExtend(c.asLong(), getInputBits()));
-    }
-
-    @Override
-    public Constant reverse(Constant c) {
-        return Constant.forPrimitiveInt(getInputBits(), CodeUtil.narrow(c.asLong(), getInputBits()));
+    protected ZeroExtendNode(ValueNode input, int resultBits) {
+        this(ArithmeticOpTable.forStamp(input.stamp()), input, resultBits);
     }
 
     @Override
@@ -63,8 +55,8 @@
     }
 
     @Override
-    public boolean preservesOrder(Condition op) {
-        switch (op) {
+    public boolean preservesOrder(Condition cond) {
+        switch (cond) {
             case GE:
             case GT:
             case LE:
@@ -77,7 +69,7 @@
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
-        ValueNode ret = canonicalConvert(forValue);
+        ValueNode ret = super.canonical(tool, forValue);
         if (ret != this) {
             return ret;
         }
@@ -106,11 +98,6 @@
     }
 
     @Override
-    public boolean inferStamp() {
-        return updateStamp(StampTool.zeroExtend(getValue().stamp(), getResultBits()));
-    }
-
-    @Override
     public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) {
         builder.setResult(this, gen.emitZeroExtend(builder.operand(getValue()), getInputBits(), getResultBits()));
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/IndexedLocationNode.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/IndexedLocationNode.java	Mon Oct 06 11:51:32 2014 +0200
@@ -108,7 +108,7 @@
         assert indexScaling > 0 && CodeUtil.isPowerOf2(indexScaling);
         int scale = CodeUtil.log2(indexScaling);
         return (IntegerStamp) IntegerStamp.OPS.getAdd().foldStamp(StampFactory.forInteger(64, displacement, displacement),
-                        StampTool.signExtend(StampTool.leftShift(index.stamp(), StampFactory.forInteger(64, scale, scale)), 64));
+                        IntegerStamp.OPS.getSignExtend().foldStamp(64, StampTool.leftShift(index.stamp(), StampFactory.forInteger(64, scale, scale))));
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampTool.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampTool.java	Mon Oct 06 11:51:32 2014 +0200
@@ -136,135 +136,6 @@
         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 = CodeUtil.mask(resultBits);
-            long downMask = CodeUtil.signExtend(inputStamp.downMask(), inputBits) & defaultMask;
-            long upMask = CodeUtil.signExtend(inputStamp.upMask(), inputBits) & defaultMask;
-
-            return new IntegerStamp(resultBits, 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 = CodeUtil.zeroExtend(inputStamp.downMask(), inputBits);
-            long upMask = CodeUtil.zeroExtend(inputStamp.upMask(), inputBits);
-
-            if (inputStamp.lowerBound() < 0 && inputStamp.upperBound() >= 0) {
-                // signed range including 0 and -1
-                // after sign extension, the whole range from 0 to MAX_INT is possible
-                return IntegerStamp.stampForMask(resultBits, downMask, upMask);
-            }
-
-            long lowerBound = CodeUtil.zeroExtend(inputStamp.lowerBound(), inputBits);
-            long upperBound = CodeUtil.zeroExtend(inputStamp.upperBound(), inputBits);
-
-            return new IntegerStamp(resultBits, lowerBound, upperBound, downMask, upMask);
-        } else {
-            return input.illegal();
-        }
-    }
-
-    public static Stamp narrowingConversion(Stamp input, int resultBits) {
-        if (input instanceof IntegerStamp) {
-            IntegerStamp inputStamp = (IntegerStamp) input;
-            int inputBits = inputStamp.getBits();
-            assert resultBits <= inputBits;
-            if (resultBits == inputBits) {
-                return inputStamp;
-            }
-
-            final long upperBound;
-            if (inputStamp.lowerBound() < CodeUtil.minValue(resultBits)) {
-                upperBound = CodeUtil.maxValue(resultBits);
-            } else {
-                upperBound = saturate(inputStamp.upperBound(), resultBits);
-            }
-            final long lowerBound;
-            if (inputStamp.upperBound() > CodeUtil.maxValue(resultBits)) {
-                lowerBound = CodeUtil.minValue(resultBits);
-            } else {
-                lowerBound = saturate(inputStamp.lowerBound(), resultBits);
-            }
-
-            long defaultMask = CodeUtil.mask(resultBits);
-            long newDownMask = inputStamp.downMask() & defaultMask;
-            long newUpMask = inputStamp.upMask() & defaultMask;
-            long newLowerBound = CodeUtil.signExtend((lowerBound | newDownMask) & newUpMask, resultBits);
-            long newUpperBound = CodeUtil.signExtend((upperBound | newDownMask) & newUpMask, resultBits);
-            return new IntegerStamp(resultBits, 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;
-        if (fromStamp.lowerBound() < toKind.getMinValue()) {
-            upperBound = toKind.getMaxValue();
-        } else {
-            upperBound = saturate(fromStamp.upperBound(), toKind);
-        }
-        final long lowerBound;
-        if (fromStamp.upperBound() > toKind.getMaxValue()) {
-            lowerBound = toKind.getMinValue();
-        } else {
-            lowerBound = saturate(fromStamp.lowerBound(), toKind);
-        }
-
-        long defaultMask = CodeUtil.mask(toKind.getBitCount());
-        long intMask = CodeUtil.mask(32);
-        long newUpMask = signExtend(fromStamp.upMask() & defaultMask, toKind) & intMask;
-        long newDownMask = signExtend(fromStamp.downMask() & defaultMask, toKind) & intMask;
-        return new IntegerStamp(toKind.getStackKind().getBitCount(), (int) ((lowerBound | newDownMask) & newUpMask), (int) ((upperBound | newDownMask) & newUpMask), newDownMask, newUpMask);
-    }
-
-    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());
-        } else {
-            return value;
-        }
-    }
-
-    private static long saturate(long v, int bits) {
-        if (bits < 64) {
-            long max = CodeUtil.maxValue(bits);
-            if (v > max) {
-                return max;
-            }
-            long min = CodeUtil.minValue(bits);
-            if (v < min) {
-                return min;
-            }
-        }
-        return v;
-    }
-
-    private static long saturate(long v, Kind kind) {
-        long max = kind.getMaxValue();
-        if (v > max) {
-            return max;
-        }
-        long min = kind.getMinValue();
-        if (v < min) {
-            return min;
-        }
-        return v;
-    }
-
     /**
      * Compute the stamp resulting from the unsigned comparison being true.
      *
--- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64ConvertSnippets.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64ConvertSnippets.java	Mon Oct 06 11:51:32 2014 +0200
@@ -161,7 +161,7 @@
 
         public void lower(FloatConvertNode convert, LoweringTool tool) {
             SnippetInfo key;
-            switch (convert.getOp()) {
+            switch (convert.getFloatConvert()) {
                 case F2I:
                     key = f2i;
                     break;
@@ -182,7 +182,7 @@
 
             Arguments args = new Arguments(key, graph.getGuardsStage(), tool.getLoweringStage());
             args.add("input", convert.getValue());
-            args.add("result", graph.unique(AMD64FloatConvertNode.create(convert.stamp(), convert.getOp(), convert.getValue())));
+            args.add("result", graph.unique(AMD64FloatConvertNode.create(convert.getOp(), convert.getValue())));
 
             SnippetTemplate template = template(args);
             Debug.log("Lowering %s in %s: node=%s, template=%s, arguments=%s", convert.getOp(), graph, convert, template, args);
--- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64FloatConvertNode.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64FloatConvertNode.java	Mon Oct 06 11:51:32 2014 +0200
@@ -24,8 +24,8 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
-import com.oracle.graal.compiler.common.calc.*;
-import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.compiler.common.type.ArithmeticOpTable.FloatConvertOp;
+import com.oracle.graal.compiler.common.type.ArithmeticOpTable.UnaryOp;
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.nodeinfo.*;
@@ -39,19 +39,17 @@
  * fixup code that handles the corner cases that differ between AMD64 and Java.
  */
 @NodeInfo
-public class AMD64FloatConvertNode extends UnaryNode implements ArithmeticLIRLowerable {
+public class AMD64FloatConvertNode extends UnaryArithmeticNode implements ArithmeticLIRLowerable {
 
-    protected final FloatConvert op;
-
-    public static AMD64FloatConvertNode create(Stamp stamp, FloatConvert op, ValueNode value) {
-        return USE_GENERATED_NODES ? new AMD64FloatConvertNodeGen(stamp, op, value) : new AMD64FloatConvertNode(stamp, op, value);
+    public static AMD64FloatConvertNode create(UnaryOp op, ValueNode value) {
+        return USE_GENERATED_NODES ? new AMD64FloatConvertNodeGen(op, value) : new AMD64FloatConvertNode(op, value);
     }
 
-    protected AMD64FloatConvertNode(Stamp stamp, FloatConvert op, ValueNode value) {
-        super(stamp, value);
-        this.op = op;
+    protected AMD64FloatConvertNode(UnaryOp op, ValueNode value) {
+        super(op, value);
     }
 
+    @Override
     public Constant evalConst(Constant... inputs) {
         // this node should never have been created if its input is constant
         throw GraalInternalError.shouldNotReachHere();
@@ -64,6 +62,6 @@
     }
 
     public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) {
-        builder.setResult(this, gen.emitFloatConvert(op, builder.operand(getValue())));
+        builder.setResult(this, gen.emitFloatConvert(((FloatConvertOp) getOp()).getFloatConvert(), builder.operand(getValue())));
     }
 }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultJavaLoweringProvider.java	Mon Oct 06 10:40:55 2014 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultJavaLoweringProvider.java	Mon Oct 06 11:51:32 2014 +0200
@@ -534,10 +534,10 @@
         switch (kind) {
             case Boolean:
             case Byte:
-                return StampTool.narrowingConversion(stamp, 8);
+                return IntegerStamp.OPS.getNarrow().foldStamp(8, stamp);
             case Char:
             case Short:
-                return StampTool.narrowingConversion(stamp, 16);
+                return IntegerStamp.OPS.getNarrow().foldStamp(16, stamp);
         }
         return stamp;
     }