changeset 15373:85e1bf62208c

Merge.
author Chris Seaton <chris.seaton@oracle.com>
date Thu, 24 Apr 2014 23:32:30 +0100
parents 2cea065e419d (current diff) 319deee16746 (diff)
children 669536c4949a
files
diffstat 36 files changed, 2629 insertions(+), 80 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG.md	Thu Apr 24 23:29:28 2014 +0100
+++ b/CHANGELOG.md	Thu Apr 24 23:32:30 2014 +0100
@@ -8,6 +8,7 @@
 * Added graal.version system property to Graal enabled VM builds.
 * Transitioned to JDK 8 as minimum JDK level for Graal.
 * Added support for stack introspection.
+* New MatchRule facility to convert multiple HIR nodes into specialized LIR
 * ...
 
 ### Truffle
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Value.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Value.java	Thu Apr 24 23:32:30 2014 +0100
@@ -40,12 +40,30 @@
         }
     };
 
+    /**
+     * This is the Value of a node which was matched as part of a complex match. The value isn't
+     * actually useable but this marks it as having been evaluated.
+     */
+    @SuppressWarnings("serial") public static Value INTERIOR_MATCH = new Value(Kind.Illegal) {
+
+        @Override
+        public String toString() {
+            return "INTERIOR_MATCH";
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            // This class is a singleton
+            return other != null && getClass() == other.getClass();
+        }
+    };
+
     private final Kind kind;
     private final PlatformKind platformKind;
 
     /**
      * Initializes a new value of the specified kind.
-     * 
+     *
      * @param platformKind the kind
      */
     protected Value(PlatformKind platformKind) {
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Thu Apr 24 23:32:30 2014 +0100
@@ -263,6 +263,25 @@
         }
     }
 
+    public void emitCompareBranchMemory(Kind cmpKind, Value left, AMD64AddressValue right, LIRFrameState state, Condition cond, boolean unorderedIsTrue, LabelRef trueLabel, LabelRef falseLabel,
+                    double trueLabelProbability) {
+        boolean mirrored = emitCompareMemory(cmpKind, left, right, state);
+        Condition finalCondition = mirrored ? cond.mirror() : cond;
+        switch (left.getKind().getStackKind()) {
+            case Int:
+            case Long:
+            case Object:
+                append(new BranchOp(finalCondition, trueLabel, falseLabel, trueLabelProbability));
+                break;
+            case Float:
+            case Double:
+                append(new FloatBranchOp(finalCondition, unorderedIsTrue, trueLabel, falseLabel, trueLabelProbability));
+                break;
+            default:
+                throw GraalInternalError.shouldNotReachHere("" + left.getKind());
+        }
+    }
+
     @Override
     public void emitOverflowCheckBranch(LabelRef overflow, LabelRef noOverflow, double overflowProbability) {
         append(new BranchOp(ConditionFlag.Overflow, overflow, noOverflow, overflowProbability));
@@ -343,6 +362,26 @@
         }
     }
 
+    /**
+     * This method emits the compare against memory instruction, and may reorder the operands. It
+     * returns true if it did so.
+     *
+     * @param b the right operand of the comparison
+     * @return true if the left and right operands were switched, false otherwise
+     */
+    private boolean emitCompareMemory(Kind cmpKind, Value a, AMD64AddressValue b, LIRFrameState state) {
+        boolean mirrored;
+        if (LIRValueUtil.isVariable(a)) {
+            Variable left = load(a);
+            emitCompareRegMemoryOp(cmpKind, left, b, state);
+            mirrored = false;
+        } else {
+            emitCompareMemoryConOp(cmpKind, b, a, state);
+            mirrored = true;
+        }
+        return mirrored;
+    }
+
     protected void emitCompareMemoryConOp(Kind kind, AMD64AddressValue address, Value value, LIRFrameState state) {
         assert kind.getStackKind() == value.getKind().getStackKind();
         switch (kind) {
@@ -761,6 +800,28 @@
         }
     }
 
+    public Variable emitRol(Value a, Value b) {
+        switch (a.getKind().getStackKind()) {
+            case Int:
+                return emitShift(IROL, a, b);
+            case Long:
+                return emitShift(LROL, a, b);
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
+    public Variable emitRor(Value a, Value b) {
+        switch (a.getKind().getStackKind()) {
+            case Int:
+                return emitShift(IROR, a, b);
+            case Long:
+                return emitShift(LROR, a, b);
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
     private AllocatableValue emitConvert2RegOp(PlatformKind kind, AMD64Arithmetic op, AllocatableValue input) {
         Variable result = newVariable(kind);
         append(new Unary2RegOp(op, result, input));
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java	Thu Apr 24 23:32:30 2014 +0100
@@ -23,15 +23,24 @@
 
 package com.oracle.graal.compiler.amd64;
 
+import static com.oracle.graal.lir.amd64.AMD64Arithmetic.*;
+
 import com.oracle.graal.amd64.*;
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.gen.*;
+import com.oracle.graal.compiler.match.*;
+import com.oracle.graal.debug.*;
 import com.oracle.graal.lir.*;
 import com.oracle.graal.lir.amd64.*;
+import com.oracle.graal.lir.amd64.AMD64ControlFlow.BranchOp;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
 
 public abstract class AMD64NodeLIRBuilder extends NodeLIRBuilder {
@@ -91,6 +100,525 @@
         return false;
     }
 
+    protected LIRFrameState getState(Access access) {
+        if (access instanceof DeoptimizingNode) {
+            return state((DeoptimizingNode) access);
+        }
+        return null;
+    }
+
+    protected Kind getMemoryKind(Access access) {
+        return (Kind) gen.getPlatformKind(access.asNode().stamp());
+    }
+
+    protected AMD64AddressValue makeAddress(Access access) {
+        return (AMD64AddressValue) access.accessLocation().generateAddress(this, gen, operand(access.object()));
+    }
+
+    protected ValueNode uncast(ValueNode value) {
+        if (value instanceof UnsafeCastNode) {
+            UnsafeCastNode cast = (UnsafeCastNode) value;
+            return cast.getOriginalNode();
+        }
+        return value;
+    }
+
+    protected ComplexMatchResult emitCompareBranchMemory(IfNode ifNode, CompareNode compare, ValueNode value, Access access) {
+        Condition cond = compare.condition();
+        Kind kind = getMemoryKind(access);
+
+        if (value.isConstant()) {
+            Constant constant = value.asConstant();
+            if (kind == Kind.Long && !NumUtil.isInt(constant.asLong())) {
+                // Only imm32 as long
+                return null;
+            }
+            if (kind.isNumericFloat()) {
+                Debug.log("Skipping constant compares for float kinds");
+                return null;
+            }
+            if (kind == Kind.Object) {
+                if (!constant.isNull()) {
+                    Debug.log("Skipping constant compares for Object kinds");
+                    return null;
+                }
+            }
+        } else {
+            if (kind == Kind.Object) {
+                // Can't compare against objects since they require encode/decode
+                Debug.log("Skipping compares for Object kinds");
+                return null;
+            }
+        }
+
+        PlatformKind cmpKind = gen.getPlatformKind(compare.x().stamp());
+        if (cmpKind instanceof Kind) {
+            // emitCompareBranchMemory expects the memory on the right, so mirror the condition if
+            // that's not true. It might be mirrored again the actual compare is emitted but that's
+            // ok.
+            Condition finalCondition = uncast(compare.x()) == access ? cond.mirror() : cond;
+            return new ComplexMatchResult() {
+                public Value evaluate(NodeLIRBuilder builder) {
+                    LabelRef trueLabel = getLIRBlock(ifNode.trueSuccessor());
+                    LabelRef falseLabel = getLIRBlock(ifNode.falseSuccessor());
+                    boolean unorderedIsTrue = compare.unorderedIsTrue();
+                    double trueLabelProbability = ifNode.probability(ifNode.trueSuccessor());
+                    Value other;
+                    if (value.isConstant()) {
+                        other = value.asConstant();
+                    } else {
+                        other = operand(value);
+                    }
+
+                    getLIRGeneratorTool().emitCompareBranchMemory((Kind) cmpKind, other, makeAddress(access), getState(access), finalCondition, unorderedIsTrue, trueLabel, falseLabel,
+                                    trueLabelProbability);
+                    return null;
+                }
+            };
+        }
+        return null;
+
+    }
+
+    private ComplexMatchResult emitIntegerTestBranchMemory(IfNode x, ValueNode value, Access access) {
+        LabelRef trueLabel = getLIRBlock(x.trueSuccessor());
+        LabelRef falseLabel = getLIRBlock(x.falseSuccessor());
+        double trueLabelProbability = x.probability(x.trueSuccessor());
+        Kind kind = getMemoryKind(access);
+        if (value.isConstant()) {
+            if (kind != kind.getStackKind()) {
+                return null;
+            }
+            Constant constant = value.asConstant();
+            if (kind == Kind.Long && !NumUtil.isInt(constant.asLong())) {
+                // Only imm32 as long
+                return null;
+            }
+            return new ComplexMatchResult() {
+                public Value evaluate(NodeLIRBuilder builder) {
+                    gen.append(new AMD64TestMemoryOp(kind, makeAddress(access), constant, getState(access)));
+                    gen.append(new BranchOp(Condition.EQ, trueLabel, falseLabel, trueLabelProbability));
+                    return null;
+                }
+            };
+        } else {
+            return new ComplexMatchResult() {
+                public Value evaluate(NodeLIRBuilder builder) {
+                    gen.append(new AMD64TestMemoryOp(kind, makeAddress(access), operand(value), getState(access)));
+                    gen.append(new BranchOp(Condition.EQ, trueLabel, falseLabel, trueLabelProbability));
+                    return null;
+                }
+            };
+        }
+    }
+
+    protected Value emitConvert2MemoryOp(PlatformKind kind, AMD64Arithmetic op, Access access) {
+        AMD64AddressValue address = makeAddress(access);
+        LIRFrameState state = getState(access);
+        return getLIRGeneratorTool().emitConvert2MemoryOp(kind, op, address, state);
+    }
+
+    private Value emitFloatConvertMemory(FloatConvertNode op, Access access) {
+        switch (op.getOp()) {
+            case D2F:
+                return emitConvert2MemoryOp(Kind.Float, D2F, access);
+            case D2I:
+                return emitConvert2MemoryOp(Kind.Int, D2I, access);
+            case D2L:
+                return emitConvert2MemoryOp(Kind.Long, D2L, access);
+            case F2D:
+                return emitConvert2MemoryOp(Kind.Double, F2D, access);
+            case F2I:
+                return emitConvert2MemoryOp(Kind.Int, F2I, access);
+            case F2L:
+                return emitConvert2MemoryOp(Kind.Long, F2L, access);
+            case I2D:
+                return emitConvert2MemoryOp(Kind.Double, I2D, access);
+            case I2F:
+                return emitConvert2MemoryOp(Kind.Float, I2F, access);
+            case L2D:
+                return emitConvert2MemoryOp(Kind.Double, L2D, access);
+            case L2F:
+                return emitConvert2MemoryOp(Kind.Float, L2F, access);
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+    }
+
+    private ComplexMatchResult emitSignExtendMemory(Access access, int fromBits, int toBits) {
+        assert fromBits <= toBits && toBits <= 64;
+        Kind kind = null;
+        AMD64Arithmetic op = null;
+        if (fromBits == toBits) {
+            return null;
+        } else if (toBits > 32) {
+            kind = Kind.Long;
+            // sign extend to 64 bits
+            switch (fromBits) {
+                case 8:
+                    op = B2L;
+                    break;
+                case 16:
+                    op = S2L;
+                    break;
+                case 32:
+                    op = I2L;
+                    break;
+                default:
+                    throw GraalInternalError.unimplemented("unsupported sign extension (" + fromBits + " bit -> " + toBits + " bit)");
+            }
+        } else {
+            kind = Kind.Int;
+            // sign extend to 32 bits (smaller values are internally represented as 32 bit values)
+            switch (fromBits) {
+                case 8:
+                    op = B2I;
+                    break;
+                case 16:
+                    op = S2I;
+                    break;
+                case 32:
+                    return null;
+                default:
+                    throw GraalInternalError.unimplemented("unsupported sign extension (" + fromBits + " bit -> " + toBits + " bit)");
+            }
+        }
+        if (kind != null && op != null) {
+            Kind localKind = kind;
+            AMD64Arithmetic localOp = op;
+            return new ComplexMatchResult() {
+                public Value evaluate(NodeLIRBuilder builder) {
+                    return emitConvert2MemoryOp(localKind, localOp, access);
+                }
+            };
+        }
+        return null;
+    }
+
+    private Value emitReinterpretMemory(PlatformKind to, Access access) {
+        Kind from = getMemoryKind(access);
+        assert to != from : "should have been eliminated";
+
+        /*
+         * Conversions between integer to floating point types require moves between CPU and FPU
+         * registers.
+         */
+        switch ((Kind) to) {
+            case Int:
+                switch (from) {
+                    case Float:
+                        return emitConvert2MemoryOp(to, MOV_F2I, access);
+                }
+                break;
+            case Long:
+                switch (from) {
+                    case Double:
+                        return emitConvert2MemoryOp(to, MOV_D2L, access);
+                }
+                break;
+            case Float:
+                switch (from) {
+                    case Int:
+                        return emitConvert2MemoryOp(to, MOV_I2F, access);
+                }
+                break;
+            case Double:
+                switch (from) {
+                    case Long:
+                        return emitConvert2MemoryOp(to, MOV_L2D, access);
+                }
+                break;
+        }
+        throw GraalInternalError.shouldNotReachHere();
+    }
+
+    static Object lock = new Object();
+
+    private AMD64Arithmetic getOp(ValueNode operation, Access access) {
+        Kind memoryKind = getMemoryKind(access);
+        if (operation.getClass() == IntegerAddNode.class) {
+            switch (memoryKind) {
+                case Int:
+                    return IADD;
+                case Long:
+                    return LADD;
+            }
+        } else if (operation.getClass() == FloatAddNode.class) {
+            switch (memoryKind) {
+                case Float:
+                    return FADD;
+                case Double:
+                    return DADD;
+            }
+        } else if (operation.getClass() == AndNode.class) {
+            switch (memoryKind) {
+                case Int:
+                    return IAND;
+                case Long:
+                    return LAND;
+            }
+        } else if (operation.getClass() == OrNode.class) {
+            switch (memoryKind) {
+                case Int:
+                    return IOR;
+                case Long:
+                    return LOR;
+            }
+        } else if (operation.getClass() == XorNode.class) {
+            switch (memoryKind) {
+                case Int:
+                    return IXOR;
+                case Long:
+                    return LXOR;
+            }
+        } else if (operation.getClass() == IntegerSubNode.class) {
+            switch (memoryKind) {
+                case Int:
+                    return ISUB;
+                case Long:
+                    return LSUB;
+            }
+        } else if (operation.getClass() == FloatSubNode.class) {
+            switch (memoryKind) {
+                case Float:
+                    return FSUB;
+                case Double:
+                    return DSUB;
+            }
+        } else if (operation.getClass() == IntegerMulNode.class) {
+            switch (memoryKind) {
+                case Int:
+                    return IMUL;
+                case Long:
+                    return LMUL;
+            }
+        } else if (operation.getClass() == FloatMulNode.class) {
+            switch (memoryKind) {
+                case Float:
+                    return FMUL;
+                case Double:
+                    return DMUL;
+            }
+        }
+        return null;
+    }
+
+    @MatchRule("(If (IntegerTest=compare Read=access value))")
+    @MatchRule("(If (IntegerTest=compare FloatingRead=access value))")
+    public static class IfIntegerTest extends AMD64MatchGenerator {
+        IfNode root;
+        Access access;
+        ValueNode value;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            return gen.emitIntegerTestBranchMemory(root, value, access);
+        }
+    }
+
+    @MatchRule("(If (IntegerEquals=compare value Read=access))")
+    @MatchRule("(If (IntegerLessThan=compare value Read=access))")
+    @MatchRule("(If (IntegerBelowThan=compare value Read=access))")
+    @MatchRule("(If (IntegerEquals=compare value FloatingRead=access))")
+    @MatchRule("(If (IntegerLessThan=compare value FloatingRead=access))")
+    @MatchRule("(If (IntegerBelowThan=compare value FloatingRead=access))")
+    @MatchRule("(If (FloatEquals=compare value Read=access))")
+    @MatchRule("(If (FloatEquals=compare value FloatingRead=access))")
+    @MatchRule("(If (FloatLessThan=compare value Read=access))")
+    @MatchRule("(If (FloatLessThan=compare value FloatingRead=access))")
+    public static class IfCompareMemory extends AMD64MatchGenerator {
+        IfNode root;
+        Access access;
+        ValueNode value;
+        CompareNode compare;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            return gen.emitCompareBranchMemory(root, compare, value, access);
+        }
+    }
+
+    @MatchRule("(Or (LeftShift=lshift value Constant) (UnsignedRightShift=rshift value Constant))")
+    public static class RotateLeftConstant extends AMD64MatchGenerator {
+        LeftShiftNode lshift;
+        UnsignedRightShiftNode rshift;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            if ((lshift.getShiftAmountMask() & (lshift.y().asConstant().asInt() + rshift.y().asConstant().asInt())) == 0) {
+                return builder -> gen.getLIRGeneratorTool().emitRol(gen.operand(lshift.x()), gen.operand(lshift.y()));
+            }
+            return null;
+        }
+
+    }
+
+    @MatchRule("(Or (LeftShift=lshift value (IntegerSub Constant=delta shiftAmount)) (UnsignedRightShift value shiftAmount))")
+    public static class RotateRightVariable extends AMD64MatchGenerator {
+        ValueNode value;
+        ValueNode shiftAmount;
+        ConstantNode delta;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            if (delta.asConstant().asLong() == 0 || delta.asConstant().asLong() == 32) {
+                return builder -> gen.getLIRGeneratorTool().emitRor(gen.operand(value), gen.operand(shiftAmount));
+            }
+            return null;
+        }
+
+    }
+
+    @MatchRule("(Or (LeftShift value shiftAmount) (UnsignedRightShift value (IntegerSub Constant=delta shiftAmount)))")
+    public static class RotateLeftVariable extends AMD64MatchGenerator {
+        ValueNode value;
+        ValueNode shiftAmount;
+        ConstantNode delta;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            if (delta.asConstant().asLong() == 0 || delta.asConstant().asLong() == 32) {
+                return builder -> gen.getLIRGeneratorTool().emitRol(gen.operand(value), gen.operand(shiftAmount));
+            }
+            return null;
+        }
+    }
+
+    @MatchRule("(IntegerAdd value Read=access)")
+    @MatchRule("(IntegerSub value Read=access)")
+    @MatchRule("(IntegerMul value Read=access)")
+    @MatchRule("(FloatAdd value Read=access)")
+    @MatchRule("(FloatSub value Read=access)")
+    @MatchRule("(FloatMul value Read=access)")
+    @MatchRule("(Or value Read=access)")
+    @MatchRule("(Xor value Read=access)")
+    @MatchRule("(And value Read=access)")
+    @MatchRule("(IntegerAdd value FloatingRead=access)")
+    @MatchRule("(IntegerSub value FloatingRead=access)")
+    @MatchRule("(IntegerMul value FloatingRead=access)")
+    @MatchRule("(FloatAdd value FloatingRead=access)")
+    @MatchRule("(FloatSub value FloatingRead=access)")
+    @MatchRule("(FloatMul value FloatingRead=access)")
+    @MatchRule("(Or value FloatingRead=access)")
+    @MatchRule("(Xor value FloatingRead=access)")
+    @MatchRule("(And value FloatingRead=access)")
+    public static class BinaryRead extends AMD64MatchGenerator {
+        BinaryNode root;
+        Access access;
+        ValueNode value;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            AMD64Arithmetic op = gen.getOp(root, access);
+            if (op != null) {
+                return builder -> gen.getLIRGeneratorTool().emitBinaryMemory(op, gen.getMemoryKind(access), gen.getLIRGeneratorTool().asAllocatable(gen.operand(value)), gen.makeAddress(access),
+                                gen.getState(access));
+            }
+            return null;
+        }
+    }
+
+    @MatchRule("(Write Narrow=narrow value)")
+    public static class WriteNarrow extends AMD64MatchGenerator {
+        WriteNode root;
+        NarrowNode narrow;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            return new ComplexMatchResult() {
+                @Override
+                public Value evaluate(NodeLIRBuilder builder) {
+                    PlatformKind writeKind = gen.getLIRGeneratorTool().getPlatformKind(root.value().stamp());
+                    Value address = root.location().generateAddress(builder, gen.getLIRGeneratorTool(), gen.operand(root.object()));
+                    Value v = gen.operand(narrow.getInput());
+                    gen.getLIRGeneratorTool().emitStore(writeKind, address, v, gen.state(root));
+                    return null;
+                }
+            };
+        }
+    }
+
+    @MatchRule("(SignExtend Read=access)")
+    @MatchRule("(SignExtend FloatingRead=access)")
+    public static class SignExtend extends AMD64MatchGenerator {
+        Access access;
+        SignExtendNode root;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            return gen.emitSignExtendMemory(access, root.getInputBits(), root.getResultBits());
+        }
+    }
+
+    static abstract class AMD64MatchGenerator implements MatchGenerator {
+        public AMD64MatchGenerator() {
+        }
+
+        public ComplexMatchResult match(NodeLIRBuilder gen) {
+            return match((AMD64NodeLIRBuilder) gen);
+        }
+
+        abstract public ComplexMatchResult match(AMD64NodeLIRBuilder gen);
+    }
+
+    @MatchRule("(ZeroExtend Read=access)")
+    @MatchRule("(ZeroExtend FloatingRead=access)")
+    public static class ZeroExtend extends AMD64MatchGenerator {
+        Access access;
+        ZeroExtendNode root;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            Kind memoryKind = gen.getMemoryKind(access);
+            if (memoryKind.getBitCount() != root.getInputBits() && !memoryKind.isUnsigned()) {
+                /*
+                 * The memory being read from is signed and smaller than the result size so this is
+                 * a sign extension to inputBits followed by a zero extension to resultBits which
+                 * can't be expressed in a memory operation.
+                 */
+                return null;
+            }
+            return new ComplexMatchResult() {
+                public Value evaluate(NodeLIRBuilder unused) {
+                    return gen.getLIRGeneratorTool().emitZeroExtendMemory(memoryKind == Kind.Short ? Kind.Char : memoryKind, root.getResultBits(), gen.makeAddress(access), gen.getState(access));
+                }
+            };
+        }
+    }
+
+    @MatchRule("(FloatConvert Read=access)")
+    @MatchRule("(FloatConvert FloatingRead=access)")
+    public static class FloatConvert extends AMD64MatchGenerator {
+        Access access;
+        FloatConvertNode root;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            return new ComplexMatchResult() {
+                public Value evaluate(NodeLIRBuilder builder) {
+                    return gen.emitFloatConvertMemory(root, access);
+                }
+            };
+        }
+    }
+
+    @MatchRule("(Reinterpret Read=access)")
+    @MatchRule("(Reinterpret FloatingRead=access)")
+    public static class Reinterpret extends AMD64MatchGenerator {
+        Access access;
+        ReinterpretNode root;
+
+        @Override
+        public ComplexMatchResult match(AMD64NodeLIRBuilder gen) {
+            return new ComplexMatchResult() {
+                public Value evaluate(NodeLIRBuilder builder) {
+                    PlatformKind kind = gen.getLIRGeneratorTool().getPlatformKind(root.stamp());
+                    return gen.emitReinterpretMemory(kind, access);
+                }
+            };
+        }
+    }
+
     @Override
     public void visitBreakpointNode(BreakpointNode node) {
         JavaType[] sig = new JavaType[node.arguments().size()];
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java	Thu Apr 24 23:32:30 2014 +0100
@@ -272,7 +272,9 @@
     @Option(help = "")
     public static final OptionValue<Boolean> OptPushThroughPi = new OptionValue<>(true);
     @Option(help = "Allow backend to emit arithmetic and compares directly against memory.")
-    public static final OptionValue<Boolean> OptFoldMemory = new OptionValue<>(true);
+    public static final OptionValue<Boolean> OptFoldMemory = new OptionValue<>(false);
+    @Option(help = "Allow backend to match complex expressions.")
+    public static final OptionValue<Boolean> MatchExpressions = new OptionValue<>(true);
 
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/META-INF/services/javax.annotation.processing.Processor	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,1 @@
+com.oracle.graal.compiler.match.MatchProcessor
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Thu Apr 24 23:32:30 2014 +0100
@@ -23,6 +23,7 @@
 package com.oracle.graal.compiler.gen;
 
 import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.compiler.common.GraalOptions.*;
 import static com.oracle.graal.lir.LIR.*;
 import static com.oracle.graal.nodes.ConstantNode.*;
 
@@ -35,6 +36,7 @@
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.common.cfg.*;
 import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.compiler.match.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.Debug.Scope;
 import com.oracle.graal.graph.*;
@@ -63,10 +65,13 @@
     private ValueNode currentInstruction;
     private ValueNode lastInstructionPrinted; // Debugging only
 
+    private Map<Class<? extends ValueNode>, List<MatchStatement>> matchRules;
+
     public NodeLIRBuilder(StructuredGraph graph, LIRGeneratorTool gen) {
         this.gen = gen;
         this.nodeOperands = graph.createNodeMap();
         this.debugInfoBuilder = createDebugInfoBuilder(nodeOperands);
+        matchRules = MatchRuleRegistry.lookup(getClass());
     }
 
     @SuppressWarnings("hiding")
@@ -153,13 +158,25 @@
     @Override
     public Value setResult(ValueNode x, Value operand) {
         assert (!isRegister(operand) || !gen.attributes(asRegister(operand)).isAllocatable());
-        assert nodeOperands == null || nodeOperands.get(x) == null : "operand cannot be set twice";
+        assert nodeOperands != null && (nodeOperands.get(x) == null || nodeOperands.get(x) instanceof ComplexMatchValue) : "operand cannot be set twice";
         assert operand != null && isLegal(operand) : "operand must be legal";
         assert !(x instanceof VirtualObjectNode);
         nodeOperands.set(x, operand);
         return operand;
     }
 
+    /**
+     * Used by the {@link MatchStatement} machinery to override the generation LIR for some
+     * ValueNodes.
+     */
+    public void setMatchResult(ValueNode x, Value operand) {
+        assert operand.equals(Value.INTERIOR_MATCH) || operand instanceof ComplexMatchValue;
+        assert operand instanceof ComplexMatchValue || x.usages().count() == 1 : "interior matches must be single user";
+        assert nodeOperands != null && nodeOperands.get(x) == null : "operand cannot be set twice";
+        assert !(x instanceof VirtualObjectNode);
+        nodeOperands.set(x, operand);
+    }
+
     public LabelRef getLIRBlock(FixedNode b) {
         assert gen.getResult().getLIR().getControlFlowGraph() instanceof ControlFlowGraph;
         Block result = ((ControlFlowGraph) gen.getResult().getLIR().getControlFlowGraph()).blockFor(b);
@@ -192,6 +209,13 @@
         }
 
         List<ScheduledNode> nodes = blockMap.get(block);
+
+        if (MatchExpressions.getValue()) {
+            // Allow NodeLIRBuilder subclass to specialize code generation of any interesting groups
+            // of instructions
+            matchComplexExpressions(nodes);
+        }
+
         int instructionsFolded = 0;
         for (int i = 0; i < nodes.size(); i++) {
             Node instr = nodes.get(i);
@@ -207,7 +231,8 @@
 
             } else if (instr instanceof ValueNode) {
                 ValueNode valueNode = (ValueNode) instr;
-                if (!hasOperand(valueNode)) {
+                Value operand = getOperand(valueNode);
+                if (operand == null) {
                     if (!peephole(valueNode)) {
                         instructionsFolded = maybeFoldMemory(nodes, i, valueNode);
                         if (instructionsFolded == 0) {
@@ -220,6 +245,16 @@
                             }
                         }
                     }
+                } else if (Value.INTERIOR_MATCH.equals(operand)) {
+                    // Doesn't need to be evaluated
+                    Debug.log("interior match for %s", valueNode);
+                } else if (operand instanceof ComplexMatchValue) {
+                    Debug.log("complex match for %s", valueNode);
+                    ComplexMatchValue match = (ComplexMatchValue) operand;
+                    operand = match.evaluate(this);
+                    if (operand != null) {
+                        setResult(valueNode, operand);
+                    }
                 } else {
                     // There can be cases in which the result of an instruction is already set
                     // before by other instructions.
@@ -244,6 +279,31 @@
         gen.doBlockEnd(block);
     }
 
+    protected void matchComplexExpressions(List<ScheduledNode> nodes) {
+        if (matchRules != null) {
+            try (Scope s = Debug.scope("MatchComplexExpressions")) {
+                // Match the nodes in backwards order to encourage longer matches.
+                for (int index = nodes.size() - 1; index >= 0; index--) {
+                    ScheduledNode snode = nodes.get(index);
+                    if (!(snode instanceof ValueNode)) {
+                        continue;
+                    }
+                    ValueNode node = (ValueNode) snode;
+                    // See if this node is the root of any MatchStatements
+                    List<MatchStatement> statements = matchRules.get(node.getClass());
+                    if (statements != null) {
+                        for (MatchStatement statement : statements) {
+                            if (statement.generate(this, node, nodes)) {
+                                // Found a match so skip to the next
+                                break;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     private static final DebugMetric MemoryFoldSuccess = Debug.metric("MemoryFoldSuccess");
     private static final DebugMetric MemoryFoldFailed = Debug.metric("MemoryFoldFailed");
     private static final DebugMetric MemoryFoldFailedNonAdjacent = Debug.metric("MemoryFoldedFailedNonAdjacent");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/ComplexMatchResult.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,35 @@
+/*
+ * 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.compiler.match;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.gen.*;
+
+/*
+ * A closure that can be evaluated to produce the LIR for some complex match. Using a closure
+ * allows normal evaluation in NodeLIRBuilder for all the simple nodes with the complex nodes
+ * evaluated at the proper time.
+ */
+public interface ComplexMatchResult {
+    Value evaluate(NodeLIRBuilder gen);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/ComplexMatchValue.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,49 @@
+/*
+ * 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.compiler.match;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.gen.*;
+
+/**
+ * A wrapper value for the lazy evaluation of a complex match. There's an intermediate class for the
+ * closure because Value is serializable which is a hassle for the little inner classes which
+ * usually occur here.
+ */
+public class ComplexMatchValue extends Value {
+    /**
+     *
+     */
+    private static final long serialVersionUID = -4734670273590368770L;
+
+    final ComplexMatchResult result;
+
+    public ComplexMatchValue(ComplexMatchResult result) {
+        super(Kind.Illegal);
+        this.result = result;
+    }
+
+    public Value evaluate(NodeLIRBuilder builder) {
+        return result.evaluate(builder);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/GraalMatchableNodes.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,131 @@
+/*
+ * 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.compiler.match;
+
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.extended.*;
+
+/**
+ * Helper class to describe the matchable nodes in the core Graal IR. These could possibly live in
+ * their respective classes but for simplicity in the {@link MatchProcessor} they are grouped here.
+ */
+@MatchableNode(shortName = "Constant", value = ConstantNode.class, inputs = 0)
+@MatchableNode(shortName = "FloatConvert", value = FloatConvertNode.class, inputs = 1, adapter = GraalMatchableNodes.ConvertNodeAdapter.class)
+@MatchableNode(shortName = "FloatSub", value = FloatSubNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class)
+@MatchableNode(shortName = "FloatingRead", value = FloatingReadNode.class, inputs = 1, adapter = GraalMatchableNodes.ReadNodeAdapter.class)
+@MatchableNode(shortName = "If", value = IfNode.class, inputs = 1, adapter = GraalMatchableNodes.IfNodeAdapter.class)
+@MatchableNode(shortName = "IntegerSub", value = IntegerSubNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class)
+@MatchableNode(shortName = "LeftShift", value = LeftShiftNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class)
+@MatchableNode(shortName = "Narrow", value = NarrowNode.class, inputs = 1, adapter = GraalMatchableNodes.ConvertNodeAdapter.class)
+@MatchableNode(shortName = "Read", value = ReadNode.class, inputs = 1, adapter = GraalMatchableNodes.ReadNodeAdapter.class)
+@MatchableNode(shortName = "Reinterpret", value = ReinterpretNode.class, inputs = 1, adapter = GraalMatchableNodes.ReinterpretNodeAdapter.class)
+@MatchableNode(shortName = "SignExtend", value = SignExtendNode.class, inputs = 1, adapter = GraalMatchableNodes.ConvertNodeAdapter.class)
+@MatchableNode(shortName = "UnsignedRightShift", value = UnsignedRightShiftNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class)
+@MatchableNode(shortName = "Write", value = WriteNode.class, inputs = 2, adapter = GraalMatchableNodes.WriteNodeAdapter.class)
+@MatchableNode(shortName = "ZeroExtend", value = ZeroExtendNode.class, inputs = 1, adapter = GraalMatchableNodes.ConvertNodeAdapter.class)
+@MatchableNode(shortName = "And", value = AndNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "FloatAdd", value = FloatAddNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "FloatEquals", value = FloatEqualsNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryOpLogicNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "FloatLessThan", value = FloatLessThanNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryOpLogicNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "FloatMul", value = FloatMulNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "IntegerAdd", value = IntegerAddNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "IntegerBelowThan", value = IntegerBelowThanNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryOpLogicNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "IntegerEquals", value = IntegerEqualsNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryOpLogicNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "IntegerLessThan", value = IntegerLessThanNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryOpLogicNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "IntegerMul", value = IntegerMulNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "IntegerTest", value = IntegerTestNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryOpLogicNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "ObjectEquals", value = ObjectEqualsNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryOpLogicNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "Or", value = OrNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class, commutative = true)
+@MatchableNode(shortName = "Xor", value = XorNode.class, inputs = 2, adapter = GraalMatchableNodes.BinaryNodeAdapter.class, commutative = true)
+public class GraalMatchableNodes {
+    public static class BinaryNodeAdapter extends MatchNodeAdapter {
+        @Override
+        protected ValueNode getFirstInput(ValueNode node) {
+            return ((BinaryNode) node).x();
+        }
+
+        @Override
+        protected ValueNode getSecondInput(ValueNode node) {
+            return ((BinaryNode) node).y();
+        }
+    }
+
+    public static class WriteNodeAdapter extends MatchNodeAdapter {
+
+        @Override
+        protected ValueNode getFirstInput(ValueNode node) {
+            return ((WriteNode) node).object();
+        }
+
+        @Override
+        protected ValueNode getSecondInput(ValueNode node) {
+            return ((WriteNode) node).value();
+        }
+    }
+
+    public static class ConvertNodeAdapter extends MatchNodeAdapter {
+
+        @Override
+        protected ValueNode getFirstInput(ValueNode node) {
+            return ((ConvertNode) node).getInput();
+        }
+    }
+
+    public static class ReinterpretNodeAdapter extends MatchNodeAdapter {
+
+        @Override
+        protected ValueNode getFirstInput(ValueNode node) {
+            return ((ReinterpretNode) node).value();
+        }
+    }
+
+    public static class IfNodeAdapter extends MatchNodeAdapter {
+
+        @Override
+        protected ValueNode getFirstInput(ValueNode node) {
+            return ((IfNode) node).condition();
+        }
+    }
+
+    public static class ReadNodeAdapter extends MatchNodeAdapter {
+
+        @Override
+        protected ValueNode getFirstInput(ValueNode node) {
+            return ((Access) node).object();
+        }
+    }
+
+    public static class BinaryOpLogicNodeAdapter extends MatchNodeAdapter {
+
+        @Override
+        protected ValueNode getFirstInput(ValueNode node) {
+            return ((BinaryOpLogicNode) node).x();
+        }
+
+        @Override
+        protected ValueNode getSecondInput(ValueNode node) {
+            return ((BinaryOpLogicNode) node).y();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchContext.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,191 @@
+/*
+ * 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.compiler.match;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.gen.*;
+import com.oracle.graal.compiler.match.MatchPattern.Result;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.virtual.*;
+
+/**
+ * Container for state captured during a match.
+ */
+public class MatchContext {
+    private final ArrayList<ValueNode> consumed = new ArrayList<>();
+    private final List<ScheduledNode> nodes;
+    private final ValueNode root;
+    private List<String> names;
+    private List<Class<? extends ValueNode>> types;
+    private List<ValueNode> values;
+    private final MatchStatement rule;
+    private int startIndex;
+    private int endIndex;
+    private final NodeLIRBuilder builder;
+
+    public MatchContext(NodeLIRBuilder builder, MatchStatement rule, ValueNode node, List<ScheduledNode> nodes) {
+        this.builder = builder;
+        this.rule = rule;
+        this.root = node;
+        this.nodes = nodes;
+        // The root should be the last index since all the inputs must be scheduled before.
+        startIndex = endIndex = nodes.indexOf(node);
+    }
+
+    public ValueNode getRoot() {
+        return root;
+    }
+
+    public Result captureNamedValue(String name, Class<? extends ValueNode> type, ValueNode value) {
+        if (names == null) {
+            names = new ArrayList<>(2);
+            values = new ArrayList<>(2);
+            types = new ArrayList<>(2);
+        }
+        int index = names.indexOf(name);
+        if (index == -1) {
+            names.add(name);
+            values.add(value);
+            types.add(type);
+            return Result.OK;
+        } else {
+            if (values.get(index) != value) {
+                return Result.NAMED_VALUE_MISMATCH(value, rule.getPattern());
+            }
+            return Result.OK;
+        }
+    }
+
+    public Result validate() {
+        // Ensure that there's no unsafe work in between these operations.
+        for (int i = startIndex; i <= endIndex; i++) {
+            ScheduledNode node = getNodes().get(i);
+            if (node instanceof ConstantNode || node instanceof LocationNode || node instanceof VirtualObjectNode || node instanceof ParameterNode) {
+                // these can be evaluated lazily so don't worry about them. This should probably be
+                // captured by some interface that indicates that their generate method is empty.
+                continue;
+            } else if (!consumed.contains(node) && node != root) {
+                // This is too verbose for normal logging.
+                // Debug.log("unexpected node %s", node);
+                // for (int j = startIndex; j <= endIndex; j++) {
+                // ScheduledNode theNode = getNodes().get(j);
+                // Debug.log("%s(%s) %1s", (consumed.contains(theNode) || theNode == root) ? "*" :
+                // " ",
+                // theNode.usages().count(), theNode);
+                // }
+                return Result.NOT_SAFE(node, rule.getPattern());
+            }
+        }
+        return Result.OK;
+    }
+
+    /**
+     * Transfers the captured value into the MatchGenerator instance. The reflective information
+     * should really be generated and checking during construction of the MatchStatement but this is
+     * ok for now.
+     */
+    public void transferState(MatchGenerator generator) {
+        try {
+            for (int i = 0; i < names.size(); i++) {
+                String name = names.get(i);
+                try {
+                    Field field = generator.getClass().getDeclaredField(name);
+                    field.setAccessible(true);
+                    field.set(generator, values.get(i));
+                } catch (NoSuchFieldException e) {
+                    // Doesn't exist so the generator doesn't care about the value.
+                }
+            }
+        } catch (SecurityException | IllegalArgumentException | IllegalAccessException e) {
+            throw new GraalInternalError(e);
+        }
+        try {
+            Field field = generator.getClass().getDeclaredField("root");
+            field.setAccessible(true);
+            field.set(generator, getRoot());
+        } catch (NoSuchFieldException e) {
+            // Doesn't exist
+        } catch (SecurityException | IllegalAccessException | IllegalArgumentException e) {
+            throw new GraalInternalError(e);
+        }
+    }
+
+    public void setResult(ComplexMatchResult result) {
+        setResult(new ComplexMatchValue(result));
+    }
+
+    /**
+     * Mark the interior nodes with INTERIOR_MATCH and set the Value of the root to be the result.
+     * During final LIR generation it will be evaluated to produce the actual LIR value.
+     *
+     * @param result
+     */
+    public void setResult(ComplexMatchValue result) {
+        Debug.log("matched %s %s", rule.getName(), rule.getPattern());
+        // Debug.log("%s", rule.formatMatch(root));
+        for (ValueNode node : consumed) {
+            // All the interior nodes should be skipped during the normal doRoot calls in
+            // NodeLIRBuilder so mark them as interior matches. The root of the match will get a
+            // closure which will be evaluated to produce the final LIR.
+            getBuilder().setMatchResult(node, Value.INTERIOR_MATCH);
+        }
+        getBuilder().setMatchResult(root, result);
+    }
+
+    /**
+     * Mark a node as consumed by the match. Consumed nodes will never be evaluated.
+     *
+     * @return Result.OK if the node can be safely consumed.
+     */
+    public Result consume(ValueNode node) {
+        if (node.usages().count() != 1) {
+            return Result.TOO_MANY_USERS(node, rule.getPattern());
+        }
+
+        if (getBuilder().hasOperand(node)) {
+            return Result.ALREADY_USED(node, rule.getPattern());
+        }
+
+        int index = getNodes().indexOf(node);
+        if (index == -1) {
+            return Result.NOT_IN_BLOCK(node, rule.getPattern());
+        }
+        startIndex = Math.min(startIndex, index);
+        consumed.add(node);
+        return Result.OK;
+    }
+
+    private NodeLIRBuilder getBuilder() {
+        return builder;
+    }
+
+    private List<ScheduledNode> getNodes() {
+        return nodes;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchGenerator.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,35 @@
+/*
+ * 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.compiler.match;
+
+import com.oracle.graal.compiler.gen.*;
+
+/**
+ * Code generator for complex match patterns.
+ *
+ * @returns null if the match can't be generated or a {@link ComplexMatchResult} that can be
+ *          evaluated during LIR generation to produce the final LIR value.
+ */
+public interface MatchGenerator {
+    ComplexMatchResult match(NodeLIRBuilder gen);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchNodeAdapter.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,42 @@
+/*
+ * 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.compiler.match;
+
+import com.oracle.graal.nodes.*;
+
+/**
+ * Helper class to visit the matchable inputs of a node in a specified order. This may not be needed
+ * in the end since this could probably be done using the inputs iterator but it simplifies things
+ * for the moment.
+ */
+public class MatchNodeAdapter {
+    @SuppressWarnings("unused")
+    protected ValueNode getFirstInput(ValueNode node) {
+        throw new InternalError();
+    }
+
+    @SuppressWarnings("unused")
+    protected ValueNode getSecondInput(ValueNode node) {
+        throw new InternalError();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,201 @@
+/*
+ * 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.compiler.match;
+
+import com.oracle.graal.graph.Node.Verbosity;
+import com.oracle.graal.nodes.*;
+
+/**
+ * A simple recursive pattern matcher for a DAG of nodes.
+ */
+
+public class MatchPattern {
+
+    enum MatchResultCode {
+        OK,
+        WRONG_CLASS,
+        NAMED_VALUE_MISMATCH,
+        TOO_MANY_USERS,
+        NOT_IN_BLOCK,
+        NOT_SAFE,
+        ALREADY_USED,
+    }
+
+    /**
+     * A descriptive result for match failures. This can be helpful for debugging why a match
+     * doesn't work as expected.
+     */
+    static class Result {
+        final MatchResultCode code;
+        final ScheduledNode node;
+        final MatchPattern matcher;
+
+        Result(MatchResultCode result, ScheduledNode node, MatchPattern matcher) {
+            this.code = result;
+            this.node = node;
+            this.matcher = matcher;
+        }
+
+        static final Result OK = new Result(MatchResultCode.OK, null, null);
+
+        static Result WRONG_CLASS(ValueNode node, MatchPattern matcher) {
+            return new Result(MatchResultCode.WRONG_CLASS, node, matcher);
+        }
+
+        static Result NAMED_VALUE_MISMATCH(ValueNode node, MatchPattern matcher) {
+            return new Result(MatchResultCode.NAMED_VALUE_MISMATCH, node, matcher);
+        }
+
+        static Result TOO_MANY_USERS(ValueNode node, MatchPattern matcher) {
+            return new Result(MatchResultCode.TOO_MANY_USERS, node, matcher);
+        }
+
+        static Result NOT_IN_BLOCK(ScheduledNode node, MatchPattern matcher) {
+            return new Result(MatchResultCode.NOT_IN_BLOCK, node, matcher);
+        }
+
+        static Result NOT_SAFE(ScheduledNode node, MatchPattern matcher) {
+            return new Result(MatchResultCode.NOT_SAFE, node, matcher);
+        }
+
+        static Result ALREADY_USED(ValueNode node, MatchPattern matcher) {
+            return new Result(MatchResultCode.ALREADY_USED, node, matcher);
+        }
+
+        @Override
+        public String toString() {
+            if (code == MatchResultCode.OK) {
+                return "OK";
+            }
+            return code + " " + node.toString(Verbosity.Id) + "|" + node.getClass().getSimpleName() + " " + matcher;
+        }
+    }
+
+    /**
+     * The expected type of the node. It must match exactly.
+     */
+    private final Class<? extends ValueNode> nodeClass;
+    /**
+     * An optional name for this node. A name can occur multiple times in a match and that name must
+     * always refer to the same node of the match will fail.
+     */
+    private final String name;
+    /**
+     * An optional pattern for the first input.
+     */
+    private final MatchPattern first;
+    /**
+     * An optional pattern for the second input.
+     */
+    private final MatchPattern second;
+    /**
+     * Helper class to visit the inputs.
+     */
+    private final MatchNodeAdapter adapter;
+    /**
+     * Can there only be one user of the node. Constant nodes can be matched even if there are other
+     * users.
+     */
+    private final boolean singleUser;
+
+    public MatchPattern(String name, boolean singleUser) {
+        this(null, name, null, null, null, singleUser);
+    }
+
+    public MatchPattern(Class<? extends ValueNode> nodeClass, String name, boolean singleUser) {
+        this(nodeClass, name, null, null, null, singleUser);
+    }
+
+    public MatchPattern(Class<? extends ValueNode> nodeClass, String name, MatchPattern first, MatchNodeAdapter adapter, boolean singleUser) {
+        this(nodeClass, name, first, null, adapter, singleUser);
+    }
+
+    public MatchPattern(Class<? extends ValueNode> nodeClass, String name, MatchPattern first, MatchPattern second, MatchNodeAdapter adapter, boolean singleUser) {
+        this.nodeClass = nodeClass;
+        this.name = name;
+        this.singleUser = singleUser;
+        this.first = first;
+        this.second = second;
+        this.adapter = adapter;
+    }
+
+    Class<? extends ValueNode> nodeClass() {
+        return nodeClass;
+    }
+
+    Result match(ValueNode node, MatchContext context) {
+        return matchTree(node, context, true);
+    }
+
+    private Result matchTree(ValueNode node, MatchContext context, boolean atRoot) {
+        Result result = Result.OK;
+        if (nodeClass != null && node.getClass() != nodeClass) {
+            return Result.WRONG_CLASS(node, this);
+        }
+        if (singleUser && !atRoot) {
+            result = context.consume(node);
+            if (result != Result.OK) {
+                return result;
+            }
+        }
+
+        if (name != null) {
+            result = context.captureNamedValue(name, nodeClass, node);
+        }
+
+        if (first != null) {
+            result = first.matchTree(adapter.getFirstInput(node), context, false);
+            if (result == Result.OK && second != null) {
+                result = second.matchTree(adapter.getSecondInput(node), context, false);
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * For a node starting at root, produce a String showing the inputs that matched against this
+     * rule. It's assumed that a match has already succeeded against this rule, otherwise the
+     * printing may produce exceptions.
+     */
+    public String formatMatch(ValueNode root) {
+        String result = String.format("%s", root);
+        if (first == null && second == null) {
+            return result;
+        } else {
+            return "(" + result + (first != null ? " " + first.formatMatch(adapter.getFirstInput(root)) : "") + (second != null ? " " + second.formatMatch(adapter.getSecondInput(root)) : "") + ")";
+        }
+    }
+
+    @Override
+    public String toString() {
+        if (nodeClass == null) {
+            return name;
+        } else {
+            String pre = first != null || second != null ? "(" : "";
+            String post = first != null || second != null ? ")" : "";
+            String nodeName = nodeClass.getSimpleName();
+            return pre + nodeName + (name != null ? "=\"" + name + "\"" : "") + (first != null ? (" " + first.toString()) : "") + (second != null ? (" " + second.toString()) : "") + post;
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,574 @@
+/*
+ * 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.compiler.match;
+
+import java.io.*;
+import java.util.*;
+import java.util.regex.*;
+
+import javax.annotation.processing.*;
+import javax.lang.model.*;
+import javax.lang.model.element.*;
+import javax.lang.model.type.*;
+import javax.tools.Diagnostic.Kind;
+import javax.tools.*;
+
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.gen.*;
+import com.oracle.graal.nodes.*;
+
+/**
+ * Processes classes annotated with {@link MatchRule}. A {@link MatchStatementSet} service is
+ * generated for each top level class containing at least one such field. These service objects can
+ * be retrieved as follows:
+ *
+ * <pre>
+ *     ServiceLoader<MatchStatementSet> sl = ServiceLoader.loadInstalled(MatchStatementSet.class);
+ *     for (MatchStatementSet rules : sl) {
+ *         ...
+ *     }
+ * </pre>
+ */
+@SupportedAnnotationTypes({"com.oracle.graal.compiler.match.MatchRule", "com.oracle.graal.compiler.match.MatchRules", "com.oracle.graal.compiler.match.MatchableNode"})
+public class MatchProcessor extends AbstractProcessor {
+
+    @Override
+    public SourceVersion getSupportedSourceVersion() {
+        return SourceVersion.latest();
+    }
+
+    private final Set<Element> processedMatchRule = new HashSet<>();
+    private final Set<Element> processedMatchableNode = new HashSet<>();
+
+    private static class RuleParseError extends RuntimeException {
+        private static final long serialVersionUID = 6456128283609257490L;
+
+        RuleParseError(String message) {
+            super(message);
+        }
+    }
+
+    private class RuleParser {
+        final String[] tokens;
+        int current;
+
+        RuleParser(String rule) {
+            Matcher m = tokenizer.matcher(rule);
+            List<String> list = new ArrayList<>();
+            int end = 0;
+            while (m.lookingAt()) {
+                list.add(m.group(1));
+                end = m.end();
+                m.region(m.end(), m.regionEnd());
+            }
+            if (end != m.regionEnd()) {
+                throw new RuleParseError("unexpected tokens :" + rule.substring(m.end(), m.regionEnd()));
+            }
+            tokens = list.toArray(new String[0]);
+        }
+
+        String next() {
+            return tokens[current++];
+        }
+
+        String peek() {
+            return tokens[current];
+        }
+
+        boolean done() {
+            return current == tokens.length;
+        }
+
+        private MatchDescriptor parseSexp() {
+            if (peek().equals("(")) {
+                next();
+                MatchDescriptor descriptor = parseType(true);
+                for (int n = 0; n < descriptor.nodeType.inputs; n++) {
+                    if (peek().equals("(")) {
+                        descriptor.inputs[n] = parseSexp();
+                    } else {
+                        descriptor.inputs[n] = parseType(false);
+                    }
+                }
+                for (int n = 0; n < descriptor.nodeType.inputs; n++) {
+                    if (descriptor.inputs[n] == null) {
+                        throw new RuleParseError("not enough inputs for " + descriptor.name);
+                    }
+                }
+                if (peek().equals(")")) {
+                    next();
+                    return descriptor;
+                }
+            }
+            throw new RuleParseError("didn't swallow sexp at: " + peek());
+        }
+
+        private MatchDescriptor parseType(boolean sexp) {
+            TypeDescriptor type = null;
+            String name = null;
+            if (Character.isUpperCase(peek().charAt(0))) {
+                String token = next();
+                type = types.get(token);
+                if (type == null) {
+                    throw new RuleParseError("unknown node type: " + token);
+                }
+                if (peek().equals("=")) {
+                    next();
+                    name = next();
+                }
+            } else {
+                name = next();
+                type = null;
+            }
+            return new MatchDescriptor(type, name, sexp);
+        }
+
+        ArrayList<String> generateVariants() {
+            MatchDescriptor descriptor = parseSexp();
+            if (!done()) {
+                throw new RuleParseError("didn't consume all tokens");
+            }
+            return descriptor.generateVariants();
+        }
+    }
+
+    static Pattern tokenizer = Pattern.compile("\\s*([()=]|[A-Za-z][A-Za-z0-9]*)\\s*");
+
+    static class TypeDescriptor {
+        /**
+         * The name uses in match expressions to refer to this type.
+         */
+        final String shortName;
+        /**
+         * The {@link ValueNode} class represented by this type.
+         */
+        final String nodeClass;
+
+        /**
+         * The {@link ValueNode} class represented by this type.
+         */
+        final String nodePackage;
+
+        /**
+         * Expected number of matchable inputs. Should be less <= 2 at the moment.
+         */
+        final int inputs;
+
+        /**
+         * An adapter class to read the proper matchable inputs of the class.
+         */
+        final String adapter;
+
+        /**
+         * Should swapped variants of this match be generated. The user of the match is expected to
+         * compensate for any ordering differences in compare which are commutative but require
+         * reinterpreting the condition in that case.
+         */
+        final boolean commutative;
+
+        /**
+         * Can multiple users of this node subsume it. Constants can be swallowed into a match even
+         * if there are multiple users.
+         */
+        final boolean cloneable;
+
+        TypeDescriptor(String shortName, String nodeClass, String nodePackage, int inputs, String adapter, boolean commutative) {
+            this.shortName = shortName;
+            this.nodeClass = nodeClass;
+            this.nodePackage = nodePackage;
+            this.inputs = inputs;
+            this.adapter = adapter;
+            this.commutative = commutative;
+            this.cloneable = (nodePackage + "." + nodeClass).equals(ConstantNode.class.getName());
+            assert !commutative || inputs == 2;
+        }
+    }
+
+    HashMap<String, TypeDescriptor> types = new HashMap<>();
+    ArrayList<String> packages = new ArrayList<>();
+
+    private void declareType(String shortName, String nodeClass, String nodePackage, int inputs, String adapter, boolean commutative) {
+        TypeDescriptor descriptor = new TypeDescriptor(shortName, nodeClass, nodePackage, inputs, adapter, commutative);
+        types.put(shortName, descriptor);
+        if (!packages.contains(descriptor.nodePackage)) {
+            packages.add(descriptor.nodePackage);
+        }
+    }
+
+    private static String findPackage(Element type) {
+        Element enclosing = type.getEnclosingElement();
+        while (enclosing != null && enclosing.getKind() != ElementKind.PACKAGE) {
+            enclosing = enclosing.getEnclosingElement();
+        }
+        if (enclosing != null && enclosing.getKind() == ElementKind.PACKAGE) {
+            return ((PackageElement) enclosing).getQualifiedName().toString();
+        }
+        throw new GraalInternalError("can't find package for %s", type);
+    }
+
+    static class MatchDescriptor {
+        TypeDescriptor nodeType;
+        String name;
+        MatchDescriptor[] inputs;
+
+        MatchDescriptor(TypeDescriptor nodeType, String name, boolean sexp) {
+            this.nodeType = nodeType;
+            this.name = name;
+            if (sexp) {
+                this.inputs = new MatchDescriptor[nodeType.inputs];
+            } else {
+                this.inputs = new MatchDescriptor[0];
+            }
+        }
+
+        ArrayList<String> generateVariants() {
+            String prefix = formatPrefix();
+            String suffix = formatSuffix();
+            ArrayList<String> variants = new ArrayList<>();
+            if (inputs.length == 2) {
+                // Generate this version and a swapped version
+                for (String first : inputs[0].generateVariants()) {
+                    for (String second : inputs[1].generateVariants()) {
+                        variants.add(prefix + ", " + first + ", " + second + suffix);
+                        if (nodeType.commutative) {
+                            variants.add(prefix + ", " + second + ", " + first + suffix);
+                        }
+                    }
+                }
+            } else if (inputs.length == 1) {
+                for (String first : inputs[0].generateVariants()) {
+                    variants.add(prefix + ", " + first + suffix);
+                }
+            } else {
+                variants.add(prefix + suffix);
+            }
+            return variants;
+        }
+
+        private String formatPrefix() {
+            if (nodeType == null) {
+                return String.format("new MatchPattern(%s, false", name != null ? ("\"" + name + "\"") : "null");
+            } else {
+                return String.format("new MatchPattern(%s.class, %s", nodeType.nodeClass, name != null ? ("\"" + name + "\"") : "null");
+            }
+        }
+
+        private String formatSuffix() {
+            if (nodeType != null) {
+                if (inputs.length != nodeType.inputs) {
+                    return ", true)";
+                } else {
+                    if (nodeType.adapter != null) {
+                        return ", " + nodeType.adapter + "," + !nodeType.cloneable + ")";
+                    }
+                    if (nodeType.cloneable) {
+                        return ", false)";
+                    }
+                }
+            }
+            return ")";
+        }
+
+    }
+
+    private void createFiles(MatchRuleDescriptor info) {
+        String pkg = ((PackageElement) info.topDeclaringType.getEnclosingElement()).getQualifiedName().toString();
+        Name topDeclaringClass = info.topDeclaringType.getSimpleName();
+
+        String optionsClassName = topDeclaringClass + "_" + MatchStatementSet.class.getSimpleName();
+        Element[] originatingElements = info.originatingElements.toArray(new Element[info.originatingElements.size()]);
+
+        Filer filer = processingEnv.getFiler();
+        try (PrintWriter out = createSourceFile(pkg, optionsClassName, filer, originatingElements)) {
+
+            out.println("// CheckStyle: stop header check");
+            out.println("// GENERATED CONTENT - DO NOT EDIT");
+            out.println("// Source: " + topDeclaringClass + ".java");
+            out.println("package " + pkg + ";");
+            out.println("");
+            out.println("import java.util.*;");
+            out.println("import " + MatchStatementSet.class.getPackage().getName() + ".*;");
+            out.println("import " + NodeLIRBuilder.class.getName() + ";");
+            for (String p : packages) {
+                out.println("import " + p + ".*;");
+            }
+            out.println("");
+            out.println("public class " + optionsClassName + " implements " + MatchStatementSet.class.getSimpleName() + " {");
+            String desc = MatchStatement.class.getSimpleName();
+            out.println("    // CheckStyle: stop line length check");
+            out.println("    private static final List<" + desc + "> options = Collections.unmodifiableList(Arrays.asList(");
+
+            int i = 0;
+            for (MatchRuleItem option : info.options) {
+                String optionValue;
+                if (option.field.getModifiers().contains(Modifier.PRIVATE)) {
+                    optionValue = "field(" + option.declaringClass + ".class, \"" + option.field.getSimpleName() + "\")";
+                } else {
+                    optionValue = option.declaringClass + "." + option.field.getSimpleName();
+                }
+                String name = option.name;
+                Name fieldName = option.field.getSimpleName();
+                String comma = i == info.options.size() - 1 ? "" : ",";
+                out.printf("        new MatchStatement(\"%s\", %s, %s.class)%s\n", fieldName, name, optionValue, comma);
+                i++;
+            }
+            out.println("    ));");
+            out.println("    // CheckStyle: resume line length check");
+            out.println();
+
+            out.println("    public Class<? extends NodeLIRBuilder> forClass() {");
+            out.println("        return " + topDeclaringClass + ".class;");
+            out.println("    }");
+            out.println();
+            out.println("    @Override");
+            out.println("    public List<" + desc + "> statements() {");
+            out.println("        return options;");
+            out.println("    }");
+            out.println("}");
+        }
+
+        try {
+            createProviderFile(pkg, optionsClassName, originatingElements);
+        } catch (IOException e) {
+            processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(), info.topDeclaringType);
+        }
+    }
+
+    private void createProviderFile(String pkg, String providerClassName, Element... originatingElements) throws IOException {
+        String filename = "META-INF/providers/" + pkg + "." + providerClassName;
+        FileObject file = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", filename, originatingElements);
+        PrintWriter writer = new PrintWriter(new OutputStreamWriter(file.openOutputStream(), "UTF-8"));
+        writer.println(MatchStatementSet.class.getName());
+        writer.close();
+    }
+
+    protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) {
+        try {
+            // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle
+            JavaFileObject sourceFile = filer.createSourceFile(pkg + "." + relativeName, originatingElements);
+            return new PrintWriter(sourceFile.openWriter()) {
+
+                @Override
+                public void println() {
+                    print("\n");
+                }
+            };
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    static class MatchRuleItem implements Comparable<MatchRuleItem> {
+
+        final String name;
+        final String declaringClass;
+        final TypeElement field;
+
+        public MatchRuleItem(String name, String declaringClass, TypeElement field) {
+            this.name = name;
+            this.declaringClass = declaringClass;
+            this.field = field;
+        }
+
+        @Override
+        public int compareTo(MatchRuleItem other) {
+            return name.compareTo(other.name);
+        }
+
+        @Override
+        public String toString() {
+            return declaringClass + "." + field;
+        }
+    }
+
+    static class MatchRuleDescriptor {
+
+        final TypeElement topDeclaringType;
+        final List<MatchRuleItem> options = new ArrayList<>();
+        final Set<Element> originatingElements = new HashSet<>();
+
+        public MatchRuleDescriptor(TypeElement topDeclaringType) {
+            this.topDeclaringType = topDeclaringType;
+        }
+    }
+
+    private static TypeElement topDeclaringType(Element element) {
+        Element enclosing = element.getEnclosingElement();
+        if (enclosing == null || enclosing.getKind() == ElementKind.PACKAGE) {
+            assert element.getKind() == ElementKind.CLASS || element.getKind() == ElementKind.INTERFACE;
+            return (TypeElement) element;
+        }
+        return topDeclaringType(enclosing);
+    }
+
+    @Override
+    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+        if (roundEnv.processingOver()) {
+            return true;
+        }
+
+        try {
+            // Import default definitions
+            processMatchableNode(processingEnv.getElementUtils().getTypeElement(GraalMatchableNodes.class.getName()));
+            for (Element element : roundEnv.getElementsAnnotatedWith(MatchableNodeImport.class)) {
+                // Import any other definitions required by this element
+                String[] imports = element.getAnnotation(MatchableNodeImport.class).value();
+                for (String m : imports) {
+                    TypeElement el = processingEnv.getElementUtils().getTypeElement(m);
+                    processMatchableNode(el);
+                }
+            }
+
+            // Process any local MatchableNode declarations
+            for (Element element : roundEnv.getElementsAnnotatedWith(MatchableNode.class)) {
+                processMatchableNode(element);
+            }
+
+            Map<TypeElement, MatchRuleDescriptor> map = new HashMap<>();
+            for (Element element : roundEnv.getElementsAnnotatedWith(MatchRule.class)) {
+                processMatchRule(map, element);
+            }
+            for (Element element : roundEnv.getElementsAnnotatedWith(MatchRules.class)) {
+                processMatchRule(map, element);
+            }
+
+            for (MatchRuleDescriptor info : map.values()) {
+                createFiles(info);
+            }
+
+        } catch (Throwable t) {
+            processingEnv.getMessager().printMessage(Kind.ERROR, "Exception throw during processing: " + t);
+        }
+
+        return true;
+    }
+
+    /**
+     * Build up the type table to be used during parsing of the MatchRule.
+     */
+    private void processMatchableNode(Element element) {
+        if (!processedMatchableNode.contains(element)) {
+            processedMatchableNode.add(element);
+            TypeElement topDeclaringType = topDeclaringType(element);
+            MatchableNode[] matchables = element.getAnnotationsByType(MatchableNode.class);
+            for (MatchableNode matchable : matchables) {
+                String nodeClass;
+                String nodePackage;
+                String shortName = matchable.shortName();
+                TypeMirror nodeClassMirror = null;
+                try {
+                    matchable.value();
+                } catch (MirroredTypeException e) {
+                    nodeClassMirror = e.getTypeMirror();
+                }
+                if (nodeClassMirror == null) {
+                    throw new GraalInternalError("Can't get mirror for node class %s", element);
+                }
+                if (nodeClassMirror.toString().equals(MatchableNode.class.getName())) {
+                    nodeClass = topDeclaringType.getQualifiedName().toString();
+                } else {
+                    nodeClass = nodeClassMirror.toString();
+                }
+                nodePackage = findPackage(processingEnv.getElementUtils().getTypeElement(nodeClass));
+                assert nodeClass.startsWith(nodePackage);
+                nodeClass = nodeClass.substring(nodePackage.length() + 1);
+
+                TypeMirror nodeAdapterMirror = null;
+                try {
+                    matchable.adapter();
+                } catch (MirroredTypeException e) {
+                    nodeAdapterMirror = e.getTypeMirror();
+                }
+                if (nodeAdapterMirror == null) {
+                    throw new GraalInternalError("Can't get mirror for adapter %s", element);
+                }
+                String nodeAdapter = null;
+                if (!nodeAdapterMirror.toString().equals(MatchableNode.class.getName())) {
+                    nodeAdapter = String.format("new %s()", nodeAdapterMirror.toString());
+                }
+
+                declareType(shortName, nodeClass, nodePackage, matchable.inputs(), nodeAdapter, matchable.commutative());
+            }
+        }
+    }
+
+    private void processMatchRule(Map<TypeElement, MatchRuleDescriptor> map, Element element) {
+        if (!processedMatchRule.contains(element)) {
+            processedMatchRule.add(element);
+            TypeElement topDeclaringType = topDeclaringType(element);
+            MatchRuleDescriptor options = map.get(topDeclaringType);
+            if (options == null) {
+                options = new MatchRuleDescriptor(topDeclaringType);
+                map.put(topDeclaringType, options);
+            }
+            MatchRule[] matchRules = element.getAnnotationsByType(MatchRule.class);
+            for (MatchRule matchRule : matchRules) {
+                processMatchRule(element, options, matchRule);
+            }
+        }
+    }
+
+    private void processMatchRule(Element element, MatchRuleDescriptor info, MatchRule matchRule) {
+        assert element instanceof TypeElement;
+        assert element.getKind() == ElementKind.CLASS;
+        TypeElement field = (TypeElement) element;
+
+        TypeMirror fieldType = field.asType();
+        if (fieldType.getKind() != TypeKind.DECLARED) {
+            processingEnv.getMessager().printMessage(Kind.ERROR, "Option field must be of type " + MatchRule.class.getName(), element);
+            return;
+        }
+
+        Element enclosing = element.getEnclosingElement();
+        String declaringClass = "";
+        String separator = "";
+        Set<Element> originatingElementsList = info.originatingElements;
+        originatingElementsList.add(field);
+        while (enclosing != null) {
+            if (enclosing.getKind() == ElementKind.CLASS || enclosing.getKind() == ElementKind.INTERFACE) {
+                if (enclosing.getModifiers().contains(Modifier.PRIVATE)) {
+                    String msg = String.format("Option field cannot be declared in a private %s %s", enclosing.getKind().name().toLowerCase(), enclosing);
+                    processingEnv.getMessager().printMessage(Kind.ERROR, msg, element);
+                    return;
+                }
+                originatingElementsList.add(enclosing);
+                declaringClass = enclosing.getSimpleName() + separator + declaringClass;
+                separator = ".";
+            } else {
+                assert enclosing.getKind() == ElementKind.PACKAGE;
+            }
+            enclosing = enclosing.getEnclosingElement();
+        }
+
+        String rule = matchRule.value();
+        try {
+            ArrayList<String> matches = new RuleParser(rule).generateVariants();
+            for (String match : matches) {
+                info.options.add(new MatchRuleItem(match, declaringClass, field));
+            }
+        } catch (RuleParseError e) {
+            processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage(), element);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRule.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,50 @@
+/*
+ * 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.compiler.match;
+
+import java.lang.annotation.*;
+
+import com.oracle.graal.nodes.*;
+
+/**
+ * This annotation declares a textual pattern for matching an HIR DAG. It's an s-expression with a
+ * node followed by its inputs. Node types are always uppercase and lowercase words are the names of
+ * nodes.
+ *
+ * <pre>
+ *   NAME := [a-z][a-zA-Z0-9]*
+ *   NODETYPE := [A-Z][a-zA-Z0-9]*
+ *   NODEORNAME :=  NODE [ = NAME ] | NAME
+ *   EXPRESSION := ( NODEORNAME [ EXPRESSION | NODEORNAME [ EXPRESSION | NODEORNAME ] )
+ * </pre>
+ *
+ * All matched nodes except the root of the match and {@link ConstantNode}s must have a single user.
+ * All matched nodes must be in the same block.
+ */
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Repeatable(value = MatchRules.class)
+public @interface MatchRule {
+    String value();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRuleRegistry.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,79 @@
+/*
+ * 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.compiler.match;
+
+import java.util.*;
+
+import com.oracle.graal.compiler.gen.*;
+import com.oracle.graal.nodes.*;
+
+public class MatchRuleRegistry {
+
+    private static final HashMap<Class<? extends NodeLIRBuilder>, Map<Class<? extends ValueNode>, List<MatchStatement>>> registry = new HashMap<>();
+
+    /**
+     * Collect all the {@link MatchStatement}s defined by the superclass chain of theClass.
+     *
+     * @param theClass
+     * @return the set of {@link MatchStatement}s applicable to theClass.
+     */
+    public synchronized static Map<Class<? extends ValueNode>, List<MatchStatement>> lookup(Class<? extends NodeLIRBuilder> theClass) {
+        Map<Class<? extends ValueNode>, List<MatchStatement>> result = registry.get(theClass);
+
+        if (result == null) {
+            HashMap<Class<? extends NodeLIRBuilder>, List<MatchStatement>> localRules = new HashMap<>();
+            ServiceLoader<MatchStatementSet> sl = ServiceLoader.loadInstalled(MatchStatementSet.class);
+            for (MatchStatementSet rules : sl) {
+                localRules.put(rules.forClass(), rules.statements());
+            }
+
+            // Walk the class hierarchy collecting lists and merge them together. The subclass
+            // rules are first which gives them preference over earlier rules.
+            Map<Class<? extends ValueNode>, List<MatchStatement>> rules = new HashMap<>();
+            Class<?> currentClass = theClass;
+            do {
+                List<MatchStatement> statements = localRules.get(currentClass);
+                if (statements != null) {
+                    for (MatchStatement statement : statements) {
+                        Class<? extends ValueNode> nodeClass = statement.getPattern().nodeClass();
+                        List<MatchStatement> current = rules.get(nodeClass);
+                        if (current == null) {
+                            current = new ArrayList<>();
+                            rules.put(nodeClass, current);
+                        }
+                        current.add(statement);
+                    }
+                }
+                currentClass = currentClass.getSuperclass();
+            } while (currentClass != NodeLIRBuilder.class);
+            registry.put(theClass, rules);
+            assert registry.get(theClass) == rules;
+            result = rules;
+        }
+
+        if (result.size() == 0) {
+            return null;
+        }
+        return result;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRules.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,34 @@
+/*
+ * 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.compiler.match;
+
+import java.lang.annotation.*;
+
+/**
+ * The repeatable representation of {@link MatchRule}. Should never be used directly.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface MatchRules {
+    MatchRule[] value();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatement.java	Thu Apr 24 23:32:30 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.compiler.match;
+
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.gen.*;
+import com.oracle.graal.compiler.match.MatchPattern.Result;
+import com.oracle.graal.nodes.*;
+
+/**
+ * A named {@link MatchPattern} along with a {@link MatchGenerator} that can be evaluated to replace
+ * one or more {@link ValueNode}s with a single {@link Value}.
+ */
+
+public class MatchStatement {
+    private final String name;
+    private final MatchPattern pattern;
+    private final Class<? extends MatchGenerator> generatorClass;
+
+    public MatchStatement(String name, MatchPattern pattern) {
+        this.name = name;
+        this.pattern = pattern;
+        this.generatorClass = null;
+    }
+
+    public MatchStatement(String name, MatchPattern pattern, Class<? extends MatchGenerator> generator) {
+        this.name = name;
+        this.pattern = pattern;
+        this.generatorClass = generator;
+    }
+
+    /**
+     * Attempt to match the current statement against a Node.
+     *
+     * @param builder the current builder instance.
+     * @param node the node to be matched
+     * @param nodes the nodes in the current block
+     * @return true if the statement matched something and set a {@link ComplexMatchResult} to be
+     *         evaluated by the NodeLIRBuilder.
+     */
+    public boolean generate(NodeLIRBuilder builder, ValueNode node, List<ScheduledNode> nodes) {
+        MatchContext context = new MatchContext(builder, this, node, nodes);
+        Result result = pattern.match(node, context);
+        if (result == Result.OK) {
+            result = context.validate();
+        }
+        if (result == Result.OK) {
+            MatchGenerator generator = null;
+            try {
+                generator = generatorClass.newInstance();
+                // Transfer values into gen
+                context.transferState(generator);
+                ComplexMatchResult value = generator.match(builder);
+                if (value != null) {
+                    context.setResult(value);
+                    return true;
+                }
+            } catch (InstantiationException | IllegalAccessException e) {
+                throw new GraalInternalError(e);
+            }
+        } else {
+            // This is fairly verbose for normal usage.
+            // if (result.code != MatchResultCode.WRONG_CLASS) {
+            // // Don't bother logging if it's just the wrong shape.
+            // Debug.log("while matching %s|%s %s %s %s", context.getRoot().toString(Verbosity.Id),
+            // context.getRoot().getClass().getSimpleName(), getName(), result, node.graph());
+            // }
+        }
+        return false;
+    }
+
+    public String formatMatch(ValueNode root) {
+        return pattern.formatMatch(root);
+    }
+
+    public MatchPattern getPattern() {
+        return pattern;
+    }
+
+    public String getName() {
+        return name;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatementSet.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,40 @@
+/*
+ * 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.compiler.match;
+
+import java.util.*;
+
+import com.oracle.graal.compiler.gen.*;
+
+public interface MatchStatementSet {
+    /**
+     * @return the {@link NodeLIRBuilder} subclass which defined this set of {@link MatchStatement}
+     *         instances.
+     */
+    public Class<? extends NodeLIRBuilder> forClass();
+
+    /**
+     * @return the {@link MatchStatement}s available with this {@link NodeLIRBuilder} subclass.
+     */
+    public List<MatchStatement> statements();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchableNode.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,66 @@
+/*
+ * 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.compiler.match;
+
+import java.lang.annotation.*;
+
+import com.oracle.graal.nodes.*;
+
+/**
+ * Describes the properties of a node for use when building a {@link MatchPattern}s.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Repeatable(value = MatchableNodes.class)
+public @interface MatchableNode {
+
+    /**
+     * The {@link ValueNode} subclass this annotation describes. These annotations might work better
+     * if they were directly on the node being described but that may complicate the annotation
+     * processing.
+     */
+    Class<? extends ValueNode> value();
+
+    /**
+     * The name used in match patterns. Defaults to class.getSimpleName() with the word Node removed
+     * from the end.
+     */
+    String shortName() default "";
+
+    /**
+     * The number of matchable inputs, which may be less than the real number of inputs.
+     */
+    int inputs() default 0;
+
+    /**
+     * A helper class to visit the inputs in a specified order. Should be a subclass of
+     * {@link MatchNodeAdapter}.
+     */
+    Class<?> adapter() default MatchableNode.class;
+
+    /**
+     * Can a pattern be matched with the operands swapped. This will cause swapped versions of
+     * patterns to be automatically generated.
+     */
+    boolean commutative() default false;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchableNodeImport.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,36 @@
+/*
+ * 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.compiler.match;
+
+import java.lang.annotation.*;
+
+/**
+ * A list of classes which contain one or more {@link MatchableNode} annotations describing nodes
+ * that may be used in match expressions. Those {@link MatchableNode} declarations are parsed before
+ * processing any {@link MatchRule}s.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface MatchableNodeImport {
+    String[] value() default {};
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchableNodes.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,34 @@
+/*
+ * 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.compiler.match;
+
+import java.lang.annotation.*;
+
+/**
+ * The repeatable representation of {@link MatchableNode}. Should never be used directly.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface MatchableNodes {
+    MatchableNode[] value() default {};
+}
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Thu Apr 24 23:32:30 2014 +0100
@@ -26,17 +26,20 @@
 import static com.oracle.graal.api.code.ValueUtil.*;
 import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
 import static com.oracle.graal.hotspot.nodes.UncommonTrapCallNode.*;
+import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
 
 import java.util.*;
 
 import com.oracle.graal.amd64.*;
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.amd64.*;
 import com.oracle.graal.compiler.amd64.*;
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.HotSpotVMConfig.CompressEncoding;
+import com.oracle.graal.hotspot.data.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.nodes.type.*;
 import com.oracle.graal.hotspot.stubs.*;
@@ -44,6 +47,7 @@
 import com.oracle.graal.lir.StandardOp.NoOp;
 import com.oracle.graal.lir.StandardOp.SaveRegistersOp;
 import com.oracle.graal.lir.amd64.*;
+import com.oracle.graal.lir.amd64.AMD64ControlFlow.BranchOp;
 import com.oracle.graal.lir.amd64.AMD64ControlFlow.CondMoveOp;
 import com.oracle.graal.lir.amd64.AMD64Move.CompareAndSwapOp;
 import com.oracle.graal.lir.amd64.AMD64Move.LeaDataOp;
@@ -52,6 +56,7 @@
 import com.oracle.graal.lir.amd64.AMD64Move.MoveToRegOp;
 import com.oracle.graal.lir.amd64.AMD64Move.StoreConstantOp;
 import com.oracle.graal.lir.amd64.AMD64Move.StoreOp;
+import com.oracle.graal.lir.asm.*;
 import com.oracle.graal.lir.gen.*;
 
 /**
@@ -551,4 +556,54 @@
         return result;
     }
 
+    public static class CompareMemoryCompressedOp extends AMD64LIRInstruction {
+        @Alive({COMPOSITE}) protected AMD64AddressValue x;
+        @Use({CONST, REG}) protected Value y;
+        @State protected LIRFrameState state;
+
+        public CompareMemoryCompressedOp(AMD64AddressValue x, Value y, LIRFrameState state) {
+            assert HotSpotGraalRuntime.runtime().getConfig().useCompressedOops;
+            this.x = x;
+            this.y = y;
+            this.state = state;
+        }
+
+        @Override
+        public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
+            if (state != null) {
+                crb.recordImplicitException(masm.position(), state);
+            }
+            if (y instanceof Constant) {
+                Constant constant = (Constant) y;
+                if (constant.isNull()) {
+                    masm.cmpl(x.toAddress(), 0);
+                } else {
+                    if (y.getKind() == Kind.Object) {
+                        crb.recordInlineDataInCode(new OopData(0, HotSpotObjectConstant.asObject(constant), true));
+                    } else if (y.getKind() == Kind.Long) {
+                        crb.recordInlineDataInCode(new MetaspaceData(0, constant.asLong(), HotSpotMetaspaceConstant.getMetaspaceObject(constant), true));
+                    } else {
+                        throw GraalInternalError.shouldNotReachHere();
+                    }
+                    masm.cmpl(x.toAddress(), 0xdeaddead);
+                }
+            } else {
+                masm.cmpl(asRegister(y), x.toAddress());
+            }
+        }
+    }
+
+    protected void emitCompareBranchMemoryCompressed(Value left, Value right, Condition cond, LabelRef trueLabel, LabelRef falseLabel, double trueLabelProbability, LIRFrameState state) {
+        boolean mirrored = false;
+        if (left instanceof AMD64AddressValue) {
+            append(new CompareMemoryCompressedOp((AMD64AddressValue) left, right, state));
+        } else {
+            assert right instanceof AMD64AddressValue;
+            append(new CompareMemoryCompressedOp((AMD64AddressValue) right, left, state));
+            mirrored = true;
+        }
+
+        Condition finalCondition = mirrored ? cond.mirror() : cond;
+        append(new BranchOp(finalCondition, trueLabel, falseLabel, trueLabelProbability));
+    }
 }
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotMemoryPeephole.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotMemoryPeephole.java	Thu Apr 24 23:32:30 2014 +0100
@@ -23,21 +23,14 @@
 
 package com.oracle.graal.hotspot.amd64;
 
-import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
-
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.asm.amd64.*;
 import com.oracle.graal.compiler.amd64.*;
-import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.hotspot.*;
-import com.oracle.graal.hotspot.data.*;
-import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.hotspot.amd64.AMD64HotSpotLIRGenerator.CompareMemoryCompressedOp;
 import com.oracle.graal.hotspot.nodes.type.*;
 import com.oracle.graal.lir.*;
-import com.oracle.graal.lir.amd64.*;
 import com.oracle.graal.lir.amd64.AMD64ControlFlow.BranchOp;
-import com.oracle.graal.lir.asm.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
 
@@ -46,39 +39,6 @@
  */
 
 public class AMD64HotSpotMemoryPeephole extends AMD64MemoryPeephole {
-    public static class CompareMemoryCompressedOp extends AMD64LIRInstruction {
-        @Alive({COMPOSITE}) protected AMD64AddressValue x;
-        @Use({CONST}) protected Value y;
-        @State protected LIRFrameState state;
-
-        public CompareMemoryCompressedOp(AMD64AddressValue x, Constant y, LIRFrameState state) {
-            assert HotSpotGraalRuntime.runtime().getConfig().useCompressedOops;
-            this.x = x;
-            this.y = y;
-            this.state = state;
-        }
-
-        @Override
-        public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
-            Constant constant = (Constant) y;
-            if (constant.isNull()) {
-                masm.cmpl(x.toAddress(), 0);
-            } else {
-                if (y.getKind() == Kind.Object) {
-                    crb.recordInlineDataInCode(new OopData(0, HotSpotObjectConstant.asObject(constant), true));
-                } else if (y.getKind() == Kind.Long) {
-                    crb.recordInlineDataInCode(new MetaspaceData(0, constant.asLong(), HotSpotMetaspaceConstant.getMetaspaceObject(constant), true));
-                } else {
-                    throw GraalInternalError.shouldNotReachHere();
-                }
-                if (state != null) {
-                    crb.recordImplicitException(masm.position(), state);
-                }
-                masm.cmpl(x.toAddress(), 0xdeaddead);
-            }
-        }
-    }
-
     AMD64HotSpotMemoryPeephole(AMD64NodeLIRBuilder gen) {
         super(gen);
     }
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java	Thu Apr 24 23:32:30 2014 +0100
@@ -32,7 +32,9 @@
 import com.oracle.graal.asm.*;
 import com.oracle.graal.asm.amd64.AMD64Address.Scale;
 import com.oracle.graal.compiler.amd64.*;
+import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.gen.*;
+import com.oracle.graal.compiler.match.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.hotspot.*;
@@ -45,13 +47,47 @@
 import com.oracle.graal.lir.amd64.AMD64Move.CompareAndSwapOp;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
 
 /**
  * LIR generator specialized for AMD64 HotSpot.
  */
+@MatchableNodeImport({"com.oracle.graal.hotspot.nodes.HotSpotMatchableNodes"})
 public class AMD64HotSpotNodeLIRBuilder extends AMD64NodeLIRBuilder implements HotSpotNodeLIRBuilder {
 
+    private static ValueNode filterCompression(ValueNode node) {
+        ValueNode result = node;
+        while (result instanceof CompressionNode) {
+            result = ((CompressionNode) result).getInput();
+        }
+        return result;
+    }
+
+    private void emitCompareCompressedMemory(IfNode ifNode, ValueNode value, Access access, CompareNode compare) {
+        Condition cond = compare.condition();
+        LabelRef trueLabel = getLIRBlock(ifNode.trueSuccessor());
+        LabelRef falseLabel = getLIRBlock(ifNode.falseSuccessor());
+        double trueLabelProbability = ifNode.probability(ifNode.trueSuccessor());
+        Value left;
+        Value right;
+        if (access == filterCompression(compare.x())) {
+            if (value.isConstant()) {
+                left = value.asConstant();
+            } else {
+                left = gen.loadNonConst(operand(value));
+            }
+            right = makeAddress(access);
+        } else {
+            assert access == filterCompression(compare.y());
+            left = makeAddress(access);
+            right = gen.loadNonConst(operand(value));
+            cond = cond.mirror();
+        }
+        getGen().emitCompareBranchMemoryCompressed(left, right, cond, trueLabel, falseLabel, trueLabelProbability, getState(access));
+    }
+
     public AMD64HotSpotNodeLIRBuilder(StructuredGraph graph, LIRGeneratorTool gen) {
         super(graph, gen);
         memoryPeephole = new AMD64HotSpotMemoryPeephole(this);
@@ -201,4 +237,40 @@
         gen.emitMove(result, raxLocal);
         setResult(x, result);
     }
+
+    /**
+     * Helper class to convert the NodeLIRBuilder into the current subclass.
+     */
+    static abstract class AMD64HotSpotMatchGenerator implements MatchGenerator {
+        public AMD64HotSpotMatchGenerator() {
+        }
+
+        public ComplexMatchResult match(NodeLIRBuilder gen) {
+            return match((AMD64HotSpotNodeLIRBuilder) gen);
+        }
+
+        abstract public ComplexMatchResult match(AMD64HotSpotNodeLIRBuilder gen);
+    }
+
+    @MatchRule("(If (ObjectEquals=compare Constant=value (Compression Read=access)))")
+    @MatchRule("(If (ObjectEquals=compare Constant=value (Compression FloatingRead=access)))")
+    @MatchRule("(If (ObjectEquals=compare (Compression value) (Compression Read=access)))")
+    @MatchRule("(If (ObjectEquals=compare (Compression value) (Compression FloatingRead=access)))")
+    public static class IfCompareMemory extends AMD64HotSpotMatchGenerator {
+        IfNode root;
+        Access access;
+        ValueNode value;
+        CompareNode compare;
+
+        @Override
+        public ComplexMatchResult match(AMD64HotSpotNodeLIRBuilder gen) {
+            if (HotSpotGraalRuntime.runtime().getConfig().useCompressedOops) {
+                return builder -> {
+                    gen.emitCompareCompressedMemory(root, value, access, compare);
+                    return null;
+                };
+            }
+            return null;
+        }
+    }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/debug/BenchmarkCounters.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/debug/BenchmarkCounters.java	Thu Apr 24 23:32:30 2014 +0100
@@ -99,7 +99,6 @@
 
     private static final boolean DUMP_STATIC = false;
 
-    public static String excludedClassPrefix = null;
     public static boolean enabled = false;
 
     public static final ConcurrentHashMap<String, Integer> indexes = new ConcurrentHashMap<>();
@@ -115,9 +114,10 @@
         String name;
         String group = counter.getGroup();
         if (counter.isWithContext()) {
-            name = counter.getName() + " @ " + counter.graph().graphId() + ":" + MetaUtil.format("%h.%n", counter.graph().method());
-            if (counter.graph().name != null) {
-                name += " (" + counter.graph().name + ")";
+            StructuredGraph graph = counter.graph();
+            name = counter.getName() + " @ " + graph.graphId() + ":" + (graph.method() == null ? "" : MetaUtil.format("%h.%n", graph.method()));
+            if (graph.name != null) {
+                name += " (" + graph.name + ")";
             }
             name += "#" + group;
 
@@ -289,25 +289,30 @@
         final class BenchmarkCountersOutputStream extends CallbackOutputStream {
 
             private long startTime;
+            private boolean running;
             private boolean waitingForEnd;
 
             private BenchmarkCountersOutputStream(PrintStream delegate, String start, String end) {
-                super(delegate, new String[]{start, end, "\n"});
+                super(delegate, new String[]{"\n", end, start});
             }
 
             @Override
             protected void patternFound(int index) {
                 switch (index) {
-                    case 0:
+                    case 2:
                         startTime = System.nanoTime();
                         BenchmarkCounters.clear(compilerToVM.collectCounters());
+                        running = true;
                         break;
                     case 1:
-                        waitingForEnd = true;
+                        if (running) {
+                            waitingForEnd = true;
+                        }
                         break;
-                    case 2:
+                    case 0:
                         if (waitingForEnd) {
                             waitingForEnd = false;
+                            running = false;
                             BenchmarkCounters.dump(delegate, (System.nanoTime() - startTime) / 1000000000d, compilerToVM.collectCounters(), 100);
                         }
                         break;
@@ -329,7 +334,6 @@
                     throw new GraalInternalError("invalid arguments to BenchmarkDynamicCounters: err|out");
                 }
             }
-            excludedClassPrefix = "Lcom/oracle/graal/";
             enabled = true;
         }
         if (Options.GenericDynamicCounters.getValue()) {
@@ -371,26 +375,24 @@
 
     public static void lower(DynamicCounterNode counter, HotSpotRegistersProvider registers, HotSpotVMConfig config, Kind wordKind) {
         StructuredGraph graph = counter.graph();
-        if (excludedClassPrefix == null || (counter.graph().method() != null && !counter.graph().method().getDeclaringClass().getName().startsWith(excludedClassPrefix))) {
 
-            ReadRegisterNode thread = graph.add(new ReadRegisterNode(registers.getThreadRegister(), wordKind, true, false));
+        ReadRegisterNode thread = graph.add(new ReadRegisterNode(registers.getThreadRegister(), wordKind, true, false));
 
-            int index = BenchmarkCounters.getIndex(counter);
-            if (index >= config.graalCountersSize) {
-                throw new GraalInternalError("too many counters, reduce number of counters or increase -XX:GraalCounterSize=... (current value: " + config.graalCountersSize + ")");
-            }
-            ConstantLocationNode arrayLocation = ConstantLocationNode.create(LocationIdentity.ANY_LOCATION, wordKind, config.graalCountersThreadOffset, graph);
-            ReadNode readArray = graph.add(new ReadNode(thread, arrayLocation, StampFactory.forKind(wordKind), BarrierType.NONE, false));
-            ConstantLocationNode location = ConstantLocationNode.create(LocationIdentity.ANY_LOCATION, Kind.Long, Unsafe.ARRAY_LONG_INDEX_SCALE * index, graph);
-            ReadNode read = graph.add(new ReadNode(readArray, location, StampFactory.forKind(Kind.Long), BarrierType.NONE, false));
-            IntegerAddNode add = graph.unique(new IntegerAddNode(StampFactory.forKind(Kind.Long), read, counter.getIncrement()));
-            WriteNode write = graph.add(new WriteNode(readArray, add, location, BarrierType.NONE, false));
+        int index = BenchmarkCounters.getIndex(counter);
+        if (index >= config.graalCountersSize) {
+            throw new GraalInternalError("too many counters, reduce number of counters or increase -XX:GraalCounterSize=... (current value: " + config.graalCountersSize + ")");
+        }
+        ConstantLocationNode arrayLocation = ConstantLocationNode.create(LocationIdentity.ANY_LOCATION, wordKind, config.graalCountersThreadOffset, graph);
+        ReadNode readArray = graph.add(new ReadNode(thread, arrayLocation, StampFactory.forKind(wordKind), BarrierType.NONE, false));
+        ConstantLocationNode location = ConstantLocationNode.create(LocationIdentity.ANY_LOCATION, Kind.Long, Unsafe.ARRAY_LONG_INDEX_SCALE * index, graph);
+        ReadNode read = graph.add(new ReadNode(readArray, location, StampFactory.forKind(Kind.Long), BarrierType.NONE, false));
+        IntegerAddNode add = graph.unique(new IntegerAddNode(StampFactory.forKind(Kind.Long), read, counter.getIncrement()));
+        WriteNode write = graph.add(new WriteNode(readArray, add, location, BarrierType.NONE, false));
 
-            graph.addBeforeFixed(counter, thread);
-            graph.addBeforeFixed(counter, readArray);
-            graph.addBeforeFixed(counter, read);
-            graph.addBeforeFixed(counter, write);
-        }
+        graph.addBeforeFixed(counter, thread);
+        graph.addBeforeFixed(counter, readArray);
+        graph.addBeforeFixed(counter, read);
+        graph.addBeforeFixed(counter, write);
         graph.removeFixed(counter);
     }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CompressionNode.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CompressionNode.java	Thu Apr 24 23:32:30 2014 +0100
@@ -93,6 +93,10 @@
         throw GraalInternalError.shouldNotReachHere();
     }
 
+    public ValueNode getInput() {
+        return input;
+    }
+
     @Override
     public Node canonical(CanonicalizerTool tool) {
         if (input instanceof CompressionNode) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/HotSpotMatchableNodes.java	Thu Apr 24 23:32:30 2014 +0100
@@ -0,0 +1,37 @@
+/*
+ * 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.hotspot.nodes;
+
+import com.oracle.graal.compiler.match.*;
+import com.oracle.graal.nodes.*;
+
+@MatchableNode(shortName = "Compression", value = CompressionNode.class, inputs = 1, adapter = HotSpotMatchableNodes.CompressionNodeAdapter.class)
+public class HotSpotMatchableNodes {
+    public static class CompressionNodeAdapter extends MatchNodeAdapter {
+        @Override
+        protected ValueNode getFirstInput(ValueNode node) {
+            return ((CompressionNode) node).getInput();
+        }
+    }
+
+}
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java	Thu Apr 24 23:32:30 2014 +0100
@@ -39,8 +39,8 @@
 
     // @formatter:off
 
-    IADD, ISUB, IMUL, IDIV, IDIVREM, IREM, IUDIV, IUREM, IAND, IOR, IXOR, ISHL, ISHR, IUSHR,
-    LADD, LSUB, LMUL, LDIV, LDIVREM, LREM, LUDIV, LUREM, LAND, LOR, LXOR, LSHL, LSHR, LUSHR,
+    IADD, ISUB, IMUL, IDIV, IDIVREM, IREM, IUDIV, IUREM, IAND, IOR, IXOR, ISHL, ISHR, IUSHR, IROL, IROR,
+    LADD, LSUB, LMUL, LDIV, LDIVREM, LREM, LUDIV, LUREM, LAND, LOR, LXOR, LSHL, LSHR, LUSHR, LROL, LROR,
     FADD, FSUB, FMUL, FDIV, FREM, FAND, FOR, FXOR,
     DADD, DSUB, DMUL, DDIV, DREM, DAND, DOR, DXOR,
     INEG, LNEG, INOT, LNOT,
@@ -491,6 +491,14 @@
                     assert asIntReg(src).equals(AMD64.rcx);
                     masm.shrl(asIntReg(dst));
                     break;
+                case IROL:
+                    assert asIntReg(src).equals(AMD64.rcx);
+                    masm.roll(asIntReg(dst));
+                    break;
+                case IROR:
+                    assert asIntReg(src).equals(AMD64.rcx);
+                    masm.roll(asIntReg(dst));
+                    break;
 
                 case LADD:
                     masm.addq(asLongReg(dst), asLongReg(src));
@@ -522,6 +530,14 @@
                     assert asIntReg(src).equals(AMD64.rcx);
                     masm.shrq(asLongReg(dst));
                     break;
+                case LROL:
+                    assert asIntReg(src).equals(AMD64.rcx);
+                    masm.rolq(asLongReg(dst));
+                    break;
+                case LROR:
+                    assert asIntReg(src).equals(AMD64.rcx);
+                    masm.rorq(asLongReg(dst));
+                    break;
 
                 case FADD:
                     masm.addss(asFloatReg(dst), asFloatReg(src));
@@ -695,6 +711,12 @@
                 case IUSHR:
                     masm.shrl(asIntReg(dst), crb.asIntConst(src) & 31);
                     break;
+                case IROL:
+                    masm.roll(asIntReg(dst), crb.asIntConst(src) & 31);
+                    break;
+                case IROR:
+                    masm.rorl(asIntReg(dst), crb.asIntConst(src) & 31);
+                    break;
 
                 case LADD:
                     masm.addq(asLongReg(dst), crb.asIntConst(src));
@@ -723,6 +745,12 @@
                 case LUSHR:
                     masm.shrq(asLongReg(dst), crb.asIntConst(src) & 63);
                     break;
+                case LROL:
+                    masm.rolq(asLongReg(dst), crb.asIntConst(src) & 31);
+                    break;
+                case LROR:
+                    masm.rorq(asLongReg(dst), crb.asIntConst(src) & 31);
+                    break;
 
                 case FADD:
                     masm.addss(asFloatReg(dst), (AMD64Address) crb.asFloatConstRef(src));
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java	Thu Apr 24 23:32:30 2014 +0100
@@ -132,6 +132,9 @@
                     default:
                         throw GraalInternalError.shouldNotReachHere();
                 }
+
+            } else {
+                throw GraalInternalError.shouldNotReachHere();
             }
         }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IndirectCallTargetNode.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IndirectCallTargetNode.java	Thu Apr 24 23:32:30 2014 +0100
@@ -43,6 +43,6 @@
 
     @Override
     public String targetName() {
-        return "Indirect#" + ((JavaMethod) target()).getName();
+        return MetaUtil.format("Indirect#%h.%n", target());
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java	Thu Apr 24 23:32:30 2014 +0100
@@ -30,7 +30,7 @@
  * This node can be used to add a counter to the code that will estimate the dynamic number of calls
  * by adding an increment to the compiled code. This should of course only be used for
  * debugging/testing purposes.
- * 
+ *
  * A unique counter will be created for each unique name passed to the constructor. Depending on the
  * value of withContext, the name of the root method is added to the counter's name.
  */
--- a/graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionProcessor.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionProcessor.java	Thu Apr 24 23:32:30 2014 +0100
@@ -40,8 +40,10 @@
  *
  * <pre>
  * ServiceLoader&lt;Options&gt; sl = ServiceLoader.loadInstalled(Options.class);
- * for (OptionDescriptor desc : sl) {
- *     // use desc
+ * for (Options opts : sl) {
+ *     for (OptionDescriptor desc : sl) {
+ *         // use desc
+ *     }
  * }
  * </pre>
  */
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ProfileCompiledMethodsPhase.java	Thu Apr 24 23:29:28 2014 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ProfileCompiledMethodsPhase.java	Thu Apr 24 23:32:30 2014 +0100
@@ -55,8 +55,11 @@
 
     private static final String GROUP_NAME = "~profiled weight";
     private static final String GROUP_NAME_WITHOUT = "~profiled weight (invoke-free sections)";
+    private static final String GROUP_NAME_INVOKES = "~profiled invokes";
 
     private static final boolean WITH_SECTION_HEADER = false;
+    private static boolean WITH_INVOKE_FREE_SECTIONS = false;
+    private static boolean WITH_INVOKES = true;
 
     @Override
     protected void run(StructuredGraph graph) {
@@ -77,8 +80,16 @@
         while (current.next() instanceof FixedWithNextNode) {
             current = (FixedWithNextNode) current.next();
         }
-        if (graph.getEntryBCI() != -1) {
-            addSectionCounters(current, Arrays.asList(cfg.getBlocks()), cfg.getLoops(), schedule, probabilities);
+        addSectionCounters(current, Arrays.asList(cfg.getBlocks()), cfg.getLoops(), schedule, probabilities);
+
+        if (WITH_INVOKES) {
+            for (Node node : graph.getNodes()) {
+                if (node instanceof Invoke) {
+                    Invoke invoke = (Invoke) node;
+                    DynamicCounterNode.addCounterBefore(GROUP_NAME_INVOKES, invoke.callTarget().targetName(), 1, true, invoke.asNode());
+
+                }
+            }
         }
     }
 
@@ -89,7 +100,7 @@
         }
         double weight = getSectionWeight(schedule, probabilities, blocks) / probabilities.get(start);
         DynamicCounterNode.addCounterBefore(GROUP_NAME, sectionHead(start), (long) weight, true, start.next());
-        if (!hasInvoke(blocks)) {
+        if (WITH_INVOKE_FREE_SECTIONS && !hasInvoke(blocks)) {
             DynamicCounterNode.addCounterBefore(GROUP_NAME_WITHOUT, sectionHead(start), (long) weight, true, start.next());
         }
     }
--- a/mx/projects	Thu Apr 24 23:29:28 2014 +0100
+++ b/mx/projects	Thu Apr 24 23:32:30 2014 +0100
@@ -426,6 +426,7 @@
 project@com.oracle.graal.compiler@checkstyle=com.oracle.graal.graph
 project@com.oracle.graal.compiler@javaCompliance=1.8
 project@com.oracle.graal.compiler@annotationProcessors=com.oracle.graal.service.processor
+project@com.oracle.graal.compiler@annotationProcessorForDependents=true
 project@com.oracle.graal.compiler@workingSets=Graal
 
 # graal.compiler.amd64