# HG changeset patch # User Tom Rodriguez # Date 1395356223 25200 # Node ID 6ce6c4ccba8f1f0230a233334681347b84f0ca07 # Parent 579a2a124c95f3cc0f4dcf956a0586e6bc5d6a00 initial support for memory arithmetic on x86 diff -r 579a2a124c95 -r 6ce6c4ccba8f CHANGELOG.md --- a/CHANGELOG.md Thu Mar 20 13:41:32 2014 -0700 +++ b/CHANGELOG.md Thu Mar 20 15:57:03 2014 -0700 @@ -9,6 +9,7 @@ * New (tested) invariant that equality comparisons for `JavaType`/`JavaMethod`/`JavaField` values use `.equals()` instead of `==`. * Made graph caching compilation-local. * Added AllocSpy tool for analyzing allocation in Graal using the [Java Allocation Instrumenter](https://code.google.com/p/java-allocation-instrumenter/). +* Initial support for memory arithmetic operations on x86 ### Truffle * New API `TruffleRuntime#createCallNode` to create call nodes and to give the runtime system control over its implementation. diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java --- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java Thu Mar 20 15:57:03 2014 -0700 @@ -1669,6 +1669,13 @@ emitInt(imm32); } + public final void testl(AMD64Address dst, int imm32) { + prefixq(dst); + emitByte(0xF7); + emitOperandHelper(0, dst); + emitInt(imm32); + } + public final void testl(Register dst, Register src) { prefixAndEncode(dst.encoding, src.encoding); emitArith(0x85, 0xC0, dst, src); @@ -2064,6 +2071,13 @@ emitOperandHelper(dst, src); } + public final void cmpq(AMD64Address dst, int imm32) { + prefixq(dst); + emitByte(0x81); + emitOperandHelper(7, dst); + emitInt(imm32); + } + public final void cmpq(Register dst, int imm32) { prefixqAndEncode(dst.encoding); emitArith(0x81, 0xF8, dst, imm32); @@ -2410,6 +2424,13 @@ emitOperandHelper(dst, src); } + public final void testq(AMD64Address dst, int imm32) { + prefixq(dst); + emitByte(0xF7); + emitOperandHelper(0, dst); + emitInt(imm32); + } + public final void xorq(Register dst, int imm32) { prefixqAndEncode(dst.encoding); emitArith(0x81, 0xF0, dst, imm32); diff -r 579a2a124c95 -r 6ce6c4ccba8f 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 Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java Thu Mar 20 15:57:03 2014 -0700 @@ -41,6 +41,7 @@ import com.oracle.graal.lir.StandardOp.JumpOp; import com.oracle.graal.lir.amd64.*; import com.oracle.graal.lir.amd64.AMD64Arithmetic.BinaryCommutative; +import com.oracle.graal.lir.amd64.AMD64Arithmetic.BinaryMemory; import com.oracle.graal.lir.amd64.AMD64Arithmetic.BinaryRegConst; import com.oracle.graal.lir.amd64.AMD64Arithmetic.BinaryRegReg; import com.oracle.graal.lir.amd64.AMD64Arithmetic.BinaryRegStack; @@ -48,7 +49,9 @@ import com.oracle.graal.lir.amd64.AMD64Arithmetic.DivRemOp; import com.oracle.graal.lir.amd64.AMD64Arithmetic.FPDivRemOp; import com.oracle.graal.lir.amd64.AMD64Arithmetic.Unary1Op; +import com.oracle.graal.lir.amd64.AMD64Arithmetic.Unary2MemoryOp; import com.oracle.graal.lir.amd64.AMD64Arithmetic.Unary2Op; +import com.oracle.graal.lir.amd64.AMD64Compare.CompareMemoryOp; import com.oracle.graal.lir.amd64.AMD64Compare.CompareOp; import com.oracle.graal.lir.amd64.AMD64ControlFlow.BranchOp; import com.oracle.graal.lir.amd64.AMD64ControlFlow.CondMoveOp; @@ -58,6 +61,7 @@ import com.oracle.graal.lir.amd64.AMD64ControlFlow.StrategySwitchOp; import com.oracle.graal.lir.amd64.AMD64ControlFlow.TableSwitchOp; import com.oracle.graal.lir.amd64.AMD64Move.LeaOp; +import com.oracle.graal.lir.amd64.AMD64Move.LoadOp; import com.oracle.graal.lir.amd64.AMD64Move.MembarOp; import com.oracle.graal.lir.amd64.AMD64Move.MoveFromRegOp; import com.oracle.graal.lir.amd64.AMD64Move.MoveToRegOp; @@ -65,6 +69,7 @@ import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.calc.FloatConvertNode.FloatConvert; +import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; import com.oracle.graal.phases.util.*; @@ -334,6 +339,42 @@ } } + protected void emitCompareMemoryConOp(Kind kind, AMD64AddressValue left, Value right, LIRFrameState state) { + assert kind == right.getKind(); + switch (kind) { + case Int: + append(new CompareMemoryOp(ICMP, left, right, state)); + break; + case Long: + append(new CompareMemoryOp(LCMP, left, right, state)); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + } + + protected void emitCompareRegMemoryOp(Kind kind, Value left, AMD64AddressValue right, LIRFrameState state) { + switch (kind) { + case Int: + append(new CompareMemoryOp(ICMP, left, right, state)); + break; + case Long: + append(new CompareMemoryOp(LCMP, left, right, state)); + break; + case Object: + append(new CompareMemoryOp(ACMP, left, right, state)); + break; + case Float: + append(new CompareMemoryOp(FCMP, left, right, state)); + break; + case Double: + append(new CompareMemoryOp(DCMP, left, right, state)); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + } + /** * This method emits the compare instruction, and may reorder the operands. It returns true if * it did so. @@ -514,7 +555,7 @@ FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) node; if (((fixedWithNextNode instanceof IntegerDivNode) || (fixedWithNextNode instanceof IntegerRemNode)) && fixedWithNextNode.getClass() != divRem.getClass()) { FixedBinaryNode otherDivRem = (FixedBinaryNode) fixedWithNextNode; - if (otherDivRem.x() == divRem.x() && otherDivRem.y() == divRem.y() && operand(otherDivRem) == null) { + if (otherDivRem.x() == divRem.x() && otherDivRem.y() == divRem.y() && !hasOperand(otherDivRem)) { Value[] results = emitIntegerDivRem(operand(divRem.x()), operand(divRem.y()), (DeoptimizingNode) valueNode); if (divRem instanceof IntegerDivNode) { setResult(divRem, results[0]); @@ -532,6 +573,38 @@ return false; } + protected MemoryArithmeticLIRLowerer memoryPeephole; + + @Override + protected MemoryArithmeticLIRLowerer getMemoryLowerer() { + if (memoryPeephole == null) { + // Use the generic one + memoryPeephole = new AMD64MemoryPeephole(this); + } + return memoryPeephole; + } + + protected Value emitBinaryMemory(AMD64Arithmetic op, Kind kind, AllocatableValue a, AMD64AddressValue location, LIRFrameState state) { + Variable result = newVariable(a.getKind()); + append(new BinaryMemory(op, kind, result, a, location, state)); + return result; + } + + protected Value emitConvert2MemoryOp(PlatformKind kind, AMD64Arithmetic op, AMD64AddressValue address, LIRFrameState state) { + Variable result = newVariable(kind); + append(new Unary2MemoryOp(op, result, address, state)); + return result; + } + + protected Value emitZeroExtendMemory(Kind memoryKind, int resultBits, AMD64AddressValue address, LIRFrameState state) { + assert memoryKind.isUnsigned(); + // Issue a zero extending load of the proper bit size and set the result to + // the proper kind. + Variable result = newVariable(resultBits == 32 ? Kind.Int : Kind.Long); + append(new LoadOp(memoryKind, result, address, state)); + return result; + } + private void emitDivRem(AMD64Arithmetic op, Value a, Value b, LIRFrameState state) { AllocatableValue rax = AMD64.rax.asValue(a.getPlatformKind()); emitMove(rax, a); diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64MemoryPeephole.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64MemoryPeephole.java Thu Mar 20 15:57:03 2014 -0700 @@ -0,0 +1,482 @@ +/* + * 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.amd64; + +import static com.oracle.graal.lir.amd64.AMD64Arithmetic.*; +import static com.oracle.graal.nodes.ConstantNode.*; +import static com.oracle.graal.phases.GraalOptions.*; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.asm.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.amd64.*; +import com.oracle.graal.lir.amd64.AMD64ControlFlow.BranchOp; +import com.oracle.graal.lir.amd64.AMD64ControlFlow.FloatBranchOp; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.calc.FloatConvertNode.FloatConvert; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; + +public class AMD64MemoryPeephole implements MemoryArithmeticLIRLowerer { + protected final AMD64LIRGenerator gen; + protected List deferredNodes; + + protected AMD64MemoryPeephole(AMD64LIRGenerator gen) { + this.gen = gen; + } + + public Value setResult(ValueNode x, Value operand) { + return gen.setResult(x, operand); + } + + @Override + public boolean memoryPeephole(Access access, MemoryArithmeticLIRLowerable operation, List deferred) { + this.deferredNodes = deferred; + boolean result = operation.generate(this, access); + if (result) { + Debug.log("merge %s %s with %1s %s %s", access, access.asNode().stamp(), operation, result, access.asNode().graph().method()); + } else { + Debug.log("can't merge %s %s with %1s", access, access.asNode().stamp(), operation); + } + this.deferredNodes = null; + return result; + } + + protected LIRFrameState getState(Access access) { + if (access instanceof DeoptimizingNode) { + return gen.state((DeoptimizingNode) access); + } + return null; + } + + protected AMD64AddressValue makeAddress(Access access) { + return (AMD64AddressValue) access.nullCheckLocation().generateAddress(gen, gen.operand(access.object())); + } + + protected Value emitBinaryMemory(AMD64Arithmetic op, boolean commutative, ValueNode x, ValueNode y, Access access) { + ValueNode other = x; + if (other == access) { + if (commutative) { + other = y; + } else { + return null; + } + } + ensureEvaluated(other); + return gen.emitBinaryMemory(op, access.nullCheckLocation().getValueKind(), gen.asAllocatable(gen.operand(other)), makeAddress(access), getState(access)); + } + + /** + * Constants with multiple users need to be evaluated in the right location so that later users + * can pick up the operand. Make sure that happens when it needs to. + */ + protected void ensureEvaluated(ValueNode node) { + evaluateDeferred(node); + evaluateDeferred(); + } + + protected void evaluateDeferred(ValueNode node) { + // Ensure the other input value has a generated value. + if (ConstantNodeRecordsUsages) { + if (!gen.hasOperand(node)) { + assert node instanceof ConstantNode : node; + ((ConstantNode) node).generate(gen); + } + } + } + + protected void evaluateDeferred() { + if (deferredNodes != null) { + for (ValueNode node : deferredNodes) { + evaluateDeferred(node); + } + } + } + + protected Value emitConvert2MemoryOp(PlatformKind kind, AMD64Arithmetic op, Access access) { + AMD64AddressValue address = makeAddress(access); + LIRFrameState state = getState(access); + evaluateDeferred(); + return gen.emitConvert2MemoryOp(kind, op, address, state); + } + + @Override + public Value emitAddMemory(ValueNode x, ValueNode y, Access access) { + switch (access.nullCheckLocation().getValueKind()) { + case Int: + return emitBinaryMemory(IADD, true, x, y, access); + case Long: + return emitBinaryMemory(LADD, true, x, y, access); + case Float: + return emitBinaryMemory(FADD, true, x, y, access); + case Double: + return emitBinaryMemory(DADD, true, x, y, access); + default: + return null; + } + } + + @Override + public Value emitSubMemory(ValueNode x, ValueNode y, Access access) { + switch (access.nullCheckLocation().getValueKind()) { + case Int: + return emitBinaryMemory(ISUB, false, x, y, access); + case Long: + return emitBinaryMemory(LSUB, false, x, y, access); + case Float: + return emitBinaryMemory(FSUB, false, x, y, access); + case Double: + return emitBinaryMemory(DSUB, false, x, y, access); + default: + return null; + } + } + + @Override + public Value emitMulMemory(ValueNode x, ValueNode y, Access access) { + switch (access.nullCheckLocation().getValueKind()) { + case Int: + return emitBinaryMemory(IMUL, true, x, y, access); + case Long: + return emitBinaryMemory(LMUL, true, x, y, access); + case Float: + return emitBinaryMemory(FMUL, true, x, y, access); + case Double: + return emitBinaryMemory(DMUL, true, x, y, access); + default: + return null; + } + } + + @Override + public Value emitDivMemory(ValueNode x, ValueNode y, Access access) { + return null; + } + + @Override + public Value emitRemMemory(ValueNode x, ValueNode y, Access access) { + return null; + } + + @Override + public Value emitAndMemory(ValueNode x, ValueNode y, Access access) { + Kind kind = access.nullCheckLocation().getValueKind(); + switch (kind) { + case Int: + return emitBinaryMemory(IAND, true, x, y, access); + case Long: + return emitBinaryMemory(LAND, true, x, y, access); + case Short: { + ValueNode other = x == access ? y : x; + Constant constant = other instanceof ConstantNode ? ((ConstantNode) other).asConstant() : null; + if (constant != null && constant.asInt() == IntegerStamp.defaultMask(kind.getBitCount())) { + // Convert to unsigned load + ensureEvaluated(other); + return emitZeroExtendMemory(16, 32, access); + } + return null; + } + case Byte: { + if (OptFoldMemory.getValue()) { + return null; + } + ValueNode other = x == access ? y : x; + Constant constant = other instanceof ConstantNode ? ((ConstantNode) other).asConstant() : null; + if (constant != null && constant.asInt() == IntegerStamp.defaultMask(kind.getBitCount())) { + // Convert to unsigned load + ensureEvaluated(other); + return emitConvert2MemoryOp(Kind.Int, MOV_B2UI, access); + } + return null; + } + + default: + return null; + } + } + + @Override + public Value emitOrMemory(ValueNode x, ValueNode y, Access access) { + switch (access.nullCheckLocation().getValueKind()) { + case Int: + return emitBinaryMemory(IOR, true, x, y, access); + case Long: + return emitBinaryMemory(LOR, true, x, y, access); + default: + return null; + } + } + + @Override + public Value emitXorMemory(ValueNode x, ValueNode y, Access access) { + switch (access.nullCheckLocation().getValueKind()) { + case Int: + return emitBinaryMemory(IXOR, true, x, y, access); + case Long: + return emitBinaryMemory(LXOR, true, x, y, access); + default: + return null; + } + } + + @Override + public Value emitReinterpretMemory(Stamp stamp, Access access) { + PlatformKind to = gen.getPlatformKind(stamp); + Kind from = access.nullCheckLocation().getValueKind(); + 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(); + } + + @Override + public Value emitFloatConvertMemory(FloatConvert op, Access access) { + switch (op) { + 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(); + } + } + + @Override + public Value emitSignExtendMemory(Access access, int fromBits, int toBits) { + assert fromBits <= toBits && toBits <= 64; + if (fromBits == toBits) { + return null; + } else if (toBits > 32) { + // sign extend to 64 bits + switch (fromBits) { + case 8: + return emitConvert2MemoryOp(Kind.Long, B2L, access); + case 16: + return emitConvert2MemoryOp(Kind.Long, S2L, access); + case 32: + return emitConvert2MemoryOp(Kind.Long, I2L, access); + default: + throw GraalInternalError.unimplemented("unsupported sign extension (" + fromBits + " bit -> " + toBits + " bit)"); + } + } else { + + // sign extend to 32 bits (smaller values are internally represented as 32 bit values) + switch (fromBits) { + case 8: + return emitConvert2MemoryOp(Kind.Int, B2I, access); + case 16: + return emitConvert2MemoryOp(Kind.Int, S2I, access); + case 32: + return null; + default: + throw GraalInternalError.unimplemented("unsupported sign extension (" + fromBits + " bit -> " + toBits + " bit)"); + } + } + } + + @Override + public Value emitNarrowMemory(int resultBits, Access access) { + // TODO + return null; + } + + @Override + public Value emitZeroExtendMemory(int inputBits, int resultBits, Access access) { + assert resultBits == 32 || resultBits == 64; + Kind memoryKind = access.nullCheckLocation().getValueKind(); + if (memoryKind.getBitCount() != inputBits && !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; + } + if (memoryKind == Kind.Short) { + memoryKind = Kind.Char; + } + evaluateDeferred(); + return gen.emitZeroExtendMemory(memoryKind, resultBits, makeAddress(access), getState(access)); + } + + public boolean emitIfMemory(IfNode x, Access access) { + return emitBranchMemory(x.condition(), access, gen.getLIRBlock(x.trueSuccessor()), gen.getLIRBlock(x.falseSuccessor()), x.probability(x.trueSuccessor())); + } + + private boolean emitBranchMemory(LogicNode node, Access access, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { + if (node instanceof IsNullNode) { + // can't do anything interesting. + return false; + } else if (node instanceof CompareNode) { + CompareNode compare = (CompareNode) node; + return emitCompareBranchMemory(compare, access, trueSuccessor, falseSuccessor, trueSuccessorProbability); + } else if (node instanceof LogicConstantNode) { + return false; + } else if (node instanceof IntegerTestNode) { + return emitIntegerTestBranchMemory((IntegerTestNode) node, access, trueSuccessor, falseSuccessor, trueSuccessorProbability); + } else { + throw GraalInternalError.unimplemented(node.toString()); + } + } + + public boolean emitCompareBranchMemory(CompareNode compare, Access access, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { + return emitCompareBranchMemory(compare.x(), compare.y(), access, compare.condition(), compare.unorderedIsTrue(), trueSuccessor, falseSuccessor, trueSuccessorProbability); + } + + public boolean emitIntegerTestBranchMemory(IntegerTestNode test, Access access, LabelRef trueSuccessor, LabelRef falseSuccessor, double trueSuccessorProbability) { + return emitIntegerTestBranchMemory(test.x(), test.y(), access, trueSuccessor, falseSuccessor, trueSuccessorProbability); + } + + private boolean emitIntegerTestBranchMemory(ValueNode left, ValueNode right, Access access, LabelRef trueLabel, LabelRef falseLabel, double trueLabelProbability) { + assert left == access || right == access; + ValueNode other = left == access ? right : left; + Kind kind = access.nullCheckLocation().getValueKind(); + if (other.isConstant()) { + Constant constant = other.asConstant(); + if (kind == Kind.Long && !NumUtil.isInt(constant.asLong())) { + // Only imm32 as long + return false; + } + ensureEvaluated(other); + gen.append(new AMD64TestMemoryOp(makeAddress(access), constant, getState(access))); + } else { + evaluateDeferred(); + gen.append(new AMD64TestMemoryOp(makeAddress(access), gen.operand(other), getState(access))); + } + + gen.append(new BranchOp(Condition.EQ, trueLabel, falseLabel, trueLabelProbability)); + return true; + } + + protected boolean emitCompareBranchMemory(ValueNode left, ValueNode right, Access access, Condition cond, boolean unorderedIsTrue, LabelRef trueLabel, LabelRef falseLabel, + double trueLabelProbability) { + assert left == access || right == access; + ValueNode other = left == access ? right : left; + Kind kind = access.nullCheckLocation().getValueKind(); + boolean mirrored = false; + + if (other.isConstant()) { + Constant constant = other.asConstant(); + if (kind == Kind.Long && !NumUtil.isInt(constant.asLong())) { + // Only imm32 as long + return false; + } + if (kind.isNumericFloat()) { + Debug.log("Skipping constant compares for float kinds"); + return false; + } + if (kind == Kind.Object) { + if (!access.isCompressible() && !constant.isNull()) { + Debug.log("Skipping constant compares for Object kinds"); + return false; + } + } + if (kind != kind.getStackKind()) { + Debug.log("Skipping constant compares for stack kinds"); + return false; + } + ensureEvaluated(other); + gen.emitCompareMemoryConOp(kind, makeAddress(access), constant, getState(access)); + mirrored = right == access; + } else { + if (kind != kind.getStackKind()) { + // Register compares only work for stack kinds + Debug.log("Register compares only work for stack kinds"); + return false; + } else if (kind == Kind.Object) { + // Can't compare against objects since they require encode/decode + Debug.log("Skipping compares for Object kinds"); + return false; + } + + evaluateDeferred(); + gen.emitCompareRegMemoryOp(kind, gen.operand(other), makeAddress(access), getState(access)); + mirrored = left == access; + } + + Condition finalCondition = mirrored ? cond.mirror() : cond; + switch (kind.getStackKind()) { + case Long: + case Int: + case Object: + gen.append(new BranchOp(finalCondition, trueLabel, falseLabel, trueLabelProbability)); + return true; + case Float: + case Double: + gen.append(new FloatBranchOp(finalCondition, unorderedIsTrue, trueLabel, falseLabel, trueLabelProbability)); + return true; + default: + throw GraalInternalError.shouldNotReachHere("" + kind.getStackKind()); + } + } +} diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryArithmeticTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryArithmeticTest.java Thu Mar 20 15:57:03 2014 -0700 @@ -0,0 +1,1525 @@ +/* + * 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.test; + +import java.lang.reflect.*; + +import org.junit.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.nodes.*; + +public class MemoryArithmeticTest extends GraalCompilerTest { + + @Override + protected InstalledCode getCode(ResolvedJavaMethod method, StructuredGraph graph) { + return super.getCode(method, graph, true); + } + + /** + * Called before a test is executed. + */ + @Override + protected void before(Method method) { + // don't let any null exception tracking change the generated code. + getMetaAccess().lookupJavaMethod(method).reprofile(); + } + + /** + * A dummy field used by some tests to create side effects. + */ + protected static int count; + + static class FieldObject { + boolean booleanValue; + byte byteValue; + short shortValue; + char charValue; + int intValue; + float floatValue; + long longValue; + double doubleValue; + Object objectValue; + } + + static final boolean booleanTestValue1 = false; + static final byte byteTestValue1 = 0; + static final short shortTestValue1 = 0; + static final char charTestValue1 = 0; + static final int intTestValue1 = 0; + static final float floatTestValue1 = 0; + static final long longTestValue1 = 0; + static final double doubleTestValue1 = 0; + static final Object objectTestValue1 = null; + + static final boolean booleanTestValue2 = true; + static final byte byteTestValue2 = Byte.MAX_VALUE; + static final short shortTestValue2 = Short.MAX_VALUE; + static final char charTestValue2 = Character.MAX_VALUE; + static final int intTestValue2 = Integer.MAX_VALUE; + static final float floatTestValue2 = Float.MAX_VALUE; + static final long longTestValue2 = Long.MAX_VALUE; + static final double doubleTestValue2 = Double.MAX_VALUE; + static final Object objectTestValue2 = "String"; + + public static Object testBooleanCompare(FieldObject f, boolean booleanValue) { + if (f.booleanValue == booleanValue) { + return f; + } + return null; + } + + public static Object testBooleanCompareConstant1(FieldObject f) { + if (f.booleanValue == booleanTestValue1) { + return f; + } + return null; + } + + public static Object testBooleanCompareConstant2(FieldObject f) { + if (f.booleanValue == booleanTestValue2) { + return f; + } + return null; + } + + @Test + public void testBooleanCompares() { + FieldObject f = new FieldObject(); + test("testBooleanCompare", f, booleanTestValue1); + test("testBooleanCompareConstant1", f); + test("testBooleanCompareConstant2", f); + } + + @Test + public void testBooleanNullCompares() { + test("testBooleanCompare", null, booleanTestValue1); + } + + public static Object testByteCompare(FieldObject f, byte byteValue) { + if (f.byteValue == byteValue) { + return f; + } + return null; + } + + public static Object testByteCompareConstant1(FieldObject f) { + if (f.byteValue == byteTestValue1) { + return f; + } + return null; + } + + public static Object testByteCompareConstant2(FieldObject f) { + if (f.byteValue == byteTestValue2) { + return f; + } + return null; + } + + @Test + public void testByteCompares() { + FieldObject f = new FieldObject(); + test("testByteCompare", f, byteTestValue1); + test("testByteCompareConstant1", f); + test("testByteCompareConstant2", f); + } + + @Test + public void testByteNullCompares() { + test("testByteCompare", null, byteTestValue1); + } + + public static Object testShortCompare(FieldObject f, short shortValue) { + if (f.shortValue == shortValue) { + return f; + } + return null; + } + + public static Object testShortCompareConstant1(FieldObject f) { + if (f.shortValue == shortTestValue1) { + return f; + } + return null; + } + + public static Object testShortCompareConstant2(FieldObject f) { + if (f.shortValue == shortTestValue2) { + return f; + } + return null; + } + + @Test + public void testShortCompares() { + FieldObject f = new FieldObject(); + test("testShortCompare", f, shortTestValue1); + test("testShortCompareConstant1", f); + test("testShortCompareConstant2", f); + } + + @Test + public void testShortNullCompares() { + test("testShortCompare", null, shortTestValue1); + } + + public static Object testCharCompare(FieldObject f, char charValue) { + if (f.charValue == charValue) { + return f; + } + return null; + } + + public static Object testCharCompareConstant1(FieldObject f) { + if (f.charValue == charTestValue1) { + return f; + } + return null; + } + + public static Object testCharCompareConstant2(FieldObject f) { + if (f.charValue == charTestValue2) { + return f; + } + return null; + } + + @Test + public void testCharCompares() { + FieldObject f = new FieldObject(); + test("testCharCompare", f, charTestValue1); + test("testCharCompareConstant1", f); + test("testCharCompareConstant2", f); + } + + @Test + public void testCharNullCompares() { + test("testCharCompare", null, charTestValue1); + } + + public static Object testIntCompare(FieldObject f, int intValue) { + if (f.intValue == intValue) { + return f; + } + return null; + } + + public static Object testIntCompareConstant1(FieldObject f) { + if (f.intValue == intTestValue1) { + return f; + } + return null; + } + + public static Object testIntCompareConstant2(FieldObject f) { + if (f.intValue == intTestValue2) { + return f; + } + return null; + } + + @Test + public void testIntCompares() { + FieldObject f = new FieldObject(); + test("testIntCompare", f, intTestValue1); + test("testIntCompareConstant1", f); + test("testIntCompareConstant2", f); + } + + @Test + public void testIntNullCompares() { + test("testIntCompare", null, intTestValue1); + } + + public static Object testFloatCompare(FieldObject f, float floatValue) { + if (f.floatValue == floatValue) { + return f; + } + return null; + } + + public static Object testFloatCompareConstant1(FieldObject f) { + if (f.floatValue == floatTestValue1) { + return f; + } + return null; + } + + public static Object testFloatCompareConstant2(FieldObject f) { + if (f.floatValue == floatTestValue2) { + return f; + } + return null; + } + + @Test + public void testFloatCompares() { + FieldObject f = new FieldObject(); + test("testFloatCompare", f, floatTestValue1); + test("testFloatCompareConstant1", f); + test("testFloatCompareConstant2", f); + } + + @Test + public void testFloatNullCompares() { + test("testFloatCompare", null, floatTestValue1); + } + + public static Object testLongCompare(FieldObject f, long longValue) { + if (f.longValue == longValue) { + return f; + } + return null; + } + + public static Object testLongCompareConstant1(FieldObject f) { + if (f.longValue == longTestValue1) { + return f; + } + return null; + } + + public static Object testLongCompareConstant2(FieldObject f) { + if (f.longValue == longTestValue2) { + return f; + } + return null; + } + + @Test + public void testLongCompares() { + FieldObject f = new FieldObject(); + test("testLongCompare", f, longTestValue1); + test("testLongCompareConstant1", f); + test("testLongCompareConstant2", f); + } + + @Test + public void testLongNullCompares() { + test("testLongCompare", null, longTestValue1); + } + + public static Object testDoubleCompare(FieldObject f, double doubleValue) { + if (f.doubleValue == doubleValue) { + return f; + } + return null; + } + + public static Object testDoubleCompareConstant1(FieldObject f) { + if (f.doubleValue == doubleTestValue1) { + return f; + } + return null; + } + + public static Object testDoubleCompareConstant2(FieldObject f) { + if (f.doubleValue == doubleTestValue2) { + return f; + } + return null; + } + + @Test + public void testDoubleCompares() { + FieldObject f = new FieldObject(); + test("testDoubleCompare", f, doubleTestValue1); + test("testDoubleCompareConstant1", f); + test("testDoubleCompareConstant2", f); + } + + @Test + public void testDoubleNullCompares() { + test("testDoubleCompare", null, doubleTestValue1); + } + + public static Object testObjectCompare(FieldObject f, Object objectValue) { + if (f.objectValue == objectValue) { + return f; + } + return null; + } + + public static Object testObjectCompareConstant1(FieldObject f) { + if (f.objectValue == objectTestValue1) { + return f; + } + return null; + } + + public static Object testObjectCompareConstant2(FieldObject f) { + if (f.objectValue == objectTestValue2) { + return f; + } + return null; + } + + @Test + public void testObjectCompares() { + FieldObject f = new FieldObject(); + test("testObjectCompare", f, objectTestValue1); + test("testObjectCompareConstant1", f); + test("testObjectCompareConstant2", f); + } + + @Test + public void testObjectNullCompares() { + test("testObjectCompare", null, objectTestValue1); + } + + public static int testByteAdd(FieldObject f, byte byteValue) { + return f.byteValue + byteValue; + } + + public static int testByteAddConstant1(FieldObject f) { + return f.byteValue + byteTestValue1; + } + + public static int testByteAddConstant2(FieldObject f) { + return f.byteValue + byteTestValue2; + } + + @Test + public void testByteAdds() { + FieldObject f = new FieldObject(); + test("testByteAdd", f, byteTestValue1); + test("testByteAddConstant1", f); + test("testByteAddConstant2", f); + } + + @Test + public void testByteNullAdd() { + test("testByteAdd", null, byteTestValue1); + } + + public static int testShortAdd(FieldObject f, short shortValue) { + return f.shortValue + shortValue; + } + + public static int testShortAddConstant1(FieldObject f) { + return f.shortValue + shortTestValue1; + } + + public static int testShortAddConstant2(FieldObject f) { + return f.shortValue + shortTestValue2; + } + + @Test + public void testShortAdds() { + FieldObject f = new FieldObject(); + test("testShortAdd", f, shortTestValue1); + test("testShortAddConstant1", f); + test("testShortAddConstant2", f); + } + + @Test + public void testShortNullAdd() { + test("testShortAdd", null, shortTestValue1); + } + + public static int testCharAdd(FieldObject f, char charValue) { + return f.charValue + charValue; + } + + public static int testCharAddConstant1(FieldObject f) { + return f.charValue + charTestValue1; + } + + public static int testCharAddConstant2(FieldObject f) { + return f.charValue + charTestValue2; + } + + @Test + public void testCharAdds() { + FieldObject f = new FieldObject(); + test("testCharAdd", f, charTestValue1); + test("testCharAddConstant1", f); + test("testCharAddConstant2", f); + } + + @Test + public void testCharNullAdd() { + test("testCharAdd", null, charTestValue1); + } + + public static int testIntAdd(FieldObject f, int intValue) { + return f.intValue + intValue; + } + + public static int testIntAddConstant1(FieldObject f) { + return f.intValue + intTestValue1; + } + + public static int testIntAddConstant2(FieldObject f) { + return f.intValue + intTestValue2; + } + + @Test + public void testIntAdds() { + FieldObject f = new FieldObject(); + test("testIntAdd", f, intTestValue1); + test("testIntAddConstant1", f); + test("testIntAddConstant2", f); + } + + @Test + public void testIntNullAdd() { + test("testIntAdd", null, intTestValue1); + } + + public static long testLongAdd(FieldObject f, long longValue) { + return f.longValue + longValue; + } + + public static long testLongAddConstant1(FieldObject f) { + return f.longValue + longTestValue1; + } + + public static long testLongAddConstant2(FieldObject f) { + return f.longValue + longTestValue2; + } + + @Test + public void testLongAdds() { + FieldObject f = new FieldObject(); + test("testLongAdd", f, longTestValue1); + test("testLongAddConstant1", f); + test("testLongAddConstant2", f); + } + + @Test + public void testLongNullAdd() { + test("testLongAdd", null, longTestValue1); + } + + public static float testFloatAdd(FieldObject f, float floatValue) { + return f.floatValue + floatValue; + } + + public static float testFloatAddConstant1(FieldObject f) { + return f.floatValue + floatTestValue1; + } + + public static float testFloatAddConstant2(FieldObject f) { + return f.floatValue + floatTestValue2; + } + + @Test + public void testFloatAdds() { + FieldObject f = new FieldObject(); + test("testFloatAdd", f, floatTestValue1); + test("testFloatAddConstant1", f); + test("testFloatAddConstant2", f); + } + + @Test + public void testFloatNullAdd() { + test("testFloatAdd", null, floatTestValue1); + } + + public static double testDoubleAdd(FieldObject f, double doubleValue) { + return f.doubleValue + doubleValue; + } + + public static double testDoubleAddConstant1(FieldObject f) { + return f.doubleValue + doubleTestValue1; + } + + public static double testDoubleAddConstant2(FieldObject f) { + return f.doubleValue + doubleTestValue2; + } + + @Test + public void testDoubleAdds() { + FieldObject f = new FieldObject(); + test("testDoubleAdd", f, doubleTestValue1); + test("testDoubleAddConstant1", f); + test("testDoubleAddConstant2", f); + } + + @Test + public void testDoubleNullAdd() { + test("testDoubleAdd", null, doubleTestValue1); + } + + public static int testByteSub(FieldObject f, byte byteValue) { + return f.byteValue - byteValue; + } + + public static int testByteSubConstant1(FieldObject f) { + return f.byteValue - byteTestValue1; + } + + public static int testByteSubConstant2(FieldObject f) { + return f.byteValue - byteTestValue2; + } + + @Test + public void testByteSubs() { + FieldObject f = new FieldObject(); + test("testByteSub", f, byteTestValue1); + test("testByteSubConstant1", f); + test("testByteSubConstant2", f); + } + + @Test + public void testByteNullSub() { + test("testByteSub", null, byteTestValue1); + } + + public static int testShortSub(FieldObject f, short shortValue) { + return f.shortValue - shortValue; + } + + public static int testShortSubConstant1(FieldObject f) { + return f.shortValue - shortTestValue1; + } + + public static int testShortSubConstant2(FieldObject f) { + return f.shortValue - shortTestValue2; + } + + @Test + public void testShortSubs() { + FieldObject f = new FieldObject(); + test("testShortSub", f, shortTestValue1); + test("testShortSubConstant1", f); + test("testShortSubConstant2", f); + } + + @Test + public void testShortNullSub() { + test("testShortSub", null, shortTestValue1); + } + + public static int testCharSub(FieldObject f, char charValue) { + return f.charValue - charValue; + } + + public static int testCharSubConstant1(FieldObject f) { + return f.charValue - charTestValue1; + } + + public static int testCharSubConstant2(FieldObject f) { + return f.charValue - charTestValue2; + } + + @Test + public void testCharSubs() { + FieldObject f = new FieldObject(); + test("testCharSub", f, charTestValue1); + test("testCharSubConstant1", f); + test("testCharSubConstant2", f); + } + + @Test + public void testCharNullSub() { + test("testCharSub", null, charTestValue1); + } + + public static int testIntSub(FieldObject f, int intValue) { + return f.intValue - intValue; + } + + public static int testIntSubConstant1(FieldObject f) { + return f.intValue - intTestValue1; + } + + public static int testIntSubConstant2(FieldObject f) { + return f.intValue - intTestValue2; + } + + @Test + public void testIntSubs() { + FieldObject f = new FieldObject(); + test("testIntSub", f, intTestValue1); + test("testIntSubConstant1", f); + test("testIntSubConstant2", f); + } + + @Test + public void testIntNullSub() { + test("testIntSub", null, intTestValue1); + } + + public static long testLongSub(FieldObject f, long longValue) { + return f.longValue - longValue; + } + + public static long testLongSubConstant1(FieldObject f) { + return f.longValue - longTestValue1; + } + + public static long testLongSubConstant2(FieldObject f) { + return f.longValue - longTestValue2; + } + + @Test + public void testLongSubs() { + FieldObject f = new FieldObject(); + test("testLongSub", f, longTestValue1); + test("testLongSubConstant1", f); + test("testLongSubConstant2", f); + } + + @Test + public void testLongNullSub() { + test("testLongSub", null, longTestValue1); + } + + public static float testFloatSub(FieldObject f, float floatValue) { + return f.floatValue - floatValue; + } + + public static float testFloatSubConstant1(FieldObject f) { + return f.floatValue - floatTestValue1; + } + + public static float testFloatSubConstant2(FieldObject f) { + return f.floatValue - floatTestValue2; + } + + @Test + public void testFloatSubs() { + FieldObject f = new FieldObject(); + test("testFloatSub", f, floatTestValue1); + test("testFloatSubConstant1", f); + test("testFloatSubConstant2", f); + } + + @Test + public void testFloatNullSub() { + test("testFloatSub", null, floatTestValue1); + } + + public static double testDoubleSub(FieldObject f, double doubleValue) { + return f.doubleValue - doubleValue; + } + + public static double testDoubleSubConstant1(FieldObject f) { + return f.doubleValue - doubleTestValue1; + } + + public static double testDoubleSubConstant2(FieldObject f) { + return f.doubleValue - doubleTestValue2; + } + + @Test + public void testDoubleSubs() { + FieldObject f = new FieldObject(); + test("testDoubleSub", f, doubleTestValue1); + test("testDoubleSubConstant1", f); + test("testDoubleSubConstant2", f); + } + + @Test + public void testDoubleNullSub() { + test("testDoubleSub", null, doubleTestValue1); + } + + public static int testByteMul(FieldObject f, byte byteValue) { + return f.byteValue * byteValue; + } + + public static int testByteMulConstant1(FieldObject f) { + return f.byteValue * byteTestValue1; + } + + public static int testByteMulConstant2(FieldObject f) { + return f.byteValue * byteTestValue2; + } + + @Test + public void testByteMuls() { + FieldObject f = new FieldObject(); + test("testByteMul", f, byteTestValue1); + test("testByteMulConstant1", f); + test("testByteMulConstant2", f); + } + + @Test + public void testByteNullMul() { + test("testByteMul", null, byteTestValue1); + } + + public static int testShortMul(FieldObject f, short shortValue) { + return f.shortValue * shortValue; + } + + public static int testShortMulConstant1(FieldObject f) { + return f.shortValue * shortTestValue1; + } + + public static int testShortMulConstant2(FieldObject f) { + return f.shortValue * shortTestValue2; + } + + @Test + public void testShortMuls() { + FieldObject f = new FieldObject(); + test("testShortMul", f, shortTestValue1); + test("testShortMulConstant1", f); + test("testShortMulConstant2", f); + } + + @Test + public void testShortNullMul() { + test("testShortMul", null, shortTestValue1); + } + + public static int testCharMul(FieldObject f, char charValue) { + return f.charValue * charValue; + } + + public static int testCharMulConstant1(FieldObject f) { + return f.charValue * charTestValue1; + } + + public static int testCharMulConstant2(FieldObject f) { + return f.charValue * charTestValue2; + } + + @Test + public void testCharMuls() { + FieldObject f = new FieldObject(); + test("testCharMul", f, charTestValue1); + test("testCharMulConstant1", f); + test("testCharMulConstant2", f); + } + + @Test + public void testCharNullMul() { + test("testCharMul", null, charTestValue1); + } + + public static int testIntMul(FieldObject f, int intValue) { + return f.intValue * intValue; + } + + public static int testIntMulConstant1(FieldObject f) { + return f.intValue * intTestValue1; + } + + public static int testIntMulConstant2(FieldObject f) { + return f.intValue * intTestValue2; + } + + @Test + public void testIntMuls() { + FieldObject f = new FieldObject(); + test("testIntMul", f, intTestValue1); + test("testIntMulConstant1", f); + test("testIntMulConstant2", f); + } + + @Test + public void testIntNullMul() { + test("testIntMul", null, intTestValue1); + } + + public static long testLongMul(FieldObject f, long longValue) { + return f.longValue * longValue; + } + + public static long testLongMulConstant1(FieldObject f) { + return f.longValue * longTestValue1; + } + + public static long testLongMulConstant2(FieldObject f) { + return f.longValue * longTestValue2; + } + + @Test + public void testLongMuls() { + FieldObject f = new FieldObject(); + test("testLongMul", f, longTestValue1); + test("testLongMulConstant1", f); + test("testLongMulConstant2", f); + } + + @Test + public void testLongNullMul() { + test("testLongMul", null, longTestValue1); + } + + public static float testFloatMul(FieldObject f, float floatValue) { + return f.floatValue * floatValue; + } + + public static float testFloatMulConstant1(FieldObject f) { + return f.floatValue * floatTestValue1; + } + + public static float testFloatMulConstant2(FieldObject f) { + return f.floatValue * floatTestValue2; + } + + @Test + public void testFloatMuls() { + FieldObject f = new FieldObject(); + test("testFloatMul", f, floatTestValue1); + test("testFloatMulConstant1", f); + test("testFloatMulConstant2", f); + } + + @Test + public void testFloatNullMul() { + test("testFloatMul", null, floatTestValue1); + } + + public static double testDoubleMul(FieldObject f, double doubleValue) { + return f.doubleValue * doubleValue; + } + + public static double testDoubleMulConstant1(FieldObject f) { + return f.doubleValue * doubleTestValue1; + } + + public static double testDoubleMulConstant2(FieldObject f) { + return f.doubleValue * doubleTestValue2; + } + + @Test + public void testDoubleMuls() { + FieldObject f = new FieldObject(); + test("testDoubleMul", f, doubleTestValue1); + test("testDoubleMulConstant1", f); + test("testDoubleMulConstant2", f); + } + + @Test + public void testDoubleNullMul() { + test("testDoubleMul", null, doubleTestValue1); + } + + public static int testByteDiv(FieldObject f, byte byteValue) { + return f.byteValue / byteValue; + } + + public static int testByteDivConstant1(FieldObject f) { + return f.byteValue / byteTestValue1; + } + + public static int testByteDivConstant2(FieldObject f) { + return f.byteValue / byteTestValue2; + } + + @Test + public void testByteDivs() { + FieldObject f = new FieldObject(); + test("testByteDiv", f, byteTestValue1); + test("testByteDivConstant1", f); + test("testByteDivConstant2", f); + } + + @Test + public void testByteNullDiv() { + test("testByteDiv", null, byteTestValue1); + } + + public static int testShortDiv(FieldObject f, short shortValue) { + return f.shortValue / shortValue; + } + + public static int testShortDivConstant1(FieldObject f) { + return f.shortValue / shortTestValue1; + } + + public static int testShortDivConstant2(FieldObject f) { + return f.shortValue / shortTestValue2; + } + + @Test + public void testShortDivs() { + FieldObject f = new FieldObject(); + test("testShortDiv", f, shortTestValue1); + test("testShortDivConstant1", f); + test("testShortDivConstant2", f); + } + + @Test + public void testShortNullDiv() { + test("testShortDiv", null, shortTestValue1); + } + + public static int testCharDiv(FieldObject f, char charValue) { + return f.charValue / charValue; + } + + public static int testCharDivConstant1(FieldObject f) { + return f.charValue / charTestValue1; + } + + public static int testCharDivConstant2(FieldObject f) { + return f.charValue / charTestValue2; + } + + @Test + public void testCharDivs() { + FieldObject f = new FieldObject(); + test("testCharDiv", f, charTestValue1); + test("testCharDivConstant1", f); + test("testCharDivConstant2", f); + } + + @Test + public void testCharNullDiv() { + test("testCharDiv", null, charTestValue1); + } + + public static int testIntDiv(FieldObject f, int intValue) { + return f.intValue / intValue; + } + + public static int testIntDivConstant1(FieldObject f) { + return f.intValue / intTestValue1; + } + + public static int testIntDivConstant2(FieldObject f) { + return f.intValue / intTestValue2; + } + + @Test + public void testIntDivs() { + FieldObject f = new FieldObject(); + test("testIntDiv", f, intTestValue1); + test("testIntDivConstant1", f); + test("testIntDivConstant2", f); + } + + @Test + public void testIntNullDiv() { + test("testIntDiv", null, intTestValue1); + } + + public static long testLongDiv(FieldObject f, long longValue) { + return f.longValue / longValue; + } + + public static long testLongDivConstant1(FieldObject f) { + return f.longValue / longTestValue1; + } + + public static long testLongDivConstant2(FieldObject f) { + return f.longValue / longTestValue2; + } + + @Test + public void testLongDivs() { + FieldObject f = new FieldObject(); + test("testLongDiv", f, longTestValue1); + test("testLongDivConstant1", f); + test("testLongDivConstant2", f); + } + + @Test + public void testLongNullDiv() { + test("testLongDiv", null, longTestValue1); + } + + public static float testFloatDiv(FieldObject f, float floatValue) { + return f.floatValue / floatValue; + } + + public static float testFloatDivConstant1(FieldObject f) { + return f.floatValue / floatTestValue1; + } + + public static float testFloatDivConstant2(FieldObject f) { + return f.floatValue / floatTestValue2; + } + + @Test + public void testFloatDivs() { + FieldObject f = new FieldObject(); + test("testFloatDiv", f, floatTestValue1); + test("testFloatDivConstant1", f); + test("testFloatDivConstant2", f); + } + + @Test + public void testFloatNullDiv() { + test("testFloatDiv", null, floatTestValue1); + } + + public static double testDoubleDiv(FieldObject f, double doubleValue) { + return f.doubleValue / doubleValue; + } + + public static double testDoubleDivConstant1(FieldObject f) { + return f.doubleValue / doubleTestValue1; + } + + public static double testDoubleDivConstant2(FieldObject f) { + return f.doubleValue / doubleTestValue2; + } + + @Test + public void testDoubleDivs() { + FieldObject f = new FieldObject(); + test("testDoubleDiv", f, doubleTestValue1); + test("testDoubleDivConstant1", f); + test("testDoubleDivConstant2", f); + } + + @Test + public void testDoubleNullDiv() { + test("testDoubleDiv", null, doubleTestValue1); + } + + public static int testByteOr(FieldObject f, byte byteValue) { + return f.byteValue | byteValue; + } + + public static int testByteOrConstant1(FieldObject f) { + return f.byteValue | byteTestValue1; + } + + public static int testByteOrConstant2(FieldObject f) { + return f.byteValue | byteTestValue2; + } + + @Test + public void testByteOrs() { + FieldObject f = new FieldObject(); + test("testByteOr", f, byteTestValue1); + test("testByteOrConstant1", f); + test("testByteOrConstant2", f); + } + + @Test + public void testByteNullOr() { + test("testByteOr", null, byteTestValue1); + } + + public static int testShortOr(FieldObject f, short shortValue) { + return f.shortValue | shortValue; + } + + public static int testShortOrConstant1(FieldObject f) { + return f.shortValue | shortTestValue1; + } + + public static int testShortOrConstant2(FieldObject f) { + return f.shortValue | shortTestValue2; + } + + @Test + public void testShortOrs() { + FieldObject f = new FieldObject(); + test("testShortOr", f, shortTestValue1); + test("testShortOrConstant1", f); + test("testShortOrConstant2", f); + } + + @Test + public void testShortNullOr() { + test("testShortOr", null, shortTestValue1); + } + + public static int testCharOr(FieldObject f, char charValue) { + return f.charValue | charValue; + } + + public static int testCharOrConstant1(FieldObject f) { + return f.charValue | charTestValue1; + } + + public static int testCharOrConstant2(FieldObject f) { + return f.charValue | charTestValue2; + } + + @Test + public void testCharOrs() { + FieldObject f = new FieldObject(); + test("testCharOr", f, charTestValue1); + test("testCharOrConstant1", f); + test("testCharOrConstant2", f); + } + + @Test + public void testCharNullOr() { + test("testCharOr", null, charTestValue1); + } + + public static int testIntOr(FieldObject f, int intValue) { + return f.intValue | intValue; + } + + public static int testIntOrConstant1(FieldObject f) { + return f.intValue | intTestValue1; + } + + public static int testIntOrConstant2(FieldObject f) { + return f.intValue | intTestValue2; + } + + @Test + public void testIntOrs() { + FieldObject f = new FieldObject(); + test("testIntOr", f, intTestValue1); + test("testIntOrConstant1", f); + test("testIntOrConstant2", f); + } + + @Test + public void testIntNullOr() { + test("testIntOr", null, intTestValue1); + } + + public static long testLongOr(FieldObject f, long longValue) { + return f.longValue | longValue; + } + + public static long testLongOrConstant1(FieldObject f) { + return f.longValue | longTestValue1; + } + + public static long testLongOrConstant2(FieldObject f) { + return f.longValue | longTestValue2; + } + + @Test + public void testLongOrs() { + FieldObject f = new FieldObject(); + test("testLongOr", f, longTestValue1); + test("testLongOrConstant1", f); + test("testLongOrConstant2", f); + } + + @Test + public void testLongNullOr() { + test("testLongOr", null, longTestValue1); + } + + public static int testByteXor(FieldObject f, byte byteValue) { + return f.byteValue ^ byteValue; + } + + public static int testByteXorConstant1(FieldObject f) { + return f.byteValue ^ byteTestValue1; + } + + public static int testByteXorConstant2(FieldObject f) { + return f.byteValue ^ byteTestValue2; + } + + @Test + public void testByteXors() { + FieldObject f = new FieldObject(); + test("testByteXor", f, byteTestValue1); + test("testByteXorConstant1", f); + test("testByteXorConstant2", f); + } + + @Test + public void testByteNullXor() { + test("testByteXor", null, byteTestValue1); + } + + public static int testShortXor(FieldObject f, short shortValue) { + return f.shortValue ^ shortValue; + } + + public static int testShortXorConstant1(FieldObject f) { + return f.shortValue ^ shortTestValue1; + } + + public static int testShortXorConstant2(FieldObject f) { + return f.shortValue ^ shortTestValue2; + } + + @Test + public void testShortXors() { + FieldObject f = new FieldObject(); + test("testShortXor", f, shortTestValue1); + test("testShortXorConstant1", f); + test("testShortXorConstant2", f); + } + + @Test + public void testShortNullXor() { + test("testShortXor", null, shortTestValue1); + } + + public static int testCharXor(FieldObject f, char charValue) { + return f.charValue ^ charValue; + } + + public static int testCharXorConstant1(FieldObject f) { + return f.charValue ^ charTestValue1; + } + + public static int testCharXorConstant2(FieldObject f) { + return f.charValue ^ charTestValue2; + } + + @Test + public void testCharXors() { + FieldObject f = new FieldObject(); + test("testCharXor", f, charTestValue1); + test("testCharXorConstant1", f); + test("testCharXorConstant2", f); + } + + @Test + public void testCharNullXor() { + test("testCharXor", null, charTestValue1); + } + + public static int testIntXor(FieldObject f, int intValue) { + return f.intValue ^ intValue; + } + + public static int testIntXorConstant1(FieldObject f) { + return f.intValue ^ intTestValue1; + } + + public static int testIntXorConstant2(FieldObject f) { + return f.intValue ^ intTestValue2; + } + + @Test + public void testIntXors() { + FieldObject f = new FieldObject(); + test("testIntXor", f, intTestValue1); + test("testIntXorConstant1", f); + test("testIntXorConstant2", f); + } + + @Test + public void testIntNullXor() { + test("testIntXor", null, intTestValue1); + } + + public static long testLongXor(FieldObject f, long longValue) { + return f.longValue ^ longValue; + } + + public static long testLongXorConstant1(FieldObject f) { + return f.longValue ^ longTestValue1; + } + + public static long testLongXorConstant2(FieldObject f) { + return f.longValue ^ longTestValue2; + } + + @Test + public void testLongXors() { + FieldObject f = new FieldObject(); + test("testLongXor", f, longTestValue1); + test("testLongXorConstant1", f); + test("testLongXorConstant2", f); + } + + @Test + public void testLongNullXor() { + test("testLongXor", null, longTestValue1); + } + + public static int testByteAnd(FieldObject f, byte byteValue) { + return f.byteValue & byteValue; + } + + public static int testByteAndConstant1(FieldObject f) { + return f.byteValue & byteTestValue1; + } + + public static int testByteAndConstant2(FieldObject f) { + return f.byteValue & byteTestValue2; + } + + @Test + public void testByteAnds() { + FieldObject f = new FieldObject(); + test("testByteAnd", f, byteTestValue1); + test("testByteAndConstant1", f); + test("testByteAndConstant2", f); + } + + @Test + public void testByteNullAnd() { + test("testByteAnd", null, byteTestValue1); + } + + public static int testShortAnd(FieldObject f, short shortValue) { + return f.shortValue & shortValue; + } + + public static int testShortAndConstant1(FieldObject f) { + return f.shortValue & shortTestValue1; + } + + public static int testShortAndConstant2(FieldObject f) { + return f.shortValue & shortTestValue2; + } + + @Test + public void testShortAnds() { + FieldObject f = new FieldObject(); + test("testShortAnd", f, shortTestValue1); + test("testShortAndConstant1", f); + test("testShortAndConstant2", f); + } + + @Test + public void testShortNullAnd() { + test("testShortAnd", null, shortTestValue1); + } + + public static int testCharAnd(FieldObject f, char charValue) { + return f.charValue & charValue; + } + + public static int testCharAndConstant1(FieldObject f) { + return f.charValue & charTestValue1; + } + + public static int testCharAndConstant2(FieldObject f) { + return f.charValue & charTestValue2; + } + + @Test + public void testCharAnds() { + FieldObject f = new FieldObject(); + test("testCharAnd", f, charTestValue1); + test("testCharAndConstant1", f); + test("testCharAndConstant2", f); + } + + @Test + public void testCharNullAnd() { + test("testCharAnd", null, charTestValue1); + } + + public static int testIntAnd(FieldObject f, int intValue) { + return f.intValue & intValue; + } + + public static int testIntAndConstant1(FieldObject f) { + return f.intValue & intTestValue1; + } + + public static int testIntAndConstant2(FieldObject f) { + return f.intValue & intTestValue2; + } + + @Test + public void testIntAnds() { + FieldObject f = new FieldObject(); + test("testIntAnd", f, intTestValue1); + test("testIntAndConstant1", f); + test("testIntAndConstant2", f); + } + + @Test + public void testIntNullAnd() { + test("testIntAnd", null, intTestValue1); + } + + public static long testLongAnd(FieldObject f, long longValue) { + return f.longValue & longValue; + } + + public static long testLongAndConstant1(FieldObject f) { + return f.longValue & longTestValue1; + } + + public static long testLongAndConstant2(FieldObject f) { + return f.longValue & longTestValue2; + } + + @Test + public void testLongAnds() { + FieldObject f = new FieldObject(); + test("testLongAnd", f, longTestValue1); + test("testLongAndConstant1", f); + test("testLongAndConstant2", f); + } + + @Test + public void testLongNullAnd() { + test("testLongAnd", null, longTestValue1); + } + + public static boolean testIntMask(FieldObject f, int intValue) { + if ((f.intValue & intValue) != 0) { + count++; + return false; + } + return true; + } + + public static boolean testIntMaskConstant1(FieldObject f) { + return (f.intValue & intTestValue1) != 0; + } + + public static boolean testIntMaskConstant2(FieldObject f) { + return (f.intValue & intTestValue2) != 0; + } + + @Test + public void testIntMasks() { + FieldObject f = new FieldObject(); + test("testIntMask", f, intTestValue1); + test("testIntMaskConstant1", f); + test("testIntMaskConstant2", f); + } + + @Test + public void testIntNullMask() { + test("testIntMask", null, intTestValue1); + } + + public static boolean testLongMask(FieldObject f, long longValue) { + if ((f.longValue & longValue) != 0) { + count++; + return false; + } + return true; + } + + public static boolean testLongMaskConstant1(FieldObject f) { + return (f.longValue & longTestValue1) != 0; + } + + public static boolean testLongMaskConstant2(FieldObject f) { + return (f.longValue & longTestValue2) != 0; + } + + @Test + public void testLongMasks() { + FieldObject f = new FieldObject(); + test("testLongMask", f, longTestValue1); + test("testLongMaskConstant1", f); + test("testLongMaskConstant2", f); + } + + @Test + public void testLongNullMask() { + test("testLongMask", null, longTestValue1); + } + +} diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Thu Mar 20 15:57:03 2014 -0700 @@ -37,6 +37,7 @@ import com.oracle.graal.asm.*; import com.oracle.graal.compiler.target.*; import com.oracle.graal.debug.*; +import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.graph.*; import com.oracle.graal.lir.*; import com.oracle.graal.lir.StandardOp.BlockEndOp; @@ -227,18 +228,30 @@ /** * Returns the operand that has been previously initialized by - * {@link #setResult(ValueNode, Value)} with the result of an instruction. + * {@link #setResult(ValueNode, Value)} with the result of an instruction. It's a code + * generation error to ask for the operand of ValueNode that doesn't have one yet. * * @param node A node that produces a result value. */ @Override public Value operand(ValueNode node) { + Value operand = getOperand(node); + assert operand != null : String.format("missing operand for %1s", node); + return operand; + } + + @Override + public boolean hasOperand(ValueNode node) { + return getOperand(node) != null; + } + + private Value getOperand(ValueNode node) { if (nodeOperands == null) { return null; } Value operand = nodeOperands.get(node); if (operand == null) { - return getConstantOperand(node); + operand = getConstantOperand(node); } return operand; } @@ -434,23 +447,31 @@ } List nodes = blockMap.get(block); + int instructionsFolded = 0; for (int i = 0; i < nodes.size(); i++) { Node instr = nodes.get(i); if (traceLevel >= 3) { TTY.println("LIRGen for " + instr); } + if (instructionsFolded > 0) { + instructionsFolded--; + continue; + } if (!ConstantNodeRecordsUsages && instr instanceof ConstantNode) { // Loading of constants is done lazily by operand() } else if (instr instanceof ValueNode) { ValueNode valueNode = (ValueNode) instr; - if (operand(valueNode) == null) { + if (!hasOperand(valueNode)) { if (!peephole(valueNode)) { - try { - doRoot((ValueNode) instr); - } catch (GraalInternalError e) { - throw e.addContext(instr); - } catch (Throwable e) { - throw new GraalInternalError(e).addContext(instr); + instructionsFolded = maybeFoldMemory(nodes, i, valueNode); + if (instructionsFolded == 0) { + try { + doRoot((ValueNode) instr); + } catch (GraalInternalError e) { + throw e.addContext(instr); + } catch (Throwable e) { + throw new GraalInternalError(e).addContext(instr); + } } } } else { @@ -486,6 +507,138 @@ } } + private static final DebugMetric MemoryFoldSuccess = Debug.metric("MemoryFoldSuccess"); + private static final DebugMetric MemoryFoldFailed = Debug.metric("MemoryFoldFailed"); + private static final DebugMetric MemoryFoldFailedNonAdjacent = Debug.metric("MemoryFoldedFailedNonAdjacent"); + private static final DebugMetric MemoryFoldFailedDifferentBlock = Debug.metric("MemoryFoldedFailedDifferentBlock"); + + /** + * Subclass can provide helper to fold memory operations into other operations. + */ + protected MemoryArithmeticLIRLowerer getMemoryLowerer() { + return null; + } + + /** + * Try to find a sequence of Nodes which can be passed to the backend to look for optimized + * instruction sequences using memory. Currently this basically is a read with a single + * arithmetic user followed by an possible if use. This should generalized to more generic + * pattern matching so that it can be more flexibly used. + */ + private int maybeFoldMemory(List nodes, int i, ValueNode access) { + MemoryArithmeticLIRLowerer lowerer = getMemoryLowerer(); + if (lowerer != null && OptFoldMemory.getValue() && (access instanceof ReadNode || access instanceof FloatingReadNode) && access.usages().count() == 1 && i + 1 < nodes.size()) { + try (Scope s = Debug.scope("MaybeFoldMemory", access)) { + // This is all bit hacky since it's happening on the linearized schedule. This needs + // to be revisited at some point. + + // Find a memory lowerable usage of this operation + if (access.usages().first() instanceof MemoryArithmeticLIRLowerable) { + ValueNode operation = (ValueNode) access.usages().first(); + if (!nodes.contains(operation)) { + Debug.log("node %1s in different block from %1s", access, operation); + MemoryFoldFailedDifferentBlock.increment(); + return 0; + } + ValueNode firstOperation = operation; + if (operation instanceof LogicNode) { + if (operation.usages().count() == 1 && operation.usages().first() instanceof IfNode) { + ValueNode ifNode = (ValueNode) operation.usages().first(); + if (!nodes.contains(ifNode)) { + MemoryFoldFailedDifferentBlock.increment(); + Debug.log("if node %1s in different block from %1s", ifNode, operation); + try (Indent indent = Debug.logAndIndent("checking operations")) { + int start = nodes.indexOf(access); + int end = nodes.indexOf(operation); + for (int i1 = Math.min(start, end); i1 <= Math.max(start, end); i1++) { + indent.log("%d: (%d) %1s", i1, nodes.get(i1).usages().count(), nodes.get(i1)); + } + } + return 0; + } else { + operation = ifNode; + } + } + } + if (Debug.isLogEnabled()) { + synchronized ("lock") { // Hack to ensure the output is grouped. + try (Indent indent = Debug.logAndIndent("checking operations")) { + int start = nodes.indexOf(access); + int end = nodes.indexOf(operation); + for (int i1 = Math.min(start, end); i1 <= Math.max(start, end); i1++) { + indent.log("%d: (%d) %1s", i1, nodes.get(i1).usages().count(), nodes.get(i1)); + } + } + } + } + // Possible lowerable operation in the same block. Check out the dependencies. + int opIndex = nodes.indexOf(operation); + int current = i + 1; + ArrayList deferred = null; + while (current < opIndex) { + ScheduledNode node = nodes.get(current); + if (node != firstOperation) { + if (node instanceof LocationNode || node instanceof VirtualObjectNode) { + // nothing to do + } else if (node instanceof ConstantNode) { + if (deferred == null) { + deferred = new ArrayList<>(2); + } + // These nodes are collected and the backend is expended to + // evaluate them before generating the lowered form. This + // basically works around unfriendly scheduling of values which + // are defined in a block but not used there. + deferred.add((ValueNode) node); + } else { + Debug.log("unexpected node %1s", node); + // Unexpected inline node + break; + } + } + current++; + } + + if (current == opIndex) { + if (lowerer.memoryPeephole((Access) access, (MemoryArithmeticLIRLowerable) operation, deferred)) { + MemoryFoldSuccess.increment(); + // if this operation had multiple access inputs, then previous attempts + // would be marked as failures which is wrong. Try to adjust the + // counters to account for this. + for (Node input : operation.inputs()) { + if (input == access) { + continue; + } + if (input instanceof Access && nodes.contains(input)) { + MemoryFoldFailedNonAdjacent.add(-1); + } + } + if (deferred != null) { + // Ensure deferred nodes were evaluated + for (ValueNode node : deferred) { + assert hasOperand(node); + } + } + return opIndex - i; + } else { + // This isn't true failure, it just means there wasn't match for the + // pattern. Usually that means it's just not supported by the backend. + MemoryFoldFailed.increment(); + return 0; + } + } else { + MemoryFoldFailedNonAdjacent.increment(); + } + } else { + // memory usage which isn't considered lowerable. Mostly these are + // uninteresting but it might be worth looking at to ensure that interesting + // nodes are being properly handled. + // Debug.log("usage isn't lowerable %1s", access.usages().first()); + } + } + } + return 0; + } + protected abstract boolean peephole(ValueNode valueNode); private boolean hasBlockEnd(Block block) { @@ -504,7 +657,7 @@ Debug.log("Visiting %s", instr); emitNode(instr); - Debug.log("Operand for %s = %s", instr, operand(instr)); + Debug.log("Operand for %s = %s", instr, getOperand(instr)); } protected void emitNode(ValueNode node) { @@ -599,7 +752,7 @@ private Value operandForPhi(PhiNode phi) { assert phi.type() == PhiType.Value : "wrong phi type: " + phi; - Value result = operand(phi); + Value result = getOperand(phi); if (result == null) { // allocate a variable for this phi Variable newOperand = newVariable(getPhiKind(phi)); diff -r 579a2a124c95 -r 6ce6c4ccba8f 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 Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java Thu Mar 20 15:57:03 2014 -0700 @@ -78,6 +78,7 @@ assert config.basicLockSize == 8; this.config = config; this.stub = stub; + memoryPeephole = new AMD64HotSpotMemoryPeephole(this); } @Override diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotMemoryPeephole.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotMemoryPeephole.java Thu Mar 20 15:57:03 2014 -0700 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.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.graph.*; +import com.oracle.graal.hotspot.data.*; +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.calc.*; +import com.oracle.graal.nodes.extended.*; + +/** + * Specialized code gen for comparison with compressed memory. + */ + +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) { + 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, constant.asObject(), true)); + } else if (y.getKind() == Kind.Long) { + crb.recordInlineDataInCode(new MetaspaceData(0, constant.asLong(), constant.getPrimitiveAnnotation(), true)); + } else { + throw GraalInternalError.shouldNotReachHere(); + } + if (state != null) { + crb.recordImplicitException(masm.position(), state); + } + masm.cmpl(x.toAddress(), 0xdeaddead); + } + } + } + + AMD64HotSpotMemoryPeephole(AMD64LIRGenerator gen) { + super(gen); + } + + @Override + protected boolean emitCompareBranchMemory(ValueNode left, ValueNode right, Access access, Condition cond, boolean unorderedIsTrue, LabelRef trueLabel, LabelRef falseLabel, + double trueLabelProbability) { + assert left == access || right == access; + ValueNode other = left == access ? right : left; + Kind kind = access.nullCheckLocation().getValueKind(); + + if (other.isConstant() && kind == Kind.Object && access.isCompressible()) { + ensureEvaluated(other); + gen.append(new CompareMemoryCompressedOp(makeAddress(access), other.asConstant(), getState(access))); + Condition finalCondition = right == access ? cond.mirror() : cond; + gen.append(new BranchOp(finalCondition, trueLabel, falseLabel, trueLabelProbability)); + return true; + } + + return super.emitCompareBranchMemory(left, right, access, cond, unorderedIsTrue, trueLabel, falseLabel, trueLabelProbability); + } +} diff -r 579a2a124c95 -r 6ce6c4ccba8f 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 Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java Thu Mar 20 15:57:03 2014 -0700 @@ -49,6 +49,7 @@ I2F, I2D, L2F, L2D, MOV_I2F, MOV_L2D, MOV_F2I, MOV_D2L, + MOV_B2UI, MOV_B2UL, // Zero extending byte loads /* * Converts a float/double to an int/long. The result of the conversion does not comply with Java semantics @@ -102,6 +103,32 @@ } /** + * Unary operation with separate memory source and destination operand. + */ + public static class Unary2MemoryOp extends AMD64LIRInstruction { + + @Opcode private final AMD64Arithmetic opcode; + @Def({REG}) protected AllocatableValue result; + @Use({COMPOSITE}) protected AMD64AddressValue x; + @State protected LIRFrameState state; + + public Unary2MemoryOp(AMD64Arithmetic opcode, AllocatableValue result, AMD64AddressValue x, LIRFrameState state) { + this.opcode = opcode; + this.result = result; + this.x = x; + this.state = state; + } + + @Override + public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { + if (state != null) { + crb.recordImplicitException(masm.position(), state); + } + emit(crb, masm, opcode, result, x, null); + } + } + + /** * Binary operation with two operands. The first source operand is combined with the * destination. The second source operand may be a stack slot. */ @@ -135,6 +162,45 @@ /** * Binary operation with two operands. The first source operand is combined with the + * destination. The second source operand may be a stack slot. + */ + public static class BinaryMemory extends AMD64LIRInstruction { + + @Opcode private final AMD64Arithmetic opcode; + @Def({REG, HINT}) protected AllocatableValue result; + @Use({REG}) protected AllocatableValue x; + protected final Kind kind; + @Alive({COMPOSITE}) protected AMD64AddressValue location; + @State protected LIRFrameState state; + + public BinaryMemory(AMD64Arithmetic opcode, Kind kind, AllocatableValue result, AllocatableValue x, AMD64AddressValue location, LIRFrameState state) { + this.opcode = opcode; + this.result = result; + this.x = x; + this.location = location; + this.kind = kind; + this.state = state; + } + + @Override + public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { + AMD64Move.move(crb, masm, result, x); + if (state != null) { + crb.recordImplicitException(masm.position(), state); + } + emit(crb, masm, opcode, result, location, null); + } + + @Override + public void verify() { + super.verify(); + assert differentRegisters(result, location) || sameRegister(x, location); + // verifyKind(opcode, result, x, location); + } + } + + /** + * Binary operation with two operands. The first source operand is combined with the * destination. The second source operand must be a register. */ public static class BinaryRegReg extends AMD64LIRInstruction { @@ -688,7 +754,8 @@ default: throw GraalInternalError.shouldNotReachHere(); } - } else { + } else if (isStackSlot(src)) { + switch (opcode) { case IADD: masm.addl(asIntReg(dst), (AMD64Address) crb.asIntAddr(src)); @@ -819,6 +886,143 @@ default: throw GraalInternalError.shouldNotReachHere(); } + } else { + switch (opcode) { + case IADD: + masm.addl(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case ISUB: + masm.subl(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case IAND: + masm.andl(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case IMUL: + masm.imull(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case IOR: + masm.orl(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case IXOR: + masm.xorl(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + + case LADD: + masm.addq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case LSUB: + masm.subq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case LMUL: + masm.imulq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case LAND: + masm.andq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case LOR: + masm.orq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case LXOR: + masm.xorq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + + case FADD: + masm.addss(asFloatReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case FSUB: + masm.subss(asFloatReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case FMUL: + masm.mulss(asFloatReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case FDIV: + masm.divss(asFloatReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + + case DADD: + masm.addsd(asDoubleReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case DSUB: + masm.subsd(asDoubleReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case DMUL: + masm.mulsd(asDoubleReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case DDIV: + masm.divsd(asDoubleReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + + case SQRT: + masm.sqrtsd(asDoubleReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + + case B2I: + masm.movsbl(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case S2I: + masm.movswl(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case B2L: + masm.movsbq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case S2L: + masm.movswq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case I2L: + masm.movslq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case F2D: + masm.cvtss2sd(asDoubleReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case D2F: + masm.cvtsd2ss(asFloatReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case I2F: + masm.cvtsi2ssl(asFloatReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case I2D: + masm.cvtsi2sdl(asDoubleReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case L2F: + masm.cvtsi2ssq(asFloatReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case L2D: + masm.cvtsi2sdq(asDoubleReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case F2I: + masm.cvttss2sil(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case D2I: + masm.cvttsd2sil(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case F2L: + masm.cvttss2siq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case D2L: + masm.cvttsd2siq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case MOV_I2F: + masm.movss(asFloatReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case MOV_L2D: + masm.movsd(asDoubleReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case MOV_F2I: + masm.movl(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case MOV_D2L: + masm.movq(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case MOV_B2UI: + masm.movzbl(asIntReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + case MOV_B2UL: + masm.movzbl(asLongReg(dst), ((AMD64AddressValue) src).toAddress()); + break; + + default: + throw GraalInternalError.shouldNotReachHere(); + } } if (info != null) { diff -r 579a2a124c95 -r 6ce6c4ccba8f 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 Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java Thu Mar 20 15:57:03 2014 -0700 @@ -31,7 +31,6 @@ import com.oracle.graal.lir.*; import com.oracle.graal.lir.asm.*; -// @formatter:off public enum AMD64Compare { ICMP, LCMP, ACMP, FCMP, DCMP; @@ -54,57 +53,152 @@ @Override protected void verify() { super.verify(); - assert (name().startsWith("I") && x.getKind() == Kind.Int && y.getKind().getStackKind() == Kind.Int) - || (name().startsWith("L") && x.getKind() == Kind.Long && y.getKind() == Kind.Long) - || (name().startsWith("A") && x.getKind() == Kind.Object && y.getKind() == Kind.Object) - || (name().startsWith("F") && x.getKind() == Kind.Float && y.getKind() == Kind.Float) - || (name().startsWith("D") && x.getKind() == Kind.Double && y.getKind() == Kind.Double); + assert (name().startsWith("I") && x.getKind() == Kind.Int && y.getKind().getStackKind() == Kind.Int) || (name().startsWith("L") && x.getKind() == Kind.Long && y.getKind() == Kind.Long) || + (name().startsWith("A") && x.getKind() == Kind.Object && y.getKind() == Kind.Object) || + (name().startsWith("F") && x.getKind() == Kind.Float && y.getKind() == Kind.Float) || (name().startsWith("D") && x.getKind() == Kind.Double && y.getKind() == Kind.Double); + } + } + + public static class CompareMemoryOp extends AMD64LIRInstruction { + @Opcode private final AMD64Compare opcode; + @Use({REG, COMPOSITE}) protected Value x; + @Use({CONST, COMPOSITE}) protected Value y; + @State protected LIRFrameState state; + + /** + * Compare memory, constant or register, memory. + */ + public CompareMemoryOp(AMD64Compare opcode, Value x, Value y, LIRFrameState state) { + assert (x instanceof AMD64AddressValue && y instanceof Constant) || (x instanceof Variable && y instanceof AMD64AddressValue); + this.opcode = opcode; + 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); + } + emit(crb, masm, opcode, x, y); + } + + @Override + protected void verify() { + super.verify(); + assert (x instanceof AMD64AddressValue && y instanceof Constant) || (x instanceof Variable && y instanceof AMD64AddressValue); } } public static void emit(CompilationResultBuilder crb, AMD64MacroAssembler masm, AMD64Compare opcode, Value x, Value y) { - if (isRegister(y)) { + if (isRegister(x) && isRegister(y)) { switch (opcode) { - case ICMP: masm.cmpl(asIntReg(x), asIntReg(y)); break; - case LCMP: masm.cmpq(asLongReg(x), asLongReg(y)); break; - case ACMP: masm.cmpptr(asObjectReg(x), asObjectReg(y)); break; - case FCMP: masm.ucomiss(asFloatReg(x), asFloatReg(y)); break; - case DCMP: masm.ucomisd(asDoubleReg(x), asDoubleReg(y)); break; - default: throw GraalInternalError.shouldNotReachHere(); + case ICMP: + masm.cmpl(asIntReg(x), asIntReg(y)); + break; + case LCMP: + masm.cmpq(asLongReg(x), asLongReg(y)); + break; + case ACMP: + masm.cmpptr(asObjectReg(x), asObjectReg(y)); + break; + case FCMP: + masm.ucomiss(asFloatReg(x), asFloatReg(y)); + break; + case DCMP: + masm.ucomisd(asDoubleReg(x), asDoubleReg(y)); + break; + default: + throw GraalInternalError.shouldNotReachHere(); } - } else if (isConstant(y)) { + } else if (isRegister(x) && isConstant(y)) { boolean isZero = ((Constant) y).isDefaultForKind(); switch (opcode) { - case ICMP: if (isZero) { - masm.testl(asIntReg(x), asIntReg(x)); - } else { - masm.cmpl(asIntReg(x), crb.asIntConst(y)); - } - break; - case LCMP: if (isZero) { - masm.testq(asLongReg(x), asLongReg(x)); - } else { - masm.cmpq(asLongReg(x), crb.asIntConst(y)); - } - break; + case ICMP: + if (isZero) { + masm.testl(asIntReg(x), asIntReg(x)); + } else { + masm.cmpl(asIntReg(x), crb.asIntConst(y)); + } + break; + case LCMP: + if (isZero) { + masm.testq(asLongReg(x), asLongReg(x)); + } else { + masm.cmpq(asLongReg(x), crb.asIntConst(y)); + } + break; case ACMP: if (isZero) { - masm.testq(asObjectReg(x), asObjectReg(x)); break; + masm.testq(asObjectReg(x), asObjectReg(x)); + break; } else { throw GraalInternalError.shouldNotReachHere("Only null object constants are allowed in comparisons"); } - case FCMP: masm.ucomiss(asFloatReg(x), (AMD64Address) crb.asFloatConstRef(y)); break; - case DCMP: masm.ucomisd(asDoubleReg(x), (AMD64Address) crb.asDoubleConstRef(y)); break; - default: throw GraalInternalError.shouldNotReachHere(); + case FCMP: + masm.ucomiss(asFloatReg(x), (AMD64Address) crb.asFloatConstRef(y)); + break; + case DCMP: + masm.ucomisd(asDoubleReg(x), (AMD64Address) crb.asDoubleConstRef(y)); + break; + default: + throw GraalInternalError.shouldNotReachHere(); } - } else { + } else if (isRegister(x) && isStackSlot(y)) { + switch (opcode) { + case ICMP: + masm.cmpl(asIntReg(x), (AMD64Address) crb.asIntAddr(y)); + break; + case LCMP: + masm.cmpq(asLongReg(x), (AMD64Address) crb.asLongAddr(y)); + break; + case ACMP: + masm.cmpptr(asObjectReg(x), (AMD64Address) crb.asObjectAddr(y)); + break; + case FCMP: + masm.ucomiss(asFloatReg(x), (AMD64Address) crb.asFloatAddr(y)); + break; + case DCMP: + masm.ucomisd(asDoubleReg(x), (AMD64Address) crb.asDoubleAddr(y)); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + } else if (isRegister(x) && y instanceof AMD64AddressValue) { switch (opcode) { - case ICMP: masm.cmpl(asIntReg(x), (AMD64Address) crb.asIntAddr(y)); break; - case LCMP: masm.cmpq(asLongReg(x), (AMD64Address) crb.asLongAddr(y)); break; - case ACMP: masm.cmpptr(asObjectReg(x), (AMD64Address) crb.asObjectAddr(y)); break; - case FCMP: masm.ucomiss(asFloatReg(x), (AMD64Address) crb.asFloatAddr(y)); break; - case DCMP: masm.ucomisd(asDoubleReg(x), (AMD64Address) crb.asDoubleAddr(y)); break; - default: throw GraalInternalError.shouldNotReachHere(); + case ICMP: + masm.cmpl(asIntReg(x), ((AMD64AddressValue) y).toAddress()); + break; + case LCMP: + masm.cmpq(asLongReg(x), ((AMD64AddressValue) y).toAddress()); + break; + case ACMP: + masm.cmpptr(asObjectReg(x), ((AMD64AddressValue) y).toAddress()); + break; + case FCMP: + masm.ucomiss(asFloatReg(x), ((AMD64AddressValue) y).toAddress()); + break; + case DCMP: + masm.ucomisd(asDoubleReg(x), ((AMD64AddressValue) y).toAddress()); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + } else if (x instanceof AMD64AddressValue && isConstant(y)) { + switch (opcode) { + case ICMP: + masm.cmpl(((AMD64AddressValue) x).toAddress(), crb.asIntConst(y)); + break; + case LCMP: + if (crb.asLongConst(y) == (int) crb.asLongConst(y)) { + masm.cmpq(((AMD64AddressValue) x).toAddress(), (int) crb.asLongConst(y)); + } else { + throw GraalInternalError.shouldNotReachHere(); + } + break; + default: + throw GraalInternalError.shouldNotReachHere(); } } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64TestMemoryOp.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64TestMemoryOp.java Thu Mar 20 15:57:03 2014 -0700 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.lir.amd64; + +import static com.oracle.graal.api.code.ValueUtil.*; +import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.asm.amd64.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.lir.*; +import com.oracle.graal.lir.asm.*; + +public class AMD64TestMemoryOp extends AMD64LIRInstruction { + + @Use({COMPOSITE}) protected AMD64AddressValue x; + @Use({REG, CONST}) protected Value y; + @State protected LIRFrameState state; + + public AMD64TestMemoryOp(AMD64AddressValue x, Value y, LIRFrameState state) { + 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); + } + emit(crb, masm, x, y); + } + + @Override + protected void verify() { + super.verify(); + // Can't check the kind of an address so just check the other input + assert (x.getKind() == Kind.Int || x.getKind() == Kind.Long) : x + " " + y; + } + + public static void emit(CompilationResultBuilder crb, AMD64MacroAssembler masm, Value x, Value y) { + if (isRegister(y)) { + switch (y.getKind()) { + case Int: + masm.testl(asIntReg(y), ((AMD64AddressValue) x).toAddress()); + break; + case Long: + masm.testq(asLongReg(y), ((AMD64AddressValue) x).toAddress()); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + } else if (isConstant(y)) { + switch (y.getKind()) { + case Int: + masm.testl(((AMD64AddressValue) x).toAddress(), crb.asIntConst(y)); + break; + case Long: + masm.testq(((AMD64AddressValue) x).toAddress(), crb.asIntConst(y)); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + } else { + throw GraalInternalError.shouldNotReachHere(); + } + } +} diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -33,6 +33,7 @@ import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.PhiNode.PhiType; import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -42,7 +43,7 @@ * The {@code IfNode} represents a branch that can go one of two directions depending on the outcome * of a comparison. */ -public final class IfNode extends ControlSplitNode implements Simplifiable, LIRLowerable { +public final class IfNode extends ControlSplitNode implements Simplifiable, LIRLowerable, MemoryArithmeticLIRLowerable { @Successor private AbstractBeginNode trueSuccessor; @Successor private AbstractBeginNode falseSuccessor; @@ -131,6 +132,11 @@ } @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + return gen.emitIfMemory(this, access); + } + + @Override public boolean verify() { assertTrue(condition() != null, "missing condition"); assertTrue(trueSuccessor() != null, "missing trueSuccessor"); diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/AndNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/AndNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/AndNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -83,4 +84,13 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitAnd(gen.operand(x()), gen.operand(y()))); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitAndMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/BitLogicNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/BitLogicNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/BitLogicNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -29,7 +29,7 @@ /** * The {@code LogicNode} class definition. */ -public abstract class BitLogicNode extends BinaryNode implements ArithmeticLIRLowerable, NarrowableArithmeticNode { +public abstract class BitLogicNode extends BinaryNode implements ArithmeticLIRLowerable, MemoryArithmeticLIRLowerable, NarrowableArithmeticNode { /** * Constructs a new logic operation node. diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; /* TODO (thomaswue/gdub) For high-level optimization purpose the compare node should be a boolean *value* (it is currently only a helper node) @@ -34,7 +35,7 @@ * Compare should probably be made a value (so that it can be canonicalized for example) and in later stages some Compare usage should be transformed * into variants that do not materialize the value (CompareIf, CompareGuard...) */ -public abstract class CompareNode extends LogicNode implements Canonicalizable, LIRLowerable { +public abstract class CompareNode extends LogicNode implements Canonicalizable, LIRLowerable, MemoryArithmeticLIRLowerable { @Input private ValueNode x; @Input private ValueNode y; @@ -192,4 +193,8 @@ return graph.unique(comparison); } + + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + return false; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatAddNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatAddNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatAddNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -77,10 +78,19 @@ public static boolean livesLonger(ValueNode after, ValueNode value, ArithmeticLIRGenerator gen) { for (Node usage : value.usages()) { - if (usage != after && usage instanceof ValueNode && gen.operand(((ValueNode) usage)) != null) { + if (usage != after && usage instanceof ValueNode && gen.hasOperand(((ValueNode) usage))) { return true; } } return false; } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitAddMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatArithmeticNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatArithmeticNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatArithmeticNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,7 +26,7 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; -public abstract class FloatArithmeticNode extends BinaryNode implements ArithmeticLIRLowerable { +public abstract class FloatArithmeticNode extends BinaryNode implements ArithmeticLIRLowerable, MemoryArithmeticLIRLowerable { private final boolean isStrictFP; diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatConvertNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -33,7 +34,7 @@ * A {@code FloatConvert} converts between integers and floating point numbers according to Java * semantics. */ -public class FloatConvertNode extends ConvertNode implements Canonicalizable, Lowerable, ArithmeticLIRLowerable { +public class FloatConvertNode extends ConvertNode implements Canonicalizable, Lowerable, ArithmeticLIRLowerable, MemoryArithmeticLIRLowerable { public enum FloatConvert { F2I, D2I, F2L, D2L, I2F, L2F, D2F, I2D, L2D, F2D; @@ -194,4 +195,12 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitFloatConvert(op, gen.operand(getInput()))); } + + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitFloatConvertMemory(getOp(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatDivNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatDivNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatDivNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -59,4 +60,13 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitDiv(gen.operand(x()), gen.operand(y()), null)); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitDivMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatMulNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatMulNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatMulNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -69,4 +70,13 @@ } gen.setResult(this, gen.emitMul(op1, op2)); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitMulMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatRemNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatRemNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatRemNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -59,4 +60,13 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitRem(gen.operand(x()), gen.operand(y()), null)); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitRemMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatSubNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatSubNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatSubNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -78,4 +79,13 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitSub(gen.operand(x()), gen.operand(y()))); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitSubMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerAddNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerAddNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerAddNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -78,9 +79,6 @@ if (reassociated != this) { return reassociated; } - if (c < 0) { - return IntegerArithmeticNode.sub(graph(), x(), ConstantNode.forIntegerStamp(stamp(), -c, graph())); - } } if (x() instanceof NegateNode) { return IntegerArithmeticNode.sub(graph(), y(), ((NegateNode) x()).x()); @@ -102,4 +100,13 @@ } gen.setResult(this, gen.emitAdd(op1, op2)); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitAddMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerArithmeticNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerArithmeticNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerArithmeticNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,7 +26,7 @@ import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; -public abstract class IntegerArithmeticNode extends BinaryNode implements ArithmeticLIRLowerable { +public abstract class IntegerArithmeticNode extends BinaryNode implements ArithmeticLIRLowerable, MemoryArithmeticLIRLowerable { public IntegerArithmeticNode(Stamp stamp, ValueNode x, ValueNode y) { super(stamp, x, y); diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -31,7 +31,7 @@ /** * An {@code IntegerConvert} converts an integer to an integer of different width. */ -public abstract class IntegerConvertNode extends ConvertNode implements ArithmeticLIRLowerable, Canonicalizable { +public abstract class IntegerConvertNode extends ConvertNode implements ArithmeticLIRLowerable, Canonicalizable, MemoryArithmeticLIRLowerable { private final int resultBits; diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerMulNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerMulNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerMulNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -27,6 +27,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -84,4 +85,13 @@ } gen.setResult(this, gen.emitMul(op1, op2)); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitMulMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerSubNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerSubNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerSubNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -97,7 +98,9 @@ if (reassociated != this) { return reassociated; } - if (c < 0) { + if (c < 0 || ((IntegerStamp) StampFactory.forKind(y().getKind())).contains(-c)) { + // Adding a negative is more friendly to the backend since adds are + // commutative, so prefer add when it fits. return IntegerArithmeticNode.add(graph(), x(), ConstantNode.forIntegerStamp(stamp(), -c, graph())); } } else if (x().isConstant()) { @@ -117,4 +120,13 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitSub(gen.operand(x()), gen.operand(y()))); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitSubMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerTestNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerTestNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerTestNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -25,6 +25,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -33,7 +34,7 @@ * expression "(x & y) == 0", meaning that it will return true if (and only if) no bit is set in * both x and y. */ -public class IntegerTestNode extends LogicNode implements Canonicalizable, LIRLowerable { +public class IntegerTestNode extends LogicNode implements Canonicalizable, LIRLowerable, MemoryArithmeticLIRLowerable { @Input private ValueNode x; @Input private ValueNode y; @@ -76,4 +77,9 @@ } return this; } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + return false; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/NarrowNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/NarrowNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/NarrowNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -112,4 +113,13 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitNarrow(gen.operand(getInput()), getResultBits())); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitNarrowMemory(getResultBits(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/OrNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/OrNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/OrNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -75,4 +76,13 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitOr(gen.operand(x()), gen.operand(y()))); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitOrMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ReinterpretNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ReinterpretNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ReinterpretNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -34,7 +35,7 @@ * of a primitive value to some other incompatible stamp. The new stamp must have the same width as * the old stamp. */ -public class ReinterpretNode extends FloatingNode implements Canonicalizable, ArithmeticLIRLowerable { +public class ReinterpretNode extends FloatingNode implements Canonicalizable, ArithmeticLIRLowerable, MemoryArithmeticLIRLowerable { @Input private ValueNode value; @@ -106,6 +107,15 @@ gen.setResult(this, gen.emitReinterpret(kind, gen.operand(value()))); } + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitReinterpretMemory(stamp(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } + public static ValueNode reinterpret(Kind toKind, ValueNode value) { return value.graph().unique(new ReinterpretNode(toKind, value)); } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SignExtendNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SignExtendNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SignExtendNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -107,4 +108,14 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitSignExtend(gen.operand(getInput()), getInputBits(), getResultBits())); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + assert !access.nullCheckLocation().getValueKind().isUnsigned() : "can't sign extend unsigned value"; + Value result = gen.emitSignExtendMemory(access, access.nullCheckLocation().getValueKind().getBitCount(), getResultBits()); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/XorNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/XorNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/XorNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -74,4 +75,13 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitXor(gen.operand(x()), gen.operand(y()))); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitXorMemory(x(), y(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ZeroExtendNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ZeroExtendNode.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ZeroExtendNode.java Thu Mar 20 15:57:03 2014 -0700 @@ -26,6 +26,7 @@ import com.oracle.graal.graph.*; import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; @@ -87,4 +88,13 @@ public void generate(ArithmeticLIRGenerator gen) { gen.setResult(this, gen.emitZeroExtend(gen.operand(getInput()), getInputBits(), getResultBits())); } + + @Override + public boolean generate(MemoryArithmeticLIRLowerer gen, Access access) { + Value result = gen.emitZeroExtendMemory(getInputBits(), getResultBits(), access); + if (result != null) { + gen.setResult(this, result); + } + return result != null; + } } diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/ArithmeticLIRGenerator.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/ArithmeticLIRGenerator.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/ArithmeticLIRGenerator.java Thu Mar 20 15:57:03 2014 -0700 @@ -35,6 +35,8 @@ Value operand(ValueNode object); + boolean hasOperand(ValueNode object); + Value setResult(ValueNode x, Value operand); PlatformKind getPlatformKind(Stamp stamp); diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/MemoryArithmeticLIRLowerable.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/MemoryArithmeticLIRLowerable.java Thu Mar 20 15:57:03 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.nodes.spi; + +import com.oracle.graal.nodes.extended.*; + +/** + * Marks nodes which may be lowered in combination with a memory operation. + */ +public interface MemoryArithmeticLIRLowerable { + + /** + * Attempt to generate a memory form of a node operation. On platforms that support it this will + * be called when the merging is safe. + * + * @param gen + * @param access the memory input which can potentially merge into this operation. + * @return null if it's not possible to emit a memory form of this operation. A non-null value + * will be set as the operand of this node. + */ + boolean generate(MemoryArithmeticLIRLowerer gen, Access access); +} diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/MemoryArithmeticLIRLowerer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/MemoryArithmeticLIRLowerer.java Thu Mar 20 15:57:03 2014 -0700 @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.nodes.spi; + +import java.util.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.FloatConvertNode.FloatConvert; +import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.type.*; + +/** + * This interface can be used to generate LIR for arithmetic operations where one of the operations + * is load (@see ArithmeticLIRLowerable). + */ +public interface MemoryArithmeticLIRLowerer { + + Value setResult(ValueNode x, Value operand); + + Value emitAddMemory(ValueNode x, ValueNode y, Access access); + + Value emitMulMemory(ValueNode x, ValueNode y, Access access); + + Value emitSubMemory(ValueNode x, ValueNode y, Access access); + + Value emitDivMemory(ValueNode x, ValueNode y, Access access); + + Value emitRemMemory(ValueNode x, ValueNode y, Access access); + + Value emitXorMemory(ValueNode x, ValueNode y, Access access); + + Value emitOrMemory(ValueNode x, ValueNode y, Access access); + + Value emitAndMemory(ValueNode x, ValueNode y, Access access); + + Value emitReinterpretMemory(Stamp stamp, Access access); + + Value emitSignExtendMemory(Access access, int fromBits, int toBits); + + Value emitNarrowMemory(int resultBits, Access access); + + Value emitZeroExtendMemory(int inputBits, int resultBits, Access access); + + Value emitFloatConvertMemory(FloatConvert op, Access access); + + boolean memoryPeephole(Access valueNode, MemoryArithmeticLIRLowerable operation, List deferred); + + boolean emitIfMemory(IfNode ifNode, Access access); + +} diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java Thu Mar 20 15:57:03 2014 -0700 @@ -273,10 +273,10 @@ // @formatter:on private boolean performReplacement(final Node node, Node canonical) { if (canonical == node) { - Debug.log("Canonicalizer: work on %s", node); + Debug.log("Canonicalizer: work on %1s", node); return false; } else { - Debug.log("Canonicalizer: replacing %s with %s", node, canonical); + Debug.log("Canonicalizer: replacing %1s with %1s", node, canonical); METRIC_CANONICALIZED_NODES.increment(); StructuredGraph graph = (StructuredGraph) node.graph(); if (node instanceof FloatingNode) { diff -r 579a2a124c95 -r 6ce6c4ccba8f graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java Thu Mar 20 13:41:32 2014 -0700 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java Thu Mar 20 15:57:03 2014 -0700 @@ -271,6 +271,8 @@ public static final OptionValue OptDevirtualizeInvokesOptimistically = new OptionValue<>(true); @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); /**