# HG changeset patch # User Tom Rodriguez # Date 1398366054 25200 # Node ID 319deee167462ada5c770ed45d1d49a13a2f0f73 # Parent 24d4b669756e25f6391945011691f56034f130d7 add support for matching multiple HIR nodes when lowering to LIR diff -r 24d4b669756e -r 319deee16746 CHANGELOG.md --- a/CHANGELOG.md Thu Apr 24 17:30:48 2014 +0200 +++ b/CHANGELOG.md Thu Apr 24 12:00:54 2014 -0700 @@ -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 diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Value.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Value.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Value.java Thu Apr 24 12:00:54 2014 -0700 @@ -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) { diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java --- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java Thu Apr 24 12:00:54 2014 -0700 @@ -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)); diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java --- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64NodeLIRBuilder.java Thu Apr 24 12:00:54 2014 -0700 @@ -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()]; diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java Thu Apr 24 12:00:54 2014 -0700 @@ -272,7 +272,9 @@ @Option(help = "") public static final OptionValue OptPushThroughPi = new OptionValue<>(true); @Option(help = "Allow backend to emit arithmetic and compares directly against memory.") - public static final OptionValue OptFoldMemory = new OptionValue<>(true); + public static final OptionValue OptFoldMemory = new OptionValue<>(false); + @Option(help = "Allow backend to match complex expressions.") + public static final OptionValue MatchExpressions = new OptionValue<>(true); /** diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/META-INF/services/javax.annotation.processing.Processor --- /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 12:00:54 2014 -0700 @@ -0,0 +1,1 @@ +com.oracle.graal.compiler.match.MatchProcessor diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java Thu Apr 24 12:00:54 2014 -0700 @@ -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, List> 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 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 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 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"); diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/ComplexMatchResult.java --- /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 12:00:54 2014 -0700 @@ -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); +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/ComplexMatchValue.java --- /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 12:00:54 2014 -0700 @@ -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); + } +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/GraalMatchableNodes.java --- /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 12:00:54 2014 -0700 @@ -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(); + } + } +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchContext.java --- /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 12:00:54 2014 -0700 @@ -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 consumed = new ArrayList<>(); + private final List nodes; + private final ValueNode root; + private List names; + private List> types; + private List values; + private final MatchStatement rule; + private int startIndex; + private int endIndex; + private final NodeLIRBuilder builder; + + public MatchContext(NodeLIRBuilder builder, MatchStatement rule, ValueNode node, List 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 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 getNodes() { + return nodes; + } +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchGenerator.java --- /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 12:00:54 2014 -0700 @@ -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); +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchNodeAdapter.java --- /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 12:00:54 2014 -0700 @@ -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(); + } +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java --- /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 12:00:54 2014 -0700 @@ -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 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 nodeClass, String name, boolean singleUser) { + this(nodeClass, name, null, null, null, singleUser); + } + + public MatchPattern(Class nodeClass, String name, MatchPattern first, MatchNodeAdapter adapter, boolean singleUser) { + this(nodeClass, name, first, null, adapter, singleUser); + } + + public MatchPattern(Class 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 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; + } + } +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java --- /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 12:00:54 2014 -0700 @@ -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: + * + *
+ *     ServiceLoader sl = ServiceLoader.loadInstalled(MatchStatementSet.class);
+ *     for (MatchStatementSet rules : sl) {
+ *         ...
+ *     }
+ * 
+ */ +@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 processedMatchRule = new HashSet<>(); + private final Set 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 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 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 types = new HashMap<>(); + ArrayList 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 generateVariants() { + String prefix = formatPrefix(); + String suffix = formatSuffix(); + ArrayList 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 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 { + + 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 options = new ArrayList<>(); + final Set 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 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 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 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 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 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); + } + } + +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRule.java --- /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 12:00:54 2014 -0700 @@ -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. + * + *
+ *   NAME := [a-z][a-zA-Z0-9]*
+ *   NODETYPE := [A-Z][a-zA-Z0-9]*
+ *   NODEORNAME :=  NODE [ = NAME ] | NAME
+ *   EXPRESSION := ( NODEORNAME [ EXPRESSION | NODEORNAME [ EXPRESSION | NODEORNAME ] )
+ * 
+ * + * 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(); +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRuleRegistry.java --- /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 12:00:54 2014 -0700 @@ -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, Map, List>> 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, List> lookup(Class theClass) { + Map, List> result = registry.get(theClass); + + if (result == null) { + HashMap, List> localRules = new HashMap<>(); + ServiceLoader 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, List> rules = new HashMap<>(); + Class currentClass = theClass; + do { + List statements = localRules.get(currentClass); + if (statements != null) { + for (MatchStatement statement : statements) { + Class nodeClass = statement.getPattern().nodeClass(); + List 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; + } +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRules.java --- /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 12:00:54 2014 -0700 @@ -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(); +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatement.java --- /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 12:00:54 2014 -0700 @@ -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 generatorClass; + + public MatchStatement(String name, MatchPattern pattern) { + this.name = name; + this.pattern = pattern; + this.generatorClass = null; + } + + public MatchStatement(String name, MatchPattern pattern, Class 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 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; + } +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatementSet.java --- /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 12:00:54 2014 -0700 @@ -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 forClass(); + + /** + * @return the {@link MatchStatement}s available with this {@link NodeLIRBuilder} subclass. + */ + public List statements(); +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchableNode.java --- /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 12:00:54 2014 -0700 @@ -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 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; +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchableNodeImport.java --- /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 12:00:54 2014 -0700 @@ -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 {}; +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchableNodes.java --- /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 12:00:54 2014 -0700 @@ -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 {}; +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Thu Apr 24 12:00:54 2014 -0700 @@ -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)); + } } diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotMemoryPeephole.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotMemoryPeephole.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotMemoryPeephole.java Thu Apr 24 12:00:54 2014 -0700 @@ -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); } diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotNodeLIRBuilder.java Thu Apr 24 12:00:54 2014 -0700 @@ -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; + } + } } diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CompressionNode.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CompressionNode.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CompressionNode.java Thu Apr 24 12:00:54 2014 -0700 @@ -93,6 +93,10 @@ throw GraalInternalError.shouldNotReachHere(); } + public ValueNode getInput() { + return input; + } + @Override public Node canonical(CanonicalizerTool tool) { if (input instanceof CompressionNode) { diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/HotSpotMatchableNodes.java --- /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 12:00:54 2014 -0700 @@ -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(); + } + } + +} diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java --- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java Thu Apr 24 12:00:54 2014 -0700 @@ -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)); diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java --- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java Thu Apr 24 12:00:54 2014 -0700 @@ -132,6 +132,9 @@ default: throw GraalInternalError.shouldNotReachHere(); } + + } else { + throw GraalInternalError.shouldNotReachHere(); } } diff -r 24d4b669756e -r 319deee16746 graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionProcessor.java --- a/graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionProcessor.java Thu Apr 24 17:30:48 2014 +0200 +++ b/graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionProcessor.java Thu Apr 24 12:00:54 2014 -0700 @@ -40,8 +40,10 @@ * *
  * ServiceLoader<Options> sl = ServiceLoader.loadInstalled(Options.class);
- * for (OptionDescriptor desc : sl) {
- *     // use desc
+ * for (Options opts : sl) {
+ *     for (OptionDescriptor desc : sl) {
+ *         // use desc
+ *     }
  * }
  * 
*/ diff -r 24d4b669756e -r 319deee16746 mx/projects --- a/mx/projects Thu Apr 24 17:30:48 2014 +0200 +++ b/mx/projects Thu Apr 24 12:00:54 2014 -0700 @@ -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