# HG changeset patch # User Christian Humer # Date 1387291463 -3600 # Node ID cf7b5b50754147100cf4ed12ce7feef50d6678be # Parent 136df94b5aa8b163beaa14917a0a32517120dd38# Parent e1a50eac0eac05f81bba5483931e1d817b18ca17 Merge. diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java --- a/graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java Tue Dec 17 15:44:23 2013 +0100 @@ -26,6 +26,7 @@ import static com.oracle.graal.api.code.Register.*; import java.nio.*; +import java.util.*; import com.oracle.graal.api.code.*; import com.oracle.graal.api.code.Register.RegisterCategory; @@ -111,22 +112,23 @@ // @formatter:on - private final int supportedSSEVersion; - private final int supportedAVXVersion; - - public AMD64(int supportedSSEVersion, int supportedAVXVersion) { - super("AMD64", 8, ByteOrder.LITTLE_ENDIAN, true, allRegisters, LOAD_STORE | STORE_STORE, 1, r15.encoding + 1, 8); - assert supportedSSEVersion >= 2; - this.supportedSSEVersion = supportedSSEVersion; - this.supportedAVXVersion = supportedAVXVersion; + /** + * Basic set of CPU features mirroring what is returned from the cpuid instruction. + */ + public static enum CPUFeature { + SSE, SSE2, SSE3, SSE4, SSE4a, SSE4_1, SSE4_2, SSSE3, POPCNT, LZCNT, AVX, AVX2, ERMS, AMD_3DNOW_PREFETCH, AES, } - public int getSupportedSSEVersion() { - return supportedSSEVersion; + private final EnumSet features; + + public AMD64(EnumSet features) { + super("AMD64", 8, ByteOrder.LITTLE_ENDIAN, true, allRegisters, LOAD_STORE | STORE_STORE, 1, r15.encoding + 1, 8); + this.features = features; + assert features.contains(CPUFeature.SSE2) : "minimum config for x64"; } - public int getSupportedAVXVersion() { - return supportedAVXVersion; + public EnumSet getFeatures() { + return features; } @Override diff -r 136df94b5aa8 -r cf7b5b507541 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 Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java Tue Dec 17 15:44:23 2013 +0100 @@ -174,6 +174,10 @@ this.frameRegister = registerConfig == null ? null : registerConfig.getFrameRegister(); } + private boolean supports(CPUFeature feature) { + return ((AMD64) target.arch).getFeatures().contains(feature); + } + private static int encode(Register r) { assert r.encoding < 16 && r.encoding >= 0 : "encoding out of range: " + r.encoding; return r.encoding & 0x7; @@ -450,6 +454,7 @@ } public final void bsrq(Register dst, Register src) { + assert !supports(CPUFeature.LZCNT); int encode = prefixqAndEncode(dst.encoding, src.encoding); emitByte(0x0F); emitByte(0xBD); @@ -457,6 +462,7 @@ } public final void bsrq(Register dst, AMD64Address src) { + assert !supports(CPUFeature.LZCNT); prefixq(src, dst); emitByte(0x0F); emitByte(0xBD); @@ -464,6 +470,7 @@ } public final void bsrl(Register dst, Register src) { + assert !supports(CPUFeature.LZCNT); int encode = prefixAndEncode(dst.encoding, src.encoding); emitByte(0x0F); emitByte(0xBD); @@ -471,6 +478,7 @@ } public final void bsrl(Register dst, AMD64Address src) { + assert !supports(CPUFeature.LZCNT); prefix(src, dst); emitByte(0x0F); emitByte(0xBD); @@ -1405,6 +1413,7 @@ } public final void popcntl(Register dst, AMD64Address src) { + assert supports(CPUFeature.POPCNT); emitByte(0xF3); prefix(src, dst); emitByte(0x0F); @@ -1413,6 +1422,7 @@ } public final void popcntl(Register dst, Register src) { + assert supports(CPUFeature.POPCNT); emitByte(0xF3); int encode = prefixAndEncode(dst.encoding, src.encoding); emitByte(0x0F); @@ -1421,6 +1431,7 @@ } public final void popcntq(Register dst, AMD64Address src) { + assert supports(CPUFeature.POPCNT); emitByte(0xF3); prefixq(src, dst); emitByte(0x0F); @@ -1429,6 +1440,7 @@ } public final void popcntq(Register dst, Register src) { + assert supports(CPUFeature.POPCNT); emitByte(0xF3); int encode = prefixqAndEncode(dst.encoding, src.encoding); emitByte(0x0F); @@ -2549,28 +2561,28 @@ } void prefetchr(AMD64Address src) { - // assert(VM_Version::supports_3dnow_prefetch(), "must support"); + assert supports(CPUFeature.AMD_3DNOW_PREFETCH); prefetchPrefix(src); emitByte(0x0D); emitOperandHelper(0, src); } public void prefetcht0(AMD64Address src) { - // NOT_LP64(assert(VM_Version::supports_sse(), "must support")); + assert supports(CPUFeature.SSE); prefetchPrefix(src); emitByte(0x18); emitOperandHelper(1, src); } public void prefetcht1(AMD64Address src) { - // NOT_LP64(assert(VM_Version::supports_sse(), "must support")); + assert supports(CPUFeature.SSE); prefetchPrefix(src); emitByte(0x18); emitOperandHelper(2, src); } public void prefetcht2(AMD64Address src) { - // NOT_LP64(assert(VM_Version::supports_sse(), "must support")); + assert supports(CPUFeature.SSE); prefix(src); emitByte(0x0f); emitByte(0x18); @@ -2578,7 +2590,7 @@ } public void prefetchw(AMD64Address src) { - // assert(VM_Version::supports_3dnow_prefetch(), "must support"); + assert supports(CPUFeature.AMD_3DNOW_PREFETCH); prefix(src); emitByte(0x0f); emitByte(0x0D); diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CommonedConstantsTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CommonedConstantsTest.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CommonedConstantsTest.java Tue Dec 17 15:44:23 2013 +0100 @@ -27,6 +27,7 @@ import org.junit.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.phases.common.*; /** * Tests any optimization that commons loads of non-inlineable constants. @@ -37,7 +38,7 @@ // A method where a constant is used on the normal and exception edge of a non-inlined call. // The dominating block of both usages is the block containing the call. - public static Object testSnippet(String[] arr, int i) { + public static Object test0Snippet(String[] arr, int i) { Object result = null; try { result = Array.get(arr, i); @@ -53,13 +54,70 @@ @Test public void test0() { // Ensure the exception path is profiled - ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(getMethod("testSnippet")); + ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(getMethod("test0Snippet")); javaMethod.reprofile(); - testSnippet(array, array.length); + test0Snippet(array, array.length); + + test("test0Snippet", array, 0); + test("test0Snippet", array, 2); + test("test0Snippet", array, 3); + test("test0Snippet", array, 1); + } + + public static final char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray(); + + static int noninlineLength(char[] s) { + return s.length; + } + + /** + * A constant with usages before and after a non-inlined call. + */ + public static int test1Snippet(String s) { + if (s == null) { + return noninlineLength(alphabet) + 1; + } + char[] sChars = s.toCharArray(); + int count = 0; + for (int i = 0; i < alphabet.length && i < sChars.length; i++) { + if (alphabet[i] == sChars[i]) { + count++; + } + } + return count; + } - test("testSnippet", array, 0); - test("testSnippet", array, 2); - test("testSnippet", array, 3); - test("testSnippet", array, 1); + @Test + public void test1() { + getSuites().getHighTier().findPhase(AbstractInliningPhase.class).remove(); + test1Snippet(new String(alphabet)); + + test("test1Snippet", (Object) null); + test("test1Snippet", "test1Snippet"); + test("test1Snippet", ""); + } + + /** + * A constant with only usage in a loop. + */ + public static int test2Snippet(String s) { + char[] sChars = s.toCharArray(); + int count = 0; + for (int i = 0; i < alphabet.length && i < sChars.length; i++) { + if (alphabet[i] == sChars[i]) { + count++; + } + } + return count; + } + + @Test + public void test2() { + assert getSuites().getHighTier().findPhase(AbstractInliningPhase.class).hasNext(); + test2Snippet(new String(alphabet)); + + test("test2Snippet", (Object) null); + test("test2Snippet", "test1Snippet"); + test("test2Snippet", ""); } } diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/Interval.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/Interval.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/Interval.java Tue Dec 17 15:44:23 2013 +0100 @@ -418,7 +418,8 @@ /** * The {@linkplain RegisterValue register} or {@linkplain StackSlot spill slot} assigned to this - * interval. + * interval. In case of a spilled interval which is re-materialized this is + * {@link Value#ILLEGAL}. */ private AllocatableValue location; @@ -498,6 +499,17 @@ */ private Interval locationHint; + /** + * The value with which a spilled child interval can be re-materialized. Currently this must be + * a Constant. + */ + private Constant materializedValue; + + /** + * The number of times {@link #addMaterializationValue(Constant)} is called. + */ + private int numMaterializationValuesAdded; + void assignLocation(AllocatableValue newLocation) { if (isRegister(newLocation)) { assert this.location == null : "cannot re-assign location for " + this; @@ -505,6 +517,8 @@ this.location = asRegister(newLocation).asValue(kind); return; } + } else if (isIllegal(newLocation)) { + assert canMaterialize(); } else { assert this.location == null || isRegister(this.location) : "cannot re-assign location for " + this; assert isStackSlot(newLocation); @@ -621,7 +635,7 @@ // returns true if this interval has a shadow copy on the stack that is always correct boolean alwaysInMemory() { - return splitParent().spillState == SpillState.StoreAtDefinition || splitParent().spillState == SpillState.StartInMemory; + return (splitParent().spillState == SpillState.StoreAtDefinition || splitParent().spillState == SpillState.StartInMemory) && !canMaterialize(); } void removeFirstUsePos() { @@ -693,6 +707,34 @@ currentSplitChild = this; } + /** + * Sets the value which is used for re-materialization. + */ + void addMaterializationValue(Constant value) { + if (numMaterializationValuesAdded == 0) { + materializedValue = value; + } else { + // Interval is defined on multiple places -> no materialization is possible. + materializedValue = null; + } + numMaterializationValuesAdded++; + } + + /** + * Returns true if this interval can be re-materialized when spilled. This means that no + * spill-moves are needed. Instead of restore-moves the materializeValue is restored. + */ + public boolean canMaterialize() { + return getMaterializedValue() != null; + } + + /** + * Returns the value which is moved to a register instead of a restore-move from stack. + */ + public Constant getMaterializedValue() { + return splitParent().materializedValue; + } + int calcTo() { assert first != Range.EndMarker : "interval has no range"; @@ -864,12 +906,24 @@ } } + private RegisterPriority adaptPriority(RegisterPriority priority) { + /* + * In case of re-materialized values we require that use-operands are registers, because we + * don't have the value at a stack location. (Note that ShouldHaveRegister means that the + * operand can also be a StackSlot). + */ + if (priority == RegisterPriority.ShouldHaveRegister && canMaterialize()) { + return RegisterPriority.MustHaveRegister; + } + return priority; + } + // Note: use positions are sorted descending . first use has highest index int firstUsage(RegisterPriority minRegisterPriority) { assert isVariable(operand) : "cannot access use positions for fixed intervals"; for (int i = usePosList.size() - 1; i >= 0; --i) { - RegisterPriority registerPriority = usePosList.registerPriority(i); + RegisterPriority registerPriority = adaptPriority(usePosList.registerPriority(i)); if (registerPriority.greaterEqual(minRegisterPriority)) { return usePosList.usePos(i); } @@ -882,7 +936,7 @@ for (int i = usePosList.size() - 1; i >= 0; --i) { int usePos = usePosList.usePos(i); - if (usePos >= from && usePosList.registerPriority(i).greaterEqual(minRegisterPriority)) { + if (usePos >= from && adaptPriority(usePosList.registerPriority(i)).greaterEqual(minRegisterPriority)) { return usePos; } } @@ -894,7 +948,7 @@ for (int i = usePosList.size() - 1; i >= 0; --i) { int usePos = usePosList.usePos(i); - if (usePos >= from && usePosList.registerPriority(i) == exactRegisterPriority) { + if (usePos >= from && adaptPriority(usePosList.registerPriority(i)) == exactRegisterPriority) { return usePos; } } @@ -910,7 +964,7 @@ if (usePos > from) { return prev; } - if (usePosList.registerPriority(i).greaterEqual(minRegisterPriority)) { + if (adaptPriority(usePosList.registerPriority(i)).greaterEqual(minRegisterPriority)) { prev = usePos; } } @@ -1181,6 +1235,10 @@ buf.append(usePosList.usePos(i)).append(':').append(usePosList.registerPriority(i)); prev = usePosList.usePos(i); } - return buf.append("} spill-state{").append(spillState()).append("}").toString(); + buf.append("} spill-state{").append(spillState()).append("}"); + if (canMaterialize()) { + buf.append(" (remat:").append(getMaterializedValue().toString()).append(")"); + } + return buf.toString(); } } diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java Tue Dec 17 15:44:23 2013 +0100 @@ -263,11 +263,11 @@ } }; - static final IntervalPredicate IS_OOP_INTERVAL = new IntervalPredicate() { + static final IntervalPredicate IS_STACK_INTERVAL = new IntervalPredicate() { @Override public boolean apply(Interval i) { - return !isRegister(i.operand) && i.kind() == Kind.Object; + return !isRegister(i.operand); } }; @@ -282,7 +282,9 @@ void assignSpillSlot(Interval interval) { // assign the canonical spill slot of the parent (if a part of the interval // is already spilled) or allocate a new spill slot - if (interval.spillSlot() != null) { + if (interval.canMaterialize()) { + interval.assignLocation(Value.ILLEGAL); + } else if (interval.spillSlot() != null) { interval.assignLocation(interval.spillSlot()); } else { StackSlot slot = frameMap.allocateSpillSlot(interval.kind()); @@ -519,9 +521,7 @@ // called once before assignment of register numbers void eliminateSpillMoves() { - if (getTraceLevel() >= 3) { - TTY.println(" Eliminating unnecessary spill moves"); - } + Indent indent = Debug.logAndIndent("Eliminating unnecessary spill moves"); // collect all intervals that must be stored after their definition. // the list is sorted by Interval.spillDefinitionPos @@ -553,9 +553,7 @@ if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) { // move target is a stack slot that is always correct, so eliminate // instruction - if (getTraceLevel() >= 4) { - TTY.println("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult())); - } + indent.log("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult())); instructions.set(j, null); // null-instructions are deleted by assignRegNum } @@ -565,25 +563,23 @@ assert interval == Interval.EndMarker || (interval.isSplitParent() && interval.spillState() == SpillState.StoreAtDefinition) : "invalid interval"; while (interval != Interval.EndMarker && interval.spillDefinitionPos() == opId) { - if (!insertionBuffer.initialized()) { - // prepare insertion buffer (appended when all instructions of the block - // are processed) - insertionBuffer.init(instructions); - } - - AllocatableValue fromLocation = interval.location(); - AllocatableValue toLocation = canonicalSpillOpr(interval); + if (!interval.canMaterialize()) { + if (!insertionBuffer.initialized()) { + // prepare insertion buffer (appended when all instructions of the + // block are processed) + insertionBuffer.init(instructions); + } - assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + interval.spillState(); - assert isStackSlot(toLocation) : "to operand must be a stack slot"; - - insertionBuffer.append(j + 1, ir.spillMoveFactory.createMove(toLocation, fromLocation)); + AllocatableValue fromLocation = interval.location(); + AllocatableValue toLocation = canonicalSpillOpr(interval); - if (getTraceLevel() >= 4) { - StackSlot slot = interval.spillSlot(); - TTY.println("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, slot, opId); + assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + interval.spillState(); + assert isStackSlot(toLocation) : "to operand must be a stack slot"; + + insertionBuffer.append(j + 1, ir.spillMoveFactory.createMove(toLocation, fromLocation)); + + indent.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId); } - interval = interval.next; } } @@ -595,9 +591,10 @@ } // end of block iteration assert interval == Interval.EndMarker : "missed an interval"; + indent.outdent(); } - private void checkIntervals(Interval interval) { + private static void checkIntervals(Interval interval) { Interval prev = null; Interval temp = interval; while (temp != Interval.EndMarker) { @@ -607,13 +604,11 @@ assert temp.spillDefinitionPos() >= prev.spillDefinitionPos() : "when intervals are sorted by from : then they must also be sorted by spillDefinitionPos"; } - assert temp.spillSlot() != null : "interval has no spill slot assigned"; + assert temp.spillSlot() != null || temp.canMaterialize() : "interval has no spill slot assigned"; assert temp.spillDefinitionPos() >= temp.from() : "invalid order"; assert temp.spillDefinitionPos() <= temp.from() + 2 : "only intervals defined once at their start-pos can be optimized"; - if (traceLevel >= 4) { - TTY.println("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos()); - } + Debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos()); prev = temp; temp = temp.next; @@ -695,6 +690,8 @@ // iterate all blocks for (final Block block : sortedBlocks) { + Indent indent = Debug.logAndIndent("compute local live sets for block %d", block.getId()); + final BitSet liveGen = new BitSet(liveSize); final BitSet liveKill = new BitSet(liveSize); @@ -713,9 +710,7 @@ int operandNum = operandNumber(operand); if (!liveKill.get(operandNum)) { liveGen.set(operandNum); - if (getTraceLevel() >= 4) { - TTY.println(" Setting liveGen for operand %d at instruction %d", operandNum, op.id()); - } + Debug.log("liveGen for operand %d", operandNum); } if (block.getLoop() != null) { intervalInLoop.setBit(operandNum, block.getLoop().index); @@ -735,9 +730,7 @@ int operandNum = operandNumber(operand); if (!liveKill.get(operandNum)) { liveGen.set(operandNum); - if (getTraceLevel() >= 4) { - TTY.println(" Setting liveGen for LIR opId %d, operand %d because of state for %s", op.id(), operandNum, op); - } + Debug.log("liveGen in state for operand %d", operandNum); } return operand; } @@ -749,6 +742,7 @@ if (isVariable(operand)) { int varNum = operandNumber(operand); liveKill.set(varNum); + Debug.log("liveKill for operand %d", varNum); if (block.getLoop() != null) { intervalInLoop.setBit(varNum, block.getLoop().index); } @@ -764,6 +758,7 @@ } }; + Indent indent2 = indent.logAndIndent("handle op %d", op.id()); op.forEachInput(useProc); op.forEachAlive(useProc); // Add uses of live locals from interpreter's point of view for proper debug @@ -771,17 +766,19 @@ op.forEachState(stateProc); op.forEachTemp(defProc); op.forEachOutput(defProc); + indent2.outdent(); } // end of instruction iteration - blockData.get(block).liveGen = liveGen; - blockData.get(block).liveKill = liveKill; - blockData.get(block).liveIn = new BitSet(liveSize); - blockData.get(block).liveOut = new BitSet(liveSize); + BlockData blockSets = blockData.get(block); + blockSets.liveGen = liveGen; + blockSets.liveKill = liveKill; + blockSets.liveIn = new BitSet(liveSize); + blockSets.liveOut = new BitSet(liveSize); - if (getTraceLevel() >= 4) { - TTY.println("liveGen B%d %s", block.getId(), blockData.get(block).liveGen); - TTY.println("liveKill B%d %s", block.getId(), blockData.get(block).liveKill); - } + indent.log("liveGen B%d %s", block.getId(), blockSets.liveGen); + indent.log("liveKill B%d %s", block.getId(), blockSets.liveKill); + + indent.outdent(); } // end of block iteration } @@ -814,6 +811,7 @@ * {@link BlockData#liveIn} and {@link BlockData#liveOut}) for each block. */ void computeGlobalLiveSets() { + Indent indent = Debug.logAndIndent("compute global live sets"); int numBlocks = blockCount(); boolean changeOccurred; boolean changeOccurredInBlock; @@ -825,9 +823,12 @@ do { changeOccurred = false; + Indent indent2 = indent.logAndIndent("new iteration %d", iterationCount); + // iterate all blocks in reverse order for (int i = numBlocks - 1; i >= 0; i--) { Block block = blockAt(i); + BlockData blockSets = blockData.get(block); changeOccurredInBlock = false; @@ -844,10 +845,10 @@ liveOut.clear(); } - if (!blockData.get(block).liveOut.equals(liveOut)) { + if (!blockSets.liveOut.equals(liveOut)) { // A change occurred. Swap the old and new live out sets to avoid copying. - BitSet temp = blockData.get(block).liveOut; - blockData.get(block).liveOut = liveOut; + BitSet temp = blockSets.liveOut; + blockSets.liveOut = liveOut; liveOut = temp; changeOccurred = true; @@ -860,15 +861,13 @@ // !liveKill(block)) // note: liveIn has to be computed only in first iteration or if liveOut has // changed! - BitSet liveIn = blockData.get(block).liveIn; + BitSet liveIn = blockSets.liveIn; liveIn.clear(); - liveIn.or(blockData.get(block).liveOut); - liveIn.andNot(blockData.get(block).liveKill); - liveIn.or(blockData.get(block).liveGen); - } + liveIn.or(blockSets.liveOut); + liveIn.andNot(blockSets.liveKill); + liveIn.or(blockSets.liveGen); - if (getTraceLevel() >= 4) { - traceLiveness(changeOccurredInBlock, iterationCount, block); + indent2.log("block %d: livein = %s, liveout = %s", block.getId(), liveIn, blockSets.liveOut); } } iterationCount++; @@ -876,6 +875,7 @@ if (changeOccurred && iterationCount > 50) { throw new BailoutException("too many iterations in computeGlobalLiveSets"); } + indent2.outdent(); } while (changeOccurred); if (DetailedAsserts.getValue()) { @@ -888,55 +888,53 @@ if (DetailedAsserts.getValue()) { reportFailure(numBlocks); } - - TTY.println("preds=" + startBlock.getPredecessorCount() + ", succs=" + startBlock.getSuccessorCount()); - TTY.println("startBlock-ID: " + startBlock.getId()); - // bailout if this occurs in product mode. throw new GraalInternalError("liveIn set of first block must be empty: " + blockData.get(startBlock).liveIn); } + indent.outdent(); } private void reportFailure(int numBlocks) { - TTY.println(gen.getGraph().toString()); - TTY.println("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):"); + Indent indent = Debug.logAndIndent("report failure"); + indent.log("graph: %s", gen.getGraph()); + indent.log("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):"); BitSet startBlockLiveIn = blockData.get(ir.cfg.getStartBlock()).liveIn; for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) { Value operand = operandFor(operandNum); - TTY.println(" var %d; operand=%s; node=%s", operandNum, operand.toString(), gen.valueForOperand(operand)); + indent.log(" var %d; operand=%s; node=%s", operandNum, operand, gen.valueForOperand(operand)); } // print some additional information to simplify debugging for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) { Value operand = operandFor(operandNum); - TTY.println("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand.toString(), gen.valueForOperand(operand)); + final Indent indent2 = indent.logAndIndent("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand, gen.valueForOperand(operand)); Deque definedIn = new ArrayDeque<>(); HashSet usedIn = new HashSet<>(); for (Block block : sortedBlocks) { if (blockData.get(block).liveGen.get(operandNum)) { usedIn.add(block); - TTY.println("used in block B%d {", block.getId()); + indent2.log("used in block B%d {", block.getId()); for (LIRInstruction ins : ir.lir(block)) { - TTY.println(" " + ins.id() + ": " + ins.toString()); + indent2.log(" %d: %s", ins.id(), ins); ins.forEachState(new ValueProcedure() { @Override public Value doValue(Value liveStateOperand) { - TTY.println(" operand=" + liveStateOperand); + indent2.log(" operand=" + liveStateOperand); return liveStateOperand; } }); } - TTY.println("}"); + indent2.log("}"); } if (blockData.get(block).liveKill.get(operandNum)) { definedIn.add(block); - TTY.println("defined in block B%d {", block.getId()); + indent2.log("defined in block B%d {", block.getId()); for (LIRInstruction ins : ir.lir(block)) { - TTY.println(" " + ins.id() + ": " + ins.toString()); + indent2.log(" %d: %s", ins.id(), ins); } - TTY.println("}"); + indent2.log("}"); } } @@ -957,12 +955,13 @@ } } } - TTY.print("**** offending usages are in: "); + indent.log("**** offending usages are in: "); for (Block block : usedIn) { - TTY.print("B%d ", block.getId()); + indent2.log("B%d ", block.getId()); } - TTY.println(); + indent2.outdent(); } + indent.outdent(); } private void verifyLiveness() { @@ -977,21 +976,10 @@ } } - private void traceLiveness(boolean changeOccurredInBlock, int iterationCount, Block block) { - char c = iterationCount == 0 || changeOccurredInBlock ? '*' : ' '; - TTY.print("(%d) liveIn%c B%d ", iterationCount, c, block.getId()); - TTY.println(blockData.get(block).liveIn.toString()); - TTY.print("(%d) liveOut%c B%d ", iterationCount, c, block.getId()); - TTY.println(blockData.get(block).liveOut.toString()); - } - void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority, PlatformKind kind) { if (!isProcessed(operand)) { return; } - if (getTraceLevel() >= 2 && kind == null) { - TTY.println(" use %s from %d to %d (%s)", operand, from, to, registerPriority.name()); - } Interval interval = getOrCreateInterval(operand); if (kind != Kind.Illegal) { @@ -1002,15 +990,14 @@ // Register use position at even instruction id. interval.addUsePos(to & ~1, registerPriority); + + Debug.log("add use: %s, from %d to %d (%s)", interval, from, to, registerPriority.name()); } void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority, PlatformKind kind) { if (!isProcessed(operand)) { return; } - if (getTraceLevel() >= 2) { - TTY.println(" temp %s tempPos %d (%s)", operand, tempPos, RegisterPriority.MustHaveRegister.name()); - } Interval interval = getOrCreateInterval(operand); if (kind != Kind.Illegal) { @@ -1019,19 +1006,20 @@ interval.addRange(tempPos, tempPos + 1); interval.addUsePos(tempPos, registerPriority); + interval.addMaterializationValue(null); + + Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name()); } boolean isProcessed(Value operand) { return !isRegister(operand) || attributes(asRegister(operand)).isAllocatable(); } - void addDef(AllocatableValue operand, int defPos, RegisterPriority registerPriority, PlatformKind kind) { + void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority, PlatformKind kind) { if (!isProcessed(operand)) { return; } - if (getTraceLevel() >= 2) { - TTY.println(" def %s defPos %d (%s)", operand, defPos, registerPriority.name()); - } + int defPos = op.id(); Interval interval = getOrCreateInterval(operand); if (kind != Kind.Illegal) { @@ -1050,9 +1038,7 @@ // also add register priority for dead intervals interval.addRange(defPos, defPos + 1); interval.addUsePos(defPos, registerPriority); - if (getTraceLevel() >= 2) { - TTY.println("Warning: def of operand %s at %d occurs without use", operand, defPos); - } + Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos); } changeSpillDefinitionPos(interval, defPos); @@ -1060,6 +1046,9 @@ // detection of method-parameters and roundfp-results interval.setSpillState(SpillState.StartInMemory); } + interval.addMaterializationValue(gen.getMaterializedValue(op, operand)); + + Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name()); } /** @@ -1153,6 +1142,9 @@ } void buildIntervals() { + + Indent indent = Debug.logAndIndent("build intervals"); + intervalsSize = operandSize(); intervals = new Interval[intervalsSize + INITIAL_SPLIT_INTERVALS_CAPACITY]; @@ -1161,7 +1153,10 @@ // iterate all blocks in reverse order for (int i = blockCount() - 1; i >= 0; i--) { + Block block = blockAt(i); + Indent indent2 = indent.logAndIndent("handle block %d", block.getId()); + List instructions = ir.lir(block); final int blockFrom = getFirstLirInstructionId(block); int blockTo = getLastLirInstructionId(block); @@ -1174,9 +1169,7 @@ for (int operandNum = live.nextSetBit(0); operandNum >= 0; operandNum = live.nextSetBit(operandNum + 1)) { assert live.get(operandNum) : "should not stop here otherwise"; AllocatableValue operand = operandFor(operandNum); - if (getTraceLevel() >= 2) { - TTY.println("live in %s to %d", operand, blockTo + 2); - } + indent.log("live in %d: %s", operandNum, operand); addUse(operand, blockFrom, blockTo + 2, RegisterPriority.None, Kind.Illegal); @@ -1195,6 +1188,8 @@ final LIRInstruction op = instructions.get(j); final int opId = op.id(); + Indent indent3 = indent2.logAndIndent("handle inst %d: %s", opId, op); + // add a temp range for each register if operation destroys caller-save registers if (op.destroysCallerSavedRegisters()) { for (Register r : callerSaveRegs) { @@ -1202,9 +1197,7 @@ addTemp(r.asValue(), opId, RegisterPriority.None, Kind.Illegal); } } - if (getTraceLevel() >= 4) { - TTY.println("operation destroys all caller-save registers"); - } + indent.log("operation destroys all caller-save registers"); } op.forEachOutput(new ValueProcedure() { @@ -1212,7 +1205,7 @@ @Override public Value doValue(Value operand, OperandMode mode, EnumSet flags) { if (isVariableOrRegister(operand)) { - addDef((AllocatableValue) operand, opId, registerPriorityOfOutputOperand(op), operand.getPlatformKind()); + addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op), operand.getPlatformKind()); addRegisterHint(op, operand, mode, flags, true); } return operand; @@ -1270,7 +1263,10 @@ // special steps for some instructions (especially moves) handleMethodArguments(op); + indent3.outdent(); + } // end of instruction iteration + indent2.outdent(); } // end of block iteration // add the range [0, 1] to all fixed intervals. @@ -1280,6 +1276,7 @@ interval.addRange(0, 1); } } + indent.outdent(); } // * Phase 5: actual register allocation @@ -1431,6 +1428,7 @@ }; public void allocateRegisters() { + Indent indent = Debug.logAndIndent("allocate registers"); Interval precoloredIntervals; Interval notPrecoloredIntervals; @@ -1442,6 +1440,7 @@ LinearScanWalker lsw = new LinearScanWalker(this, precoloredIntervals, notPrecoloredIntervals); lsw.walk(); lsw.finishAllocation(); + indent.outdent(); } // * Phase 6: resolve data flow @@ -1546,6 +1545,8 @@ * have been split. */ void resolveDataFlow() { + Indent indent = Debug.logAndIndent("resolve data flow"); + int numBlocks = blockCount(); MoveResolver moveResolver = new MoveResolver(this); BitSet blockCompleted = new BitSet(numBlocks); @@ -1608,6 +1609,7 @@ } } } + indent.outdent(); } // * Phase 7: assign register numbers back to LIR @@ -1652,15 +1654,18 @@ interval = splitChildAtOpId(interval, opId, mode); } + if (isIllegal(interval.location()) && interval.canMaterialize()) { + return interval.getMaterializedValue(); + } return interval.location(); } - IntervalWalker initComputeOopMaps() { + protected IntervalWalker initIntervalWalker(IntervalPredicate predicate) { // setup lists of potential oops for walking Interval oopIntervals; Interval nonOopIntervals; - oopIntervals = createUnhandledLists(IS_OOP_INTERVAL, null).first; + oopIntervals = createUnhandledLists(predicate, null).first; // intervals that have no oops inside need not to be processed. // to ensure a walking until the last instruction id, add a dummy interval @@ -1671,7 +1676,11 @@ return new IntervalWalker(this, oopIntervals, nonOopIntervals); } - void computeOopMap(IntervalWalker iw, LIRInstruction op, BitSet registerRefMap, BitSet frameRefMap) { + /** + * Visits all intervals for a frame state. The frame state use this information to build the OOP + * maps. + */ + void markFrameLocations(IntervalWalker iw, LIRInstruction op, LIRFrameState info) { if (getTraceLevel() >= 3) { TTY.println("creating oop map at opId %d", op.id()); } @@ -1694,11 +1703,11 @@ // moves, any intervals which end at this instruction are included // in the oop map since we may safepoint while doing the patch // before we've consumed the inputs. - if (op.id() < interval.currentTo()) { + if (op.id() < interval.currentTo() && !isIllegal(interval.location())) { // caller-save registers must not be included into oop-maps at calls assert !op.destroysCallerSavedRegisters() || !isRegister(operand) || !isCallerSave(operand) : "interval is in a caller-save register at a call . register will be overwritten"; - frameMap.setReference(interval.location(), registerRefMap, frameRefMap); + info.markLocation(interval.location(), frameMap); // Spill optimization: when the stack value is guaranteed to be always correct, // then it must be added to the oop map even if the interval is currently in a @@ -1707,7 +1716,7 @@ assert interval.spillDefinitionPos() > 0 : "position not set correctly"; assert interval.spillSlot() != null : "no spill slot assigned"; assert !isRegister(interval.operand) : "interval is on stack : so stack slot is registered twice"; - frameMap.setReference(interval.spillSlot(), registerRefMap, frameRefMap); + info.markLocation(interval.spillSlot(), frameMap); } } } @@ -1718,9 +1727,8 @@ } private void computeDebugInfo(IntervalWalker iw, final LIRInstruction op, LIRFrameState info) { - BitSet registerRefMap = op.destroysCallerSavedRegisters() && callKillsRegisters ? null : frameMap.initRegisterRefMap(); - BitSet frameRefMap = frameMap.initFrameRefMap(); - computeOopMap(iw, op, registerRefMap, frameRefMap); + info.initDebugInfo(frameMap, !op.destroysCallerSavedRegisters() || !callKillsRegisters); + markFrameLocations(iw, op, info); info.forEachState(new ValueProcedure() { @@ -1750,12 +1758,11 @@ // the intervals // if the interval is not live, colorLirOperand will cause an assert on failure Value result = colorLirOperand((Variable) operand, tempOpId, mode); - assert !hasCall(tempOpId) || isStackSlot(result) || !isCallerSave(result) : "cannot have caller-save register operands at calls"; + assert !hasCall(tempOpId) || isStackSlot(result) || isConstant(result) || !isCallerSave(result) : "cannot have caller-save register operands at calls"; return result; } }); - - info.finish(registerRefMap, frameRefMap); + info.finish(op, frameMap); } private void assignLocations(List instructions, final IntervalWalker iw) { @@ -1811,7 +1818,7 @@ } private void assignLocations() { - IntervalWalker iw = initComputeOopMaps(); + IntervalWalker iw = initIntervalWalker(IS_STACK_INTERVAL); for (Block block : sortedBlocks) { assignLocations(ir.lir(block), iw); } @@ -1819,6 +1826,8 @@ public void allocate() { + Indent indent = Debug.logAndIndent(false, "allocate %s", gen.getGraph().method()); + try (Scope s = Debug.scope("LifetimeAnalysis")) { numberInstructions(); printLir("Before register allocation", true); @@ -1869,11 +1878,21 @@ printLir("After register number assignment", true); EdgeMoveOptimizer.optimize(ir); ControlFlowOptimizer.optimize(ir); + + /* + * Temporarily disabled because of problem in specjvm2008. TODO: fix the problem and + * re-enable it. + * + * RedundantMoveElimination.optimize(ir, frameMap, gen.getGraph().method()); + */ + NullCheckOptimizer.optimize(ir, target.implicitNullCheckLimit); printLir("After control flow optimization", false); } catch (Throwable e) { throw Debug.handle(e); } + + indent.outdent(); } void printIntervals(String label) { @@ -1998,7 +2017,7 @@ } Value l1 = i1.location(); Value l2 = i2.location(); - if (i1.intersects(i2) && (l1.equals(l2))) { + if (i1.intersects(i2) && !isIllegal(l1) && (l1.equals(l2))) { if (DetailedAsserts.getValue()) { TTY.println("Intervals %d and %d overlap and have the same register assigned", i1.operandNumber, i2.operandNumber); TTY.println(i1.logString(this)); diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java Tue Dec 17 15:44:23 2013 +0100 @@ -399,66 +399,52 @@ // 1) the left part has already a location assigned // 2) the right part is sorted into to the unhandled-list void splitBeforeUsage(Interval interval, int minSplitPos, int maxSplitPos) { - if (getTraceLevel() >= 2) { - TTY.println("----- splitting interval: "); - } - if (getTraceLevel() >= 4) { - TTY.println(interval.logString(allocator)); - } - if (getTraceLevel() >= 2) { - TTY.println(" between %d and %d", minSplitPos, maxSplitPos); - } + + try (Indent indent = Debug.logAndIndent("splitting interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) { - assert interval.from() < minSplitPos : "cannot split at start of interval"; - assert currentPosition < minSplitPos : "cannot split before current position"; - assert minSplitPos <= maxSplitPos : "invalid order"; - assert maxSplitPos <= interval.to() : "cannot split after end of interval"; + assert interval.from() < minSplitPos : "cannot split at start of interval"; + assert currentPosition < minSplitPos : "cannot split before current position"; + assert minSplitPos <= maxSplitPos : "invalid order"; + assert maxSplitPos <= interval.to() : "cannot split after end of interval"; + + int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, true); - int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, true); - - assert minSplitPos <= optimalSplitPos && optimalSplitPos <= maxSplitPos : "out of range"; - assert optimalSplitPos <= interval.to() : "cannot split after end of interval"; - assert optimalSplitPos > interval.from() : "cannot split at start of interval"; + assert minSplitPos <= optimalSplitPos && optimalSplitPos <= maxSplitPos : "out of range"; + assert optimalSplitPos <= interval.to() : "cannot split after end of interval"; + assert optimalSplitPos > interval.from() : "cannot split at start of interval"; - if (optimalSplitPos == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) { - // the split position would be just before the end of the interval - // . no split at all necessary - if (getTraceLevel() >= 4) { - TTY.println(" no split necessary because optimal split position is at end of interval"); + if (optimalSplitPos == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) { + // the split position would be just before the end of the interval + // . no split at all necessary + indent.log("no split necessary because optimal split position is at end of interval"); + return; } - return; - } - // must calculate this before the actual split is performed and before split position is - // moved to odd opId - boolean moveNecessary = !allocator.isBlockBegin(optimalSplitPos) && !interval.hasHoleBetween(optimalSplitPos - 1, optimalSplitPos); + // must calculate this before the actual split is performed and before split position is + // moved to odd opId + boolean moveNecessary = !allocator.isBlockBegin(optimalSplitPos) && !interval.hasHoleBetween(optimalSplitPos - 1, optimalSplitPos); - if (!allocator.isBlockBegin(optimalSplitPos)) { - // move position before actual instruction (odd opId) - optimalSplitPos = (optimalSplitPos - 1) | 1; - } + if (!allocator.isBlockBegin(optimalSplitPos)) { + // move position before actual instruction (odd opId) + optimalSplitPos = (optimalSplitPos - 1) | 1; + } - if (getTraceLevel() >= 4) { - TTY.println(" splitting at position %d", optimalSplitPos); - } - assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary"; - assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary"; + indent.log("splitting at position %d", optimalSplitPos); - Interval splitPart = interval.split(optimalSplitPos, allocator); + assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary"; + assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary"; - splitPart.setInsertMoveWhenActivated(moveNecessary); + Interval splitPart = interval.split(optimalSplitPos, allocator); - assert splitPart.from() >= currentInterval.currentFrom() : "cannot append new interval before current walk position"; - unhandledLists.addToListSortedByStartAndUsePositions(RegisterBinding.Any, splitPart); + splitPart.setInsertMoveWhenActivated(moveNecessary); - if (getTraceLevel() >= 2) { - TTY.println(" split interval in two parts (insertMoveWhenActivated: %b)", moveNecessary); - } - if (getTraceLevel() >= 2) { - TTY.print(" "); - TTY.println(interval.logString(allocator)); - TTY.print(" "); - TTY.println(splitPart.logString(allocator)); + assert splitPart.from() >= currentInterval.currentFrom() : "cannot append new interval before current walk position"; + unhandledLists.addToListSortedByStartAndUsePositions(RegisterBinding.Any, splitPart); + + if (Debug.isLogEnabled()) { + indent.log("left interval %s: %s", moveNecessary ? " " : "", interval.logString(allocator)); + indent.log("right interval %s: %s", moveNecessary ? "(move)" : "", splitPart.logString(allocator)); + } } } @@ -472,11 +458,7 @@ int maxSplitPos = currentPosition; int minSplitPos = Math.max(interval.previousUsage(RegisterPriority.ShouldHaveRegister, maxSplitPos) + 1, interval.from()); - if (getTraceLevel() >= 2) { - TTY.print("----- splitting and spilling interval: "); - TTY.println(interval.logString(allocator)); - TTY.println(" between %d and %d", minSplitPos, maxSplitPos); - } + Indent indent = Debug.logAndIndent("splitting and spilling interval %s between %d and %d", interval, minSplitPos, maxSplitPos); assert interval.state == State.Active : "why spill interval that is not active?"; assert interval.from() <= minSplitPos : "cannot split before start of interval"; @@ -486,33 +468,31 @@ if (minSplitPos == interval.from()) { // the whole interval is never used, so spill it entirely to memory - if (getTraceLevel() >= 2) { - TTY.println(" spilling entire interval because split pos is at beginning of interval"); - TTY.println(" use positions: " + interval.usePosList().size()); - } - assert interval.firstUsage(RegisterPriority.ShouldHaveRegister) > currentPosition : "interval must not have use position before currentPosition"; + + try (Indent indent2 = indent.logAndIndent("spilling entire interval because split pos is at beginning of interval (use positions: %d)", interval.usePosList().size())) { - allocator.assignSpillSlot(interval); - allocator.changeSpillState(interval, minSplitPos); + assert interval.firstUsage(RegisterPriority.ShouldHaveRegister) > currentPosition : "interval must not have use position before currentPosition"; + + allocator.assignSpillSlot(interval); + allocator.changeSpillState(interval, minSplitPos); - // Also kick parent intervals out of register to memory when they have no use - // position. This avoids short interval in register surrounded by intervals in - // memory . avoid useless moves from memory to register and back - Interval parent = interval; - while (parent != null && parent.isSplitChild()) { - parent = parent.getSplitChildBeforeOpId(parent.from()); + // Also kick parent intervals out of register to memory when they have no use + // position. This avoids short interval in register surrounded by intervals in + // memory . avoid useless moves from memory to register and back + Interval parent = interval; + while (parent != null && parent.isSplitChild()) { + parent = parent.getSplitChildBeforeOpId(parent.from()); - if (isRegister(parent.location())) { - if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) { - // parent is never used, so kick it out of its assigned register - if (getTraceLevel() >= 4) { - TTY.println(" kicking out interval %d out of its register because it is never used", parent.operandNumber); + if (isRegister(parent.location())) { + if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) { + // parent is never used, so kick it out of its assigned register + indent2.log("kicking out interval %d out of its register because it is never used", parent.operandNumber); + allocator.assignSpillSlot(parent); + } else { + // do not go further back because the register is actually used by + // the interval + parent = null; } - allocator.assignSpillSlot(parent); - } else { - // do not go further back because the register is actually used by the - // interval - parent = null; } } } @@ -530,35 +510,30 @@ optimalSplitPos = (optimalSplitPos - 1) | 1; } - if (getTraceLevel() >= 4) { - TTY.println(" splitting at position %d", optimalSplitPos); - } - assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary"; - assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary"; + try (Indent indent2 = indent.logAndIndent("splitting at position %d", optimalSplitPos)) { + assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary"; + assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary"; - Interval spilledPart = interval.split(optimalSplitPos, allocator); - allocator.assignSpillSlot(spilledPart); - allocator.changeSpillState(spilledPart, optimalSplitPos); + Interval spilledPart = interval.split(optimalSplitPos, allocator); + allocator.assignSpillSlot(spilledPart); + allocator.changeSpillState(spilledPart, optimalSplitPos); - if (!allocator.isBlockBegin(optimalSplitPos)) { - if (getTraceLevel() >= 4) { - TTY.println(" inserting move from interval %d to %d", interval.operandNumber, spilledPart.operandNumber); + if (!allocator.isBlockBegin(optimalSplitPos)) { + indent2.log("inserting move from interval %d to %d", interval.operandNumber, spilledPart.operandNumber); + insertMove(optimalSplitPos, interval, spilledPart); } - insertMove(optimalSplitPos, interval, spilledPart); - } - // the currentSplitChild is needed later when moves are inserted for reloading - assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild"; - spilledPart.makeCurrentSplitChild(); + // the currentSplitChild is needed later when moves are inserted for reloading + assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild"; + spilledPart.makeCurrentSplitChild(); - if (getTraceLevel() >= 2) { - TTY.println(" split interval in two parts"); - TTY.print(" "); - TTY.println(interval.logString(allocator)); - TTY.print(" "); - TTY.println(spilledPart.logString(allocator)); + if (Debug.isLogEnabled()) { + indent2.log("left interval: %s", interval.logString(allocator)); + indent2.log("spilled interval : %s", spilledPart.logString(allocator)); + } } } + indent.outdent(); } void splitStackInterval(Interval interval) { @@ -883,13 +858,8 @@ Interval interval = currentInterval; boolean result = true; - if (getTraceLevel() >= 2) { - TTY.println("+++++ activating interval " + interval.logString(allocator)); - } - - if (getTraceLevel() >= 4) { - TTY.println(" splitParent: %s, insertMoveWhenActivated: %b", interval.splitParent().operandNumber, interval.insertMoveWhenActivated()); - } + Indent indent = Debug.logAndIndent("activating interval %s, splitParent: %s, insertMoveWhenActivated: %b", interval.logString(allocator), interval.splitParent().operandNumber, + interval.insertMoveWhenActivated()); final Value operand = interval.operand; if (interval.location() != null && isStackSlot(interval.location())) { @@ -940,6 +910,8 @@ } interval.makeCurrentSplitChild(); + indent.outdent(); + return result; // true = interval is moved to active list } diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/MoveResolver.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/MoveResolver.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/MoveResolver.java Tue Dec 17 15:44:23 2013 +0100 @@ -199,9 +199,7 @@ insertionBuffer.append(insertIdx, allocator.ir.spillMoveFactory.createMove(toOpr, fromOpr)); - if (allocator.getTraceLevel() >= 4) { - TTY.println("MoveResolver: inserted move from %d (%s) to %d (%s)", fromInterval.operandNumber, fromInterval.location(), toInterval.operandNumber, toInterval.location()); - } + Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx); } private void insertMove(Value fromOpr, Interval toInterval) { @@ -211,9 +209,7 @@ AllocatableValue toOpr = toInterval.operand; insertionBuffer.append(insertIdx, allocator.ir.spillMoveFactory.createMove(toOpr, fromOpr)); - if (allocator.getTraceLevel() >= 4) { - TTY.print("MoveResolver: inserted move from constant %s to %d (%s)", fromOpr, toInterval.operandNumber, toInterval.location()); - } + Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx); } private void resolveMappings() { @@ -283,9 +279,7 @@ } spillInterval.assignLocation(spillSlot); - if (allocator.getTraceLevel() >= 4) { - TTY.println("created new Interval %s for spilling", spillInterval.operand); - } + Debug.log("created new Interval for spilling: %s", spillInterval); // insert a move from register to stack and update the mapping insertMove(fromInterval, spillInterval); @@ -325,9 +319,18 @@ } void addMapping(Interval fromInterval, Interval toInterval) { - if (allocator.getTraceLevel() >= 4) { - TTY.println("MoveResolver: adding mapping from interval %d (%s) to interval %d (%s)", fromInterval.operandNumber, fromInterval.location(), toInterval.operandNumber, toInterval.location()); + + if (isIllegal(toInterval.location()) && toInterval.canMaterialize()) { + Debug.log("no store to rematerializable interval %s needed", toInterval); + return; } + if (isIllegal(fromInterval.location()) && fromInterval.canMaterialize()) { + // Instead of a reload, re-materialize the value + Value rematValue = fromInterval.getMaterializedValue(); + addMapping(rematValue, toInterval); + return; + } + Debug.log("add move mapping from %s to %s", fromInterval, toInterval); assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval; assert fromInterval.kind() == toInterval.kind(); @@ -337,9 +340,8 @@ } void addMapping(Value fromOpr, Interval toInterval) { - if (allocator.getTraceLevel() >= 4) { - TTY.println("MoveResolver: adding mapping from %s to %d (%s)", fromOpr, toInterval.operandNumber, toInterval.location()); - } + Debug.log("add move mapping from %s to %s", fromOpr, toInterval); + assert isConstant(fromOpr) : "only for constants"; mappingFrom.add(null); diff -r 136df94b5aa8 -r cf7b5b507541 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 Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java Tue Dec 17 15:44:23 2013 +0100 @@ -28,6 +28,7 @@ import static com.oracle.graal.lir.LIR.*; import static com.oracle.graal.lir.LIRValueUtil.*; import static com.oracle.graal.nodes.ConstantNode.*; +import static com.oracle.graal.phases.GraalOptions.*; import java.util.*; import java.util.Map.Entry; @@ -39,10 +40,7 @@ import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.lir.*; -import com.oracle.graal.lir.StandardOp.BlockEndOp; -import com.oracle.graal.lir.StandardOp.JumpOp; -import com.oracle.graal.lir.StandardOp.LabelOp; -import com.oracle.graal.lir.StandardOp.NoOp; +import com.oracle.graal.lir.StandardOp.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.PhiNode.PhiType; import com.oracle.graal.nodes.calc.*; @@ -136,6 +134,19 @@ public String toString() { return block + "#" + op; } + + /** + * Removes the {@link #op} from its original location if it is still at that location. + */ + public void unpin(LIR lir) { + if (index >= 0) { + // Replace the move with a filler op so that the operation + // list does not need to be adjusted. + List instructions = lir.lir(block); + instructions.set(index, new NoOp(null, -1)); + index = -1; + } + } } private Map constantLoads; @@ -176,6 +187,19 @@ this.printIRWithLIR = Options.PrintIRWithLIR.getValue(); } + /** + * Returns a value for a interval definition, which can be used for re-materialization. + * + * @param op An instruction which defines a value + * @param operand The destination operand of the instruction + * @return Returns the value which is moved to the instruction and which can be reused at all + * reload-locations in case the interval of this instruction is spilled. Currently this + * can only be a {@link Constant}. + */ + public Constant getMaterializedValue(LIRInstruction op, Value operand) { + return null; + } + @SuppressWarnings("hiding") protected DebugInfoBuilder createDebugInfoBuilder(NodeMap nodeOperands) { return new DebugInfoBuilder(nodeOperands); @@ -248,7 +272,6 @@ LoadConstant load = constantLoads.get(value); if (load == null) { int index = lir.lir(currentBlock).size(); - // loadedValue = newVariable(value.getPlatformKind()); loadedValue = emitMove(value); LIRInstruction op = lir.lir(currentBlock).get(index); constantLoads.put(value, new LoadConstant(loadedValue, currentBlock, index, op)); @@ -256,13 +279,7 @@ Block dominator = ControlFlowGraph.commonDominator(load.block, currentBlock); loadedValue = load.variable; if (dominator != load.block) { - if (load.index >= 0) { - // Replace the move with a filler op so that the operation - // list does not need to be adjusted. - List instructions = lir.lir(load.block); - instructions.set(load.index, new NoOp(null, -1)); - load.index = -1; - } + load.unpin(lir); } else { assert load.block != currentBlock || load.index < lir.lir(currentBlock).size(); } @@ -870,6 +887,19 @@ // Remove loads where all usages are in the same block. for (Iterator> iter = constantLoads.entrySet().iterator(); iter.hasNext();) { LoadConstant lc = iter.next().getValue(); + + // Move loads of constant outside of loops + if (OptScheduleOutOfLoops.getValue()) { + Block outOfLoopDominator = lc.block; + while (outOfLoopDominator.getLoop() != null) { + outOfLoopDominator = outOfLoopDominator.getDominator(); + } + if (outOfLoopDominator != lc.block) { + lc.unpin(lir); + lc.block = outOfLoopDominator; + } + } + if (lc.index != -1) { assert lir.lir(lc.block).get(lc.index) == lc.op; iter.remove(); diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java Tue Dec 17 15:44:23 2013 +0100 @@ -37,7 +37,40 @@ public class AMD64HotSpotBackendFactory implements HotSpotBackendFactory { protected Architecture createArchitecture(HotSpotVMConfig config) { - return new AMD64(config.useSSE, config.useAVX); + return new AMD64(computeFeatures(config)); + } + + protected EnumSet computeFeatures(HotSpotVMConfig config) { + // Configure the feature set using the HotSpot flag settings. + EnumSet features = EnumSet.noneOf(AMD64.CPUFeature.class); + assert config.useSSE >= 2 : "minimum config for x64"; + features.add(AMD64.CPUFeature.SSE); + features.add(AMD64.CPUFeature.SSE2); + if (config.useSSE > 2) { + features.add(AMD64.CPUFeature.SSE3); + } + if (config.useSSE > 3) { + features.add(AMD64.CPUFeature.SSE4); + } + if (config.useAVX > 0) { + features.add(AMD64.CPUFeature.AVX); + } + if (config.useAVX > 1) { + features.add(AMD64.CPUFeature.AVX2); + } + if (config.useCountLeadingZerosInstruction) { + features.add(AMD64.CPUFeature.LZCNT); + } + if (config.usePopCountInstruction) { + features.add(AMD64.CPUFeature.POPCNT); + } + if (config.useAESIntrinsics) { + features.add(AMD64.CPUFeature.AES); + } + if (config.allocatePrefetchInstr == 3) { + features.add(AMD64.CPUFeature.AMD_3DNOW_PREFETCH); + } + return features; } protected TargetDescription createTarget(HotSpotVMConfig config) { diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java Tue Dec 17 15:44:23 2013 +0100 @@ -40,6 +40,13 @@ private final Register[] allocatable; + /** + * The same as {@link #allocatable}, except if parameter registers are removed with the + * {@link #RegisterPressure} option. The caller saved registers always include all parameter + * registers. + */ + private final Register[] callerSaved; + private final HashMap categorized = new HashMap<>(); private final RegisterAttributes[] attributesMap; @@ -129,12 +136,20 @@ csl = null; allocatable = initAllocatable(config.useCompressedOops); + Set callerSaveSet = new HashSet<>(); + Collections.addAll(callerSaveSet, allocatable); + Collections.addAll(callerSaveSet, xmmParameterRegisters); + Collections.addAll(callerSaveSet, javaGeneralParameterRegisters); + Collections.addAll(callerSaveSet, nativeGeneralParameterRegisters); + callerSaved = callerSaveSet.toArray(new Register[callerSaveSet.size()]); + assert callerSaved.length == allocatable.length || RegisterPressure.getValue() != null; + attributesMap = RegisterAttributes.createMap(this, AMD64.allRegisters); } @Override public Register[] getCallerSaveRegisters() { - return getAllocatableRegisters(); + return callerSaved; } @Override diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotSafepointOp.java --- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotSafepointOp.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotSafepointOp.java Tue Dec 17 15:44:23 2013 +0100 @@ -42,20 +42,24 @@ public class AMD64HotSpotSafepointOp extends AMD64LIRInstruction { @State protected LIRFrameState state; - @Temp({OperandFlag.REG}) private AllocatableValue temp; + @Temp({OperandFlag.REG, OperandFlag.ILLEGAL}) private AllocatableValue temp; private final HotSpotVMConfig config; public AMD64HotSpotSafepointOp(LIRFrameState state, HotSpotVMConfig config, LIRGeneratorTool tool) { this.state = state; this.config = config; - temp = tool.newVariable(tool.target().wordKind); + if (isPollingPageFar(config)) { + temp = tool.newVariable(tool.target().wordKind); + } else { + // Don't waste a register if it's unneeded + temp = Value.ILLEGAL; + } } @Override public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm) { - RegisterValue scratch = (RegisterValue) temp; - emitCode(crb, asm, config, false, state, scratch.getRegister()); + emitCode(crb, asm, config, false, state, temp instanceof RegisterValue ? ((RegisterValue) temp).getRegister() : null); } /** @@ -76,7 +80,7 @@ if (state != null) { crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT); } - asm.movq(scratch, new AMD64Address(scratch)); + asm.testl(rax, new AMD64Address(scratch)); } else { crb.recordMark(atReturn ? MARK_POLL_RETURN_NEAR : MARK_POLL_NEAR); if (state != null) { @@ -84,7 +88,7 @@ } // The C++ code transforms the polling page offset into an RIP displacement // to the real address at that offset in the polling page. - asm.movq(scratch, new AMD64Address(rip, 0)); + asm.testl(rax, new AMD64Address(rip, 0)); } } } diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/CompileTheWorldTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/CompileTheWorldTest.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/CompileTheWorldTest.java Tue Dec 17 15:44:23 2013 +0100 @@ -39,7 +39,7 @@ boolean originalSetting = ExitVMOnException.getValue(); // Compile a couple classes in rt.jar String file = System.getProperty("java.home") + "/lib/rt.jar"; - new CompileTheWorld(file, 1, 5, false).compile(); + new CompileTheWorld(file, null, 1, 5, false).compile(); ExitVMOnException.setValue(originalSetting); } diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Tue Dec 17 15:44:23 2013 +0100 @@ -47,7 +47,7 @@ import com.oracle.graal.phases.*; import com.oracle.graal.phases.tiers.*; -public final class CompilationTask implements Runnable { +public class CompilationTask implements Runnable { public static final ThreadLocal withinEnqueue = new ThreadLocal() { @@ -77,7 +77,7 @@ return new CompilationTask(backend, plan, optimisticOpts, profilingInfo, method, entryBCI, id); } - private CompilationTask(HotSpotBackend backend, PhasePlan plan, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, HotSpotResolvedJavaMethod method, int entryBCI, int id) { + protected CompilationTask(HotSpotBackend backend, PhasePlan plan, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, HotSpotResolvedJavaMethod method, int entryBCI, int id) { assert id >= 0; this.backend = backend; this.plan = plan; @@ -120,6 +120,10 @@ public static final DebugTimer CodeInstallationTime = Debug.timer("CodeInstallation"); + protected Suites getSuites(HotSpotProviders providers) { + return providers.getSuites().getDefaultSuites(); + } + public void runCompilation() { /* * no code must be outside this try/finally because it could happen otherwise that @@ -164,7 +168,7 @@ } InlinedBytecodes.add(method.getCodeSize()); CallingConvention cc = getCallingConvention(providers.getCodeCache(), Type.JavaCallee, graph.method(), false); - Suites suites = providers.getSuites().getDefaultSuites(); + Suites suites = getSuites(providers); result = compileGraph(graph, cc, method, providers, backend, backend.getTarget(), graphCache, plan, optimisticOpts, profilingInfo, method.getSpeculationLog(), suites, true, new CompilationResult(), CompilationResultBuilderFactory.Default); diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java Tue Dec 17 15:44:23 2013 +0100 @@ -22,7 +22,9 @@ */ package com.oracle.graal.hotspot; +import static com.oracle.graal.hotspot.CompileTheWorld.Options.*; import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; +import static com.oracle.graal.nodes.StructuredGraph.*; import static com.oracle.graal.phases.GraalOptions.*; import java.io.*; @@ -34,10 +36,14 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.bytecode.*; import com.oracle.graal.debug.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.hotspot.HotSpotOptions.OptionConsumer; import com.oracle.graal.hotspot.bridge.*; import com.oracle.graal.hotspot.meta.*; -import com.oracle.graal.nodes.*; +import com.oracle.graal.options.*; +import com.oracle.graal.options.OptionValue.OverrideScope; import com.oracle.graal.phases.*; +import com.oracle.graal.phases.tiers.*; import com.oracle.graal.replacements.*; /** @@ -45,6 +51,83 @@ */ public final class CompileTheWorld { + static class Options { + // @formatter:off + @Option(help = "Compile all methods in all classes on given class path") + public static final OptionValue CompileTheWorldClasspath = new OptionValue<>(null); + @Option(help = "First class to consider when using -XX:+CompileTheWorld") + public static final OptionValue CompileTheWorldStartAt = new OptionValue<>(1); + @Option(help = "Last class to consider when using -XX:+CompileTheWorld") + public static final OptionValue CompileTheWorldStopAt = new OptionValue<>(Integer.MAX_VALUE); + @Option(help = "Option value overrides to use during compile the world. For example, " + + "to disable inlining and partial escape analysis specify '-PartialEscapeAnalysis -Inline'. " + + "The format for each option is the same as on the command line just without the '-G:' prefix.") + public static final OptionValue CompileTheWorldConfig = new OptionValue<>(null); + // @formatter:on + } + + /** + * A mechanism for overriding Graal options that effect compilation. A {@link Config} object + * should be used in a try-with-resources statement to ensure overriding of options is scoped + * properly. For example: + * + *
+     *     Config config = ...;
+     *     try (AutoCloseable s = config == null ? null : config.apply()) {
+     *         // perform a Graal compilation
+     *     }
+     * 
+ */ + @SuppressWarnings("serial") + static class Config extends HashMap, Object> implements AutoCloseable, OptionConsumer { + OverrideScope scope; + + /** + * Creates a {@link Config} object by parsing a set of space separated override options. + * + * @param options a space separated set of option value settings with each option setting in + * a format compatible with + * {@link HotSpotOptions#parseOption(String, OptionConsumer)} + */ + Config(String options) { + for (String option : options.split("\\s+")) { + if (!HotSpotOptions.parseOption(option, this)) { + throw new GraalInternalError("Invalid option specified: %s", option); + } + } + } + + /** + * Applies the overrides represented by this object. The overrides are in effect until + * {@link #close()} is called on this object. + */ + Config apply() { + assert scope == null; + scope = OptionValue.override(this); + return this; + } + + public void close() { + assert scope != null; + scope.close(); + + scope = null; + + } + + public void set(OptionDescriptor desc, Object value) { + put(desc.getOptionValue(), value); + } + } + + static Config parseConfig(String input) { + if (input == null) { + return null; + } else { + return new Config(input); + } + } + /** * This is our magic token to trigger reading files from the boot class path. */ @@ -54,13 +137,13 @@ private final HotSpotGraalRuntime runtime = runtime(); private final VMToCompilerImpl vmToCompiler = (VMToCompilerImpl) runtime.getVMToCompiler(); - /** List of Zip/Jar files to compile (see {@link GraalOptions#CompileTheWorld}. */ + /** List of Zip/Jar files to compile (see {@link #CompileTheWorldClasspath}. */ private final String files; - /** Class index to start compilation at (see {@link GraalOptions#CompileTheWorldStartAt}. */ + /** Class index to start compilation at (see {@link #CompileTheWorldStartAt}. */ private final int startAt; - /** Class index to stop compilation at (see {@link GraalOptions#CompileTheWorldStopAt}. */ + /** Class index to stop compilation at (see {@link #CompileTheWorldStopAt}. */ private final int stopAt; // Counters @@ -69,28 +152,30 @@ private long compileTime = 0; private boolean verbose; + private final Config config; /** - * Create a compile-the-world instance with default values from - * {@link GraalOptions#CompileTheWorld}, {@link GraalOptions#CompileTheWorldStartAt} and - * {@link GraalOptions#CompileTheWorldStopAt}. + * Creates a compile-the-world instance with default values from + * {@link Options#CompileTheWorldClasspath}, {@link Options#CompileTheWorldStartAt} and + * {@link Options#CompileTheWorldStopAt}. */ public CompileTheWorld() { - this(CompileTheWorld.getValue(), CompileTheWorldStartAt.getValue(), CompileTheWorldStopAt.getValue(), true); + this(CompileTheWorldClasspath.getValue(), parseConfig(CompileTheWorldConfig.getValue()), CompileTheWorldStartAt.getValue(), CompileTheWorldStopAt.getValue(), true); } /** - * Create a compile-the-world instance. + * Creates a compile-the-world instance. * * @param files {@link File#pathSeparator} separated list of Zip/Jar files to compile * @param startAt index of the class file to start compilation at * @param stopAt index of the class file to stop compilation at */ - public CompileTheWorld(String files, int startAt, int stopAt, boolean verbose) { + public CompileTheWorld(String files, Config config, int startAt, int stopAt, boolean verbose) { this.files = files; this.startAt = startAt; this.stopAt = stopAt; this.verbose = verbose; + this.config = config; // We don't want the VM to exit when a method fails to compile... ExitVMOnException.setValue(false); @@ -101,12 +186,10 @@ } /** - * Compile all methods in all classes in the Zip/Jar files in - * {@link GraalOptions#CompileTheWorld}. If the GraalOptions.CompileTheWorld contains the magic - * token {@link CompileTheWorld#SUN_BOOT_CLASS_PATH} passed up from HotSpot we take the files - * from the boot class path. - * - * @throws Throwable + * Compiles all methods in all classes in the Zip/Jar archive files in + * {@link #CompileTheWorldClasspath}. If {@link #CompileTheWorldClasspath} contains the magic + * token {@link #SUN_BOOT_CLASS_PATH} passed up from HotSpot we take the files from the boot + * class path. */ public void compile() throws Throwable { if (SUN_BOOT_CLASS_PATH.equals(files)) { @@ -145,7 +228,7 @@ } /** - * Compile all methods in all classes in the Zip/Jar files passed. + * Compiles all methods in all classes in the Zip/Jar files passed. * * @param fileList {@link File#pathSeparator} separated list of Zip/Jar files to compile * @throws Throwable @@ -186,7 +269,7 @@ String className = je.getName().substring(0, je.getName().length() - ".class".length()); classFileCounter++; - try { + try (AutoCloseable s = config == null ? null : config.apply()) { // Load and initialize class Class javaClass = Class.forName(className.replace('/', '.'), true, loader); @@ -233,25 +316,49 @@ } /** - * Helper method to schedule a method for compilation and gather some statistics. + * A compilation task that creates a fresh compilation suite for its compilation. This is + * required so that a CTW compilation can be {@linkplain Config configured} differently from a + * VM triggered compilation. + */ + static class CTWCompilationTask extends CompilationTask { + + CTWCompilationTask(HotSpotBackend backend, PhasePlan plan, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, HotSpotResolvedJavaMethod method, int entryBCI, int id) { + super(backend, plan, optimisticOpts, profilingInfo, method, entryBCI, id); + } + + @Override + protected Suites getSuites(HotSpotProviders providers) { + return providers.getSuites().createSuites(); + } + } + + /** + * Compiles a method and gathers some statistics. */ private void compileMethod(HotSpotResolvedJavaMethod method) { try { long start = System.currentTimeMillis(); - vmToCompiler.compileMethod(method, StructuredGraph.INVOCATION_ENTRY_BCI, true); + + final ProfilingInfo profilingInfo = method.getCompilationProfilingInfo(false); + final OptimisticOptimizations optimisticOpts = new OptimisticOptimizations(profilingInfo); + int id = vmToCompiler.allocateCompileTaskId(); + HotSpotBackend backend = runtime.getHostBackend(); + PhasePlan phasePlan = vmToCompiler.createPhasePlan(backend.getProviders(), optimisticOpts, false); + CompilationTask task = new CTWCompilationTask(backend, phasePlan, optimisticOpts, profilingInfo, method, INVOCATION_ENTRY_BCI, id); + task.runCompilation(); + compileTime += (System.currentTimeMillis() - start); compiledMethodsCounter++; method.reprofile(); // makes the method also not-entrant } catch (Throwable t) { // Catch everything and print a message - println("CompileTheWorld (%d) : Error compiling method: %s", classFileCounter, MetaUtil.format("%H.%n(%p):%r", method)); + println("CompileTheWorldClasspath (%d) : Error compiling method: %s", classFileCounter, MetaUtil.format("%H.%n(%p):%r", method)); t.printStackTrace(TTY.cachedOut); } } /** - * Helper method for CompileTheWorld to determine if a method should be compiled (Cf. - * CompilationPolicy::can_be_compiled). + * Determines if a method should be compiled (Cf. CompilationPolicy::can_be_compiled). * * @return true if it can be compiled, false otherwise */ diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Tue Dec 17 15:44:23 2013 +0100 @@ -23,7 +23,9 @@ package com.oracle.graal.hotspot; import static com.oracle.graal.graph.UnsafeAccess.*; +import static com.oracle.graal.hotspot.CompileTheWorld.*; import static com.oracle.graal.hotspot.HotSpotGraalRuntime.Options.*; +import static com.oracle.graal.phases.GraalOptions.*; import java.lang.reflect.*; import java.util.*; @@ -232,23 +234,23 @@ initMirror(typeVoid); // Set some global options: - if (config.compileTheWorld) { - GraalOptions.CompileTheWorld.setValue(CompileTheWorld.SUN_BOOT_CLASS_PATH); + if (config.compileTheWorld && CompileTheWorld.Options.CompileTheWorldClasspath.getValue() == null) { + CompileTheWorld.Options.CompileTheWorldClasspath.setValue(SUN_BOOT_CLASS_PATH); } if (config.compileTheWorldStartAt != 1) { - GraalOptions.CompileTheWorldStartAt.setValue(config.compileTheWorldStartAt); + CompileTheWorld.Options.CompileTheWorldStartAt.setValue(config.compileTheWorldStartAt); } if (config.compileTheWorldStopAt != Integer.MAX_VALUE) { - GraalOptions.CompileTheWorldStopAt.setValue(config.compileTheWorldStopAt); + CompileTheWorld.Options.CompileTheWorldStopAt.setValue(config.compileTheWorldStopAt); } // Only set HotSpotPrintCompilation and HotSpotPrintInlining if they still have their // default value (false). - if (GraalOptions.HotSpotPrintCompilation.getValue() == false) { - GraalOptions.HotSpotPrintCompilation.setValue(config.printCompilation); + if (HotSpotPrintCompilation.getValue() == false) { + HotSpotPrintCompilation.setValue(config.printCompilation); } - if (GraalOptions.HotSpotPrintInlining.getValue() == false) { - GraalOptions.HotSpotPrintInlining.setValue(config.printInlining); + if (HotSpotPrintInlining.getValue() == false) { + HotSpotPrintInlining.setValue(config.printInlining); } if (Boolean.valueOf(System.getProperty("graal.printconfig"))) { diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java Tue Dec 17 15:44:23 2013 +0100 @@ -103,6 +103,22 @@ // Called from VM code public static boolean setOption(String option) { + return parseOption(option, null); + } + + interface OptionConsumer { + void set(OptionDescriptor desc, Object value); + } + + /** + * Parses a given option value specification. + * + * @param option the specification of an option and its value + * @param setter the object to notify of the parsed option and value. If null, the + * {@link OptionValue#setValue(Object)} method of the specified option is called + * instead. + */ + public static boolean parseOption(String option, OptionConsumer setter) { if (option.length() == 0) { return false; } @@ -175,9 +191,13 @@ } if (value != null) { - OptionValue optionValue = desc.getOptionValue(); - optionValue.setValue(value); - // Logger.info("Set option " + desc.getName() + " to " + value); + if (setter != null) { + setter.set(desc, value); + } else { + OptionValue optionValue = desc.getOptionValue(); + optionValue.setValue(value); + // Logger.info("Set option " + desc.getName() + " to " + value); + } } else { Logger.info("Wrong value \"" + valueString + "\" for option " + optionName); return false; diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Tue Dec 17 15:44:23 2013 +0100 @@ -669,6 +669,7 @@ @HotSpotVMFlag(name = "UseTLAB") @Stable public boolean useTLAB; @HotSpotVMFlag(name = "UseBiasedLocking") @Stable public boolean useBiasedLocking; @HotSpotVMFlag(name = "UsePopCountInstruction") @Stable public boolean usePopCountInstruction; + @HotSpotVMFlag(name = "UseCountLeadingZerosInstruction") @Stable public boolean useCountLeadingZerosInstruction; @HotSpotVMFlag(name = "UseAESIntrinsics") @Stable public boolean useAESIntrinsics; @HotSpotVMFlag(name = "UseCRC32Intrinsics") @Stable public boolean useCRC32Intrinsics; @HotSpotVMFlag(name = "UseG1GC") @Stable public boolean useG1GC; diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Tue Dec 17 15:44:23 2013 +0100 @@ -97,6 +97,10 @@ this.runtime = runtime; } + public int allocateCompileTaskId() { + return compileTaskIds.incrementAndGet(); + } + public void startCompiler(boolean bootstrapEnabled) throws Throwable { FastNodeClassRegistry.initialize(); @@ -556,7 +560,7 @@ final ProfilingInfo profilingInfo = method.getCompilationProfilingInfo(osrCompilation); final OptimisticOptimizations optimisticOpts = new OptimisticOptimizations(profilingInfo); - int id = compileTaskIds.incrementAndGet(); + int id = allocateCompileTaskId(); HotSpotBackend backend = runtime.getHostBackend(); CompilationTask task = CompilationTask.create(backend, createPhasePlan(backend.getProviders(), optimisticOpts, osrCompilation), optimisticOpts, profilingInfo, method, entryBCI, id); diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java Tue Dec 17 15:44:23 2013 +0100 @@ -41,7 +41,7 @@ /** * Represents a field in a HotSpot type. */ -public class HotSpotResolvedJavaField extends CompilerObject implements ResolvedJavaField, LocationIdentity { +public class HotSpotResolvedJavaField extends CompilerObject implements ResolvedJavaField { // Must not conflict with any fields flags used by the VM - the assertion in the constructor // checks this assumption diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Tue Dec 17 15:44:23 2013 +0100 @@ -217,7 +217,7 @@ TTY.println(MetaUtil.indent(MetaUtil.profileToString(profilingInfo, method, CodeUtil.NEW_LINE), " ")); } - Indent indent = Debug.logAndIndent(false, "build graph for %s", method.toString()); + Indent indent = Debug.logAndIndent(false, "build graph for %s", method); // compute the block map, setup exception handlers and get the entrypoint(s) BciBlockMapping blockMap = createBlockMap(); diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java Tue Dec 17 15:44:23 2013 +0100 @@ -166,15 +166,15 @@ /** * The maximum distance an operation with an {@linkplain #getExceptionEdge(LIRInstruction) - * exception edge} can be from the last instruction of a LIR block. The value of 2 is based on a - * non-void call operation that has an exception edge. Such a call op will have a move op after - * it to put the return value into the result variable. + * exception edge} can be from the last instruction of a LIR block. The value of 3 is based on a + * non-void call operation that has an exception edge. Such a call may move the result to + * another register and then spill it. *

* The rationale for such a constant is to limit the search for an insertion point when adding * move operations at the end of a block. Such moves must be inserted before all control flow * instructions. */ - public static final int MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END = 2; + public static final int MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END = 3; public static boolean verifyBlock(LIR lir, Block block) { List ops = lir.lir(block); diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java Tue Dec 17 15:44:23 2013 +0100 @@ -41,7 +41,7 @@ public final BytecodeFrame topFrame; private final VirtualObject[] virtualObjects; public final LabelRef exceptionEdge; - private DebugInfo debugInfo; + protected DebugInfo debugInfo; public LIRFrameState(BytecodeFrame topFrame, VirtualObject[] virtualObjects, LabelRef exceptionEdge) { this.topFrame = topFrame; @@ -109,8 +109,45 @@ } } - public void finish(BitSet registerRefMap, BitSet frameRefMap) { - debugInfo = new DebugInfo(topFrame, registerRefMap, frameRefMap); + /** + * Called by the register allocator before {@link #markLocation} to initialize the frame state. + * + * @param frameMap The frame map. + * @param canHaveRegisters True if there can be any register map entries. + */ + public void initDebugInfo(FrameMap frameMap, boolean canHaveRegisters) { + BitSet registerRefMap = (canHaveRegisters ? frameMap.initRegisterRefMap() : null); + debugInfo = new DebugInfo(topFrame, registerRefMap, frameMap.initFrameRefMap()); + } + + /** + * Called by the register allocator to mark the specified location as a reference in the + * reference map of the debug information. The tracked location can be a {@link RegisterValue} + * or a {@link StackSlot}. Note that a {@link Constant} is automatically tracked. + * + * @param location The location to be added to the reference map. + * @param frameMap The frame map. + */ + public void markLocation(Value location, FrameMap frameMap) { + if (location.getKind() == Kind.Object) { + if (isRegister(location)) { + debugInfo.getRegisterRefMap().set(asRegister(location).number); + } else if (isStackSlot(location)) { + int index = frameMap.indexForStackSlot(asStackSlot(location)); + debugInfo.getFrameRefMap().set(index); + } else { + assert isConstant(location); + } + } + } + + /** + * Called by the register allocator after all locations are marked. + * + * @param op The instruction to which this frame state belongs. + * @param frameMap The frame map. + */ + public void finish(LIRInstruction op, FrameMap frameMap) { } @Override diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/RedundantMoveElimination.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/RedundantMoveElimination.java Tue Dec 17 15:44:23 2013 +0100 @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2013, 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; + +import static com.oracle.graal.api.code.ValueUtil.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; +import com.oracle.graal.lir.StandardOp.MoveOp; +import com.oracle.graal.lir.LIRInstruction.*; +import com.oracle.graal.nodes.cfg.*; + +/** + * Removes move instructions, where the destination value is already in place. + */ +public final class RedundantMoveElimination { + + public static void optimize(LIR lir, FrameMap frameMap, ResolvedJavaMethod method) { + RedundantMoveElimination redundantMoveElimination = new RedundantMoveElimination(); + redundantMoveElimination.doOptimize(lir, frameMap, method); + } + + /** + * Holds the entry and exit states for each block for dataflow analysis. The state is an array + * with an element for each relevant location (register or stack slot). Each element holds the + * global number of the location's definition. A location definition is simply an output of an + * instruction. Note that because instructions can have multiple outputs it is not possible to + * use the instruction id for value numbering. In addition, the result of merging at block + * entries (= phi values) get unique value numbers. + * + * The value numbers also contain information if it is an object kind value or not: if the + * number is negative it is an object kind value. + */ + private static class BlockData { + + BlockData(int stateSize) { + entryState = new int[stateSize]; + exitState = new int[stateSize]; + } + + /* + * The state at block entry for global dataflow analysis. It contains a global value number + * for each location to optimize. + */ + int[] entryState; + + /* + * The state at block exit for global dataflow analysis. It contains a global value number + * for each location to optimize. + */ + int[] exitState; + + /* + * The starting number for global value numbering in this block. + */ + int entryValueNum; + } + + Map blockData = new HashMap<>(); + + Register[] callerSaveRegs; + + Map stackIndices = new HashMap<>(); + + int numRegs; + + /* + * Pseudo value for a not yet assigned location. + */ + static final int INIT_VALUE = 0; + + /** + * The main method doing the elimination of redundant moves. + */ + private void doOptimize(LIR lir, FrameMap frameMap, ResolvedJavaMethod method) { + + try (Indent indent = Debug.logAndIndent(false, "eliminate redundant moves in %s", method)) { + + callerSaveRegs = frameMap.registerConfig.getCallerSaveRegisters(); + + initBlockData(lir); + + solveDataFlow(lir); + + eliminateMoves(lir); + } + } + + private void initBlockData(LIR lir) { + + List blocks = lir.linearScanOrder(); + numRegs = 0; + + /* + * Search for relevant locations which can be optimized. These are register or stack slots + * which occur as destinations of move instructions. + */ + for (Block block : blocks) { + List instructions = lir.lir(block); + for (LIRInstruction op : instructions) { + if (isEligibleMove(op)) { + Value dest = ((MoveOp) op).getResult(); + if (isRegister(dest)) { + int regNum = ((RegisterValue) dest).getRegister().number; + if (regNum >= numRegs) { + numRegs = regNum + 1; + } + } else if (isStackSlot(dest)) { + StackSlot stackSlot = (StackSlot) dest; + if (!stackIndices.containsKey(stackSlot)) { + stackIndices.put(stackSlot, stackIndices.size()); + } + } + } + } + } + + /* + * Now we know the number of locations to optimize, so we can allocate the block states. + */ + int numLocations = numRegs + stackIndices.size(); + Debug.log("num locations = %d (regs = %d, stack = %d)", numLocations, numRegs, stackIndices.size()); + for (Block block : blocks) { + BlockData data = new BlockData(numLocations); + blockData.put(block, data); + } + } + + /** + * Calculates the entry and exit states for all basic blocks. + */ + private void solveDataFlow(LIR lir) { + + Indent indent = Debug.logAndIndent("solve data flow"); + + List blocks = lir.linearScanOrder(); + + /* + * Iterate until there are no more changes. + */ + int currentValueNum = 1; + boolean firstRound = true; + boolean changed; + do { + changed = false; + Indent indent2 = indent.logAndIndent("new iteration"); + + for (Block block : blocks) { + + BlockData data = blockData.get(block); + /* + * Initialize the number for global value numbering for this block. It is essential + * that the starting number for a block is consistent at all iterations and also in + * eliminateMoves(). + */ + if (firstRound) { + data.entryValueNum = currentValueNum; + } + int valueNum = data.entryValueNum; + assert valueNum > 0; + boolean newState = false; + + if (block == blocks.get(0) || block.isExceptionEntry()) { + /* + * The entry block has undefined values. And also exception handler blocks: the + * LinearScan can insert moves at the end of an exception handler predecessor + * block (after the invoke, which throws the exception), and in reality such + * moves are not in the control flow in case of an exception. So we assume a + * save default for exception handler blocks. + */ + indent2.log("kill all values at entry of block %d", block.getId()); + clearValues(data.entryState, valueNum); + } else { + /* + * Merge the states of predecessor blocks + */ + for (Block predecessor : block.getPredecessors()) { + BlockData predData = blockData.get(predecessor); + newState |= mergeState(data.entryState, predData.exitState, valueNum); + } + } + // Advance by the value numbers which are "consumed" by clearValues and mergeState + valueNum += data.entryState.length; + + if (newState || firstRound) { + + Indent indent3 = indent2.logAndIndent("update block %d", block.getId()); + + /* + * Derive the exit state from the entry state by iterating through all + * instructions of the block. + */ + int[] iterState = data.exitState; + copyState(iterState, data.entryState); + List instructions = lir.lir(block); + + for (LIRInstruction op : instructions) { + valueNum = updateState(iterState, op, valueNum); + } + changed = true; + indent3.outdent(); + } + if (firstRound) { + currentValueNum = valueNum; + } + } + firstRound = false; + indent2.outdent(); + + } while (changed); + + indent.outdent(); + } + + /** + * Deletes all move instructions where the target location already contains the source value. + */ + private void eliminateMoves(LIR lir) { + + Indent indent = Debug.logAndIndent("eliminate moves"); + + List blocks = lir.linearScanOrder(); + + for (Block block : blocks) { + + Indent indent2 = indent.logAndIndent("eliminate moves in block %d", block.getId()); + + List instructions = lir.lir(block); + BlockData data = blockData.get(block); + boolean hasDead = false; + + // Reuse the entry state for iteration, we don't need it later. + int[] iterState = data.entryState; + + // Add the values which are "consumed" by clearValues and mergeState in solveDataFlow + int valueNum = data.entryValueNum + data.entryState.length; + + int numInsts = instructions.size(); + for (int idx = 0; idx < numInsts; idx++) { + LIRInstruction op = instructions.get(idx); + if (isEligibleMove(op)) { + MoveOp moveOp = (MoveOp) op; + int sourceIdx = getStateIdx(moveOp.getInput()); + int destIdx = getStateIdx(moveOp.getResult()); + if (sourceIdx >= 0 && destIdx >= 0 && iterState[sourceIdx] == iterState[destIdx]) { + assert iterState[sourceIdx] != INIT_VALUE; + indent2.log("delete move %s", op); + instructions.set(idx, null); + hasDead = true; + } + } + // It doesn't harm if updateState is also called for a deleted move + valueNum = updateState(iterState, op, valueNum); + } + if (hasDead) { + instructions.removeAll(Collections.singleton(null)); + } + indent2.outdent(); + } + indent.outdent(); + } + + /** + * Updates the state for one instruction. + */ + private int updateState(final int[] state, LIRInstruction op, int initValueNum) { + + try (final Indent indent = Debug.logAndIndent("update state for op %s, initial value num = %d", op, initValueNum)) { + if (isEligibleMove(op)) { + /* + * Handle the special case of a move instruction + */ + MoveOp moveOp = (MoveOp) op; + int sourceIdx = getStateIdx(moveOp.getInput()); + int destIdx = getStateIdx(moveOp.getResult()); + if (sourceIdx >= 0 && destIdx >= 0) { + state[destIdx] = state[sourceIdx]; + indent.log("move value %d from %d to %d", state[sourceIdx], sourceIdx, destIdx); + return initValueNum; + } + } + + int valueNum = initValueNum; + + if (op.destroysCallerSavedRegisters()) { + indent.log("kill all caller save regs"); + + for (Register reg : callerSaveRegs) { + if (reg.number < numRegs) { + // Kind.Object is the save default + state[reg.number] = encodeValueNum(valueNum++, true); + } + } + } + + /* + * Value procedure for the instruction's output and temp values + */ + class OutputValueProc extends ValueProcedure { + + int opValueNum; + + OutputValueProc(int opValueNum) { + this.opValueNum = opValueNum; + } + + @Override + public Value doValue(Value operand, OperandMode mode, EnumSet flags) { + int stateIdx = getStateIdx(operand); + if (stateIdx >= 0) { + /* + * Assign a unique number to the output or temp location. + */ + state[stateIdx] = encodeValueNum(opValueNum++, operand.getKind() == Kind.Object); + indent.log("set def %d for register %s(%d): %d", opValueNum, operand, stateIdx, state[stateIdx]); + } + return operand; + } + } + + OutputValueProc outputValueProc = new OutputValueProc(valueNum); + op.forEachOutput(outputValueProc); + op.forEachTemp(outputValueProc); + valueNum = outputValueProc.opValueNum; + + if (op.hasState()) { + /* + * All instructions with framestates (mostly method calls), may do garbage + * collection. GC will rewrite all object references which are live at this point. + * So we can't rely on their values. + */ + indent.log("kill all object values"); + clearValuesOfKindObject(state, valueNum); + valueNum += state.length; + } + + return valueNum; + } + } + + /** + * The state merge function for dataflow joins. + */ + private static boolean mergeState(int[] dest, int[] source, int defNum) { + assert dest.length == source.length; + boolean changed = false; + for (int idx = 0; idx < source.length; idx++) { + int phiNum = defNum + idx; + if (dest[idx] != source[idx] && source[idx] != INIT_VALUE && dest[idx] != encodeValueNum(phiNum, isObjectValue(dest[idx]))) { + if (dest[idx] != INIT_VALUE) { + dest[idx] = encodeValueNum(phiNum, isObjectValue(dest[idx]) || isObjectValue(source[idx])); + } else { + dest[idx] = source[idx]; + } + changed = true; + } + } + return changed; + } + + private static void copyState(int[] dest, int[] source) { + assert dest.length == source.length; + for (int idx = 0; idx < source.length; idx++) { + dest[idx] = source[idx]; + } + } + + private static void clearValues(int[] state, int defNum) { + for (int idx = 0; idx < state.length; idx++) { + int phiNum = defNum + idx; + // Let the killed values assume to be object references: it's the save default. + state[idx] = encodeValueNum(phiNum, true); + } + } + + private static void clearValuesOfKindObject(int[] state, int defNum) { + for (int idx = 0; idx < state.length; idx++) { + int phiNum = defNum + idx; + if (isObjectValue(state[idx])) { + state[idx] = encodeValueNum(phiNum, true); + } + } + } + + /** + * Returns the index to the state arrays in BlockData for a specific location. + */ + private int getStateIdx(Value location) { + if (isRegister(location)) { + int regNum = ((RegisterValue) location).getRegister().number; + if (regNum < numRegs) { + return regNum; + } + } + if (isStackSlot(location)) { + StackSlot slot = (StackSlot) location; + Integer index = stackIndices.get(slot); + if (index != null) { + return index.intValue() + numRegs; + } + } + return -1; + } + + /** + * Encodes a value number + the is-object information to a number to be stored in a state. + */ + private static int encodeValueNum(int valueNum, boolean isObjectKind) { + assert valueNum > 0; + if (isObjectKind) { + return -valueNum; + } + return valueNum; + } + + /** + * Returns true if an encoded value number (which is stored in a state) refers to an object + * reference. + */ + private static boolean isObjectValue(int encodedValueNum) { + return encodedValueNum < 0; + } + + /** + * Returns true for a move instruction which is a candidate for elimination. + */ + private static boolean isEligibleMove(LIRInstruction op) { + if (op instanceof MoveOp) { + MoveOp moveOp = (MoveOp) op; + Value source = moveOp.getInput(); + Value dest = moveOp.getResult(); + /* + * Moves with mismatching kinds are not moves, but memory loads/stores! + */ + return source.getKind() == dest.getKind() && source.getPlatformKind() == dest.getPlatformKind() && source.getKind() != Kind.Illegal; + } + return false; + } +} diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StateSplit.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StateSplit.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StateSplit.java Tue Dec 17 15:44:23 2013 +0100 @@ -42,9 +42,9 @@ void setStateAfter(FrameState x); /** - * Determines if this node has a side-effect. Such nodes can not be safely re-executed because - * they modified state which is visible to other thread or modified state beyond what is - * captured in {@link FrameState} nodes. + * Determines if this node has a side-effect. Such nodes cannot be safely re-executed because + * they modify state which is visible to other threads or modify state beyond what is captured + * in {@link FrameState} nodes. */ boolean hasSideEffect(); } diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CompareAndSwapNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CompareAndSwapNode.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CompareAndSwapNode.java Tue Dec 17 15:44:23 2013 +0100 @@ -25,6 +25,8 @@ import static com.oracle.graal.graph.UnsafeAccess.*; import com.oracle.graal.api.meta.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.spi.*; @@ -34,13 +36,14 @@ * Represents an atomic compare-and-swap operation The result is a boolean that contains whether the * value matched the expected value. */ -public class CompareAndSwapNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single { +public class CompareAndSwapNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single, Canonicalizable { @Input private ValueNode object; @Input private ValueNode offset; @Input private ValueNode expected; @Input private ValueNode newValue; private final int displacement; + private final LocationIdentity locationIdentity; public ValueNode object() { return object; @@ -63,6 +66,10 @@ } public CompareAndSwapNode(ValueNode object, int displacement, ValueNode offset, ValueNode expected, ValueNode newValue) { + this(object, displacement, offset, expected, newValue, LocationIdentity.ANY_LOCATION); + } + + public CompareAndSwapNode(ValueNode object, int displacement, ValueNode offset, ValueNode expected, ValueNode newValue, LocationIdentity locationIdentity) { super(StampFactory.forKind(Kind.Boolean.getStackKind())); assert expected.kind() == newValue.kind(); this.object = object; @@ -70,11 +77,12 @@ this.expected = expected; this.newValue = newValue; this.displacement = displacement; + this.locationIdentity = locationIdentity; } @Override public LocationIdentity getLocationIdentity() { - return LocationIdentity.ANY_LOCATION; + return locationIdentity; } @Override @@ -82,6 +90,24 @@ tool.getLowerer().lower(this, tool); } + @Override + public Node canonical(CanonicalizerTool tool) { + if (getLocationIdentity() == LocationIdentity.ANY_LOCATION) { + Constant offsetConstant = offset().asConstant(); + if (offsetConstant != null) { + ResolvedJavaType receiverType = ObjectStamp.typeOrNull(object()); + if (receiverType != null) { + long constantOffset = offsetConstant.asLong(); + ResolvedJavaField field = receiverType.findInstanceFieldWithOffset(constantOffset); + if (field != null && expected().kind() == field.getKind() && newValue().kind() == field.getKind()) { + return graph().add(new CompareAndSwapNode(object, displacement, offset, expected, newValue, field)); + } + } + } + } + return this; + } + // specialized on value type until boxing/unboxing is sorted out in intrinsification @NodeIntrinsic public static boolean compareAndSwap(Object object, @ConstantNodeParameter int displacement, long offset, Object expected, Object newValue) { diff -r 136df94b5aa8 -r cf7b5b507541 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/NodeWithState.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/NodeWithState.java Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/NodeWithState.java Tue Dec 17 15:44:23 2013 +0100 @@ -29,13 +29,11 @@ /** * Interface for nodes which have {@link FrameState} nodes as input. *

- * Some node can declare more than one interface which requires a {@link FrameState} input (e.g. + * Some node can implement more than one interface which requires a {@link FrameState} input (e.g. * {@link DeoptimizingNode} and {@link StateSplit}). Since this interface can only report one - * {@link FrameState}, such nodes must ensure they only maintain a link to at most one - * {@link FrameState} at all times. Usually this is not a problem because {@link FrameState} are - * associated only with {@link StateSplit} nodes before the {@link #AFTER_FSA} stage and only with - * {@link DeoptimizingNode} after. - * + * FrameState, such nodes must ensure they only maintain a link to at most one FrameState at all + * times. Usually this is not a problem because FrameStates are associated only with StateSplit + * nodes before the {@link #AFTER_FSA} stage and only with DeoptimizingNodes after. * */ public interface NodeWithState { diff -r 136df94b5aa8 -r cf7b5b507541 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 Tue Dec 17 15:44:02 2013 +0100 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java Tue Dec 17 15:44:23 2013 +0100 @@ -87,14 +87,6 @@ @Option(help = "") public static final OptionValue DeoptsToDisableOptimisticOptimization = new OptionValue<>(40); - // compilation queue - @Option(help = "Compile all methods in all classes on given class path") - public static final OptionValue CompileTheWorld = new OptionValue<>(null); - @Option(help = "First class to consider when using CompileTheWorld") - public static final OptionValue CompileTheWorldStartAt = new OptionValue<>(1); - @Option(help = "Last class to consider when using CompileTheWorld") - public static final OptionValue CompileTheWorldStopAt = new OptionValue<>(Integer.MAX_VALUE); - // graph caching @Option(help = "") public static final OptionValue CacheGraphs = new OptionValue<>(true); diff -r 136df94b5aa8 -r cf7b5b507541 mx/mx_graal.py --- a/mx/mx_graal.py Tue Dec 17 15:44:02 2013 +0100 +++ b/mx/mx_graal.py Tue Dec 17 15:44:23 2013 +0100 @@ -1348,14 +1348,14 @@ def trufflejar(args=None): """make truffle.jar""" - + # Test with the built classes _unittest(["com.oracle.truffle.api.test", "com.oracle.truffle.api.dsl.test"], ['@Test', '@LongTest', '@Parameters']) - + # We use the DSL processor as the starting point for the classpath - this # therefore includes the DSL processor, the DSL and the API. packagejar(mx.classpath("com.oracle.truffle.dsl.processor").split(os.pathsep), "truffle.jar", None, "com.oracle.truffle.dsl.processor.TruffleProcessor") - + # Test with the JAR _unittest(["com.oracle.truffle.api.test", "com.oracle.truffle.api.dsl.test"], ['@Test', '@LongTest', '@Parameters'], "truffle.jar:") @@ -1453,7 +1453,7 @@ mx.distribution('GRAAL').add_update_listener(_installGraalJarInJdks) def packagejar(classpath, outputFile, mainClass=None, annotationProcessor=None, stripDebug=False): - prefix = '' if mx.get_os() != 'windows' else '\\??\\' # long file name hack + prefix = '' if mx.get_os() != 'windows' else '\\??\\' # long file name hack print "creating", outputFile filecount, totalsize = 0, 0 with zipfile.ZipFile(outputFile, 'w', zipfile.ZIP_DEFLATED) as zf: @@ -1475,7 +1475,7 @@ for root, _, files in os.walk(cp): for f in files: fullname = os.path.join(root, f) - arcname = fullname[len(cp)+1:].replace('\\', '/') + arcname = fullname[len(cp) + 1:].replace('\\', '/') if f.endswith(".class"): zf.write(prefix + fullname, arcname) diff -r 136df94b5aa8 -r cf7b5b507541 mx/projects --- a/mx/projects Tue Dec 17 15:44:02 2013 +0100 +++ b/mx/projects Tue Dec 17 15:44:23 2013 +0100 @@ -1,5 +1,5 @@ # The format of this file is described in the documentation for my.py. - +mxversion=1.0 suite=graal library@JDK_TOOLS@path=${JAVA_HOME}/lib/tools.jar diff -r 136df94b5aa8 -r cf7b5b507541 mxtool/mx.py --- a/mxtool/mx.py Tue Dec 17 15:44:02 2013 +0100 +++ b/mxtool/mx.py Tue Dec 17 15:44:23 2013 +0100 @@ -675,6 +675,7 @@ self.imports = [] self.commands = None self.primary = primary + self.requiredMxVersion = None self.name = _suitename(mxDir) # validated in _load_projects if load: # load suites bottom up to make sure command overriding works properly @@ -714,10 +715,17 @@ parts = key.split('@') if len(parts) == 1: - if parts[0] != 'suite': + if parts[0] == 'suite': + if self.name != value: + abort('suite name in project file does not match ' + _suitename(self.mxDir)) + elif parts[0] == 'mxversion': + try: + self.requiredMxVersion = JavaVersion(value) + except AssertionError as ae: + abort('Exception while parsing "mxversion" in project file: ' + str(ae)) + else: abort('Single part property must be "suite": ' + key) - if self.name != value: - abort('suite name in project file does not match ' + _suitename(self.mxDir)) + continue if len(parts) != 3: abort('Property name does not have 3 parts separated by "@": ' + key) @@ -889,6 +897,10 @@ def _post_init(self, opts): self._load_projects() + if self.requiredMxVersion is None: + warn("This suite does not express any required mx version. Consider adding 'mxversion=' to your projects file.") + elif self.requiredMxVersion > version: + abort("This suite requires mx version " + str(self.requiredMxVersion) + " while your current mx verion is " + str(version) + ". Please update mx.") # set the global data structures, checking for conflicts unless _check_global_structures is False for p in self.projects: existing = _projects.get(p.name) @@ -1552,7 +1564,7 @@ def __init__(self, versionString): validChar = r'[\x21-\x25\x27-\x29\x2c\x2f-\x5e\x60-\x7f]' separator = r'[.\-_]' - m = re.match(validChar + '+(' + separator + validChar + '+)*', versionString) + m = re.match("^" + validChar + '+(' + separator + validChar + '+)*$', versionString) assert m is not None, 'not a recognized version string: ' + versionString self.versionString = versionString self.parts = [int(f) if f.isdigit() else f for f in re.split(separator, versionString)] @@ -4516,6 +4528,8 @@ # no need to show the stack trace when the user presses CTRL-C abort(1) +version = JavaVersion("1.0") + if __name__ == '__main__': # rename this module as 'mx' so it is not imported twice by the commands.py modules sys.modules['mx'] = sys.modules.pop('__main__') diff -r 136df94b5aa8 -r cf7b5b507541 src/share/vm/graal/graalCompiler.cpp --- a/src/share/vm/graal/graalCompiler.cpp Tue Dec 17 15:44:02 2013 +0100 +++ b/src/share/vm/graal/graalCompiler.cpp Tue Dec 17 15:44:23 2013 +0100 @@ -115,6 +115,11 @@ // Avoid -Xcomp and -Xbatch problems by turning on interpreter and background compilation for bootstrapping. FlagSetting a(UseInterpreter, true); FlagSetting b(BackgroundCompilation, true); +#ifndef PRODUCT + // Turn off CompileTheWorld during bootstrap + // so that a complete bootstrap occurs + FlagSetting c(CompileTheWorld, false); +#endif VMToCompiler::bootstrap(); }