changeset 19170:3b2e98f9e47c

Initial prototype for loop explosion during graph building.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Fri, 06 Feb 2015 03:24:50 +0100
parents f41186c896cd
children 5adc03d22312
files graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/AddNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/BinaryArithmeticNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerBelowNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerLessThanNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java
diffstat 11 files changed, 326 insertions(+), 73 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.baseline/src/com/oracle/graal/baseline/BaselineBytecodeParser.java	Fri Feb 06 03:24:50 2015 +0100
@@ -112,7 +112,7 @@
             frameState.clearNonLiveLocals(blockMap.startBlock, liveness, true);
 
             currentBlock = blockMap.startBlock;
-            blockMap.startBlock.entryState = frameState;
+            blockMap.startBlock.setEntryState(0, frameState);
             if (blockMap.startBlock.isLoopHeader) {
                 throw GraalInternalError.unimplemented("Handle start block as loop header");
             }
@@ -624,15 +624,15 @@
                  */
                 moveConstantsToVariables();
             }
-            block.entryState = frameState.copy();
-            block.entryState.clearNonLiveLocals(block, liveness, true);
+            block.setEntryState(0, frameState.copy());
+            block.getEntryState(0).clearNonLiveLocals(block, liveness, true);
 
             Debug.log("createTarget %s: first visit", block);
             return;
         }
 
         // We already saw this block before, so we have to merge states.
-        if (!((BaselineFrameStateBuilder) block.entryState).isCompatibleWith(frameState)) {
+        if (!((BaselineFrameStateBuilder) block.getEntryState(0)).isCompatibleWith(frameState)) {
             throw new BailoutException("stacks do not match; bytecodes would not verify");
         }
 
@@ -640,7 +640,7 @@
             assert currentBlock == null || currentBlock.getId() >= block.getId() : "must be backward branch";
             if (currentBlock != null && currentBlock.numNormalSuccessors() == 1) {
                 // this is the only successor of the current block so we can adjust
-                adaptFramestate((BaselineFrameStateBuilder) block.entryState);
+                adaptFramestate((BaselineFrameStateBuilder) block.getEntryState(0));
                 return;
             }
             GraalInternalError.unimplemented("Loops not yet supported");
@@ -654,7 +654,7 @@
          */
         if (currentBlock != null && currentBlock.numNormalSuccessors() == 1) {
             // this is the only successor of the current block so we can adjust
-            adaptFramestate((BaselineFrameStateBuilder) block.entryState);
+            adaptFramestate((BaselineFrameStateBuilder) block.getEntryState(0));
             return;
         }
         GraalInternalError.unimplemented("second block visit not yet implemented");
@@ -715,7 +715,7 @@
     }
 
     protected void processBlock(BciBlock block) {
-        frameState = (BaselineFrameStateBuilder) block.entryState;
+        frameState = (BaselineFrameStateBuilder) block.getEntryState(0);
         setCurrentFrameState(frameState);
         currentBlock = block;
         iterateBytecodesForBlock(block);
@@ -745,7 +745,7 @@
              * We need to preserve the frame state builder of the loop header so that we can merge
              * values for phi functions, so make a copy of it.
              */
-            block.entryState = frameState.copy();
+            block.setEntryState(0, frameState.copy());
 
         }
         int endBCI = stream.endBCI();
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java	Fri Feb 06 03:24:50 2015 +0100
@@ -343,6 +343,9 @@
     public static final OptionValue<Boolean> ImplicitStableValues = new OptionValue<>(true);
 
 
+    @Option(help = "Max number of loop explosions per method.", type = OptionType.Debug)
+    public static final OptionValue<Integer> MaximumLoopExplosionCount = new OptionValue<>(10000);
+
     /**
      * Counts the various paths taken through snippets.
      */
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Fri Feb 06 03:24:50 2015 +0100
@@ -81,8 +81,8 @@
         public int endBci;
         public boolean isExceptionEntry;
         public boolean isLoopHeader;
-        public boolean isLastLoopEnd;
         public int loopId;
+        public int loopEnd;
 
         /**
          * XXX to be removed - currently only used by baseline compiler.
@@ -90,8 +90,10 @@
         public Loop<BciBlock> loop;
         public boolean isLoopEnd;
 
-        public FixedWithNextNode firstInstruction;
-        public AbstractFrameStateBuilder<?, ?> entryState;
+        private FixedWithNextNode firstInstruction;
+        private AbstractFrameStateBuilder<?, ?> entryState;
+        private FixedWithNextNode[] firstInstructionArray;
+        private AbstractFrameStateBuilder<?, ?>[] entryStateArray;
 
         private boolean visited;
         private boolean active;
@@ -330,6 +332,66 @@
         public void setJsrReturnBci(int bci) {
             this.getOrCreateJSRData().jsrReturnBci = bci;
         }
+
+        public FixedWithNextNode getFirstInstruction(int dimension) {
+            if (dimension == 0) {
+                return firstInstruction;
+            } else {
+                if (firstInstructionArray != null && dimension - 1 < firstInstructionArray.length) {
+                    return firstInstructionArray[dimension - 1];
+                } else {
+                    return null;
+                }
+            }
+        }
+
+        public void setFirstInstruction(int dimension, FixedWithNextNode firstInstruction) {
+            if (dimension == 0) {
+                this.firstInstruction = firstInstruction;
+            } else {
+                if (firstInstructionArray == null) {
+                    firstInstructionArray = new FixedWithNextNode[4];
+                }
+                if (dimension - 1 < firstInstructionArray.length) {
+                    // We are within bounds.
+                } else {
+                    // We are out of bounds.
+                    firstInstructionArray = Arrays.copyOf(firstInstructionArray, Math.max(firstInstructionArray.length * 2, dimension));
+                }
+
+                firstInstructionArray[dimension - 1] = firstInstruction;
+            }
+        }
+
+        public AbstractFrameStateBuilder<?, ?> getEntryState(int dimension) {
+            if (dimension == 0) {
+                return entryState;
+            } else {
+                if (entryStateArray != null && dimension - 1 < entryStateArray.length) {
+                    return entryStateArray[dimension - 1];
+                } else {
+                    return null;
+                }
+            }
+        }
+
+        public void setEntryState(int dimension, AbstractFrameStateBuilder<?, ?> entryState) {
+            if (dimension == 0) {
+                this.entryState = entryState;
+            } else {
+                if (entryStateArray == null) {
+                    entryStateArray = new AbstractFrameStateBuilder<?, ?>[4];
+                }
+                if (dimension - 1 < entryStateArray.length) {
+                    // We are within bounds.
+                } else {
+                    // We are out of bounds.
+                    entryStateArray = Arrays.copyOf(entryStateArray, Math.max(entryStateArray.length * 2, dimension));
+                }
+
+                entryStateArray[dimension - 1] = entryState;
+            }
+        }
     }
 
     public static class ExceptionDispatchBlock extends BciBlock {
@@ -783,7 +845,7 @@
                 for (BciBlock succBlock : blocks[i].getSuccessors()) {
                     succ += succBlock.getId() + " ";
                 }
-                System.out.printf("%3s %10s %s %s succ=[%s]\n", blocks[i].getId(), Long.toBinaryString(blocks[i].loops), blocks[i].isLoopHeader, blocks[i].isLastLoopEnd, succ);
+                System.out.printf("%3s %10s %s succ=[%s]\n", blocks[i].getId(), Long.toBinaryString(blocks[i].loops), blocks[i].isLoopHeader, succ);
             }
             System.out.println();
         }
@@ -791,20 +853,20 @@
 
     private int handleLoopHeader(BciBlock[] newBlocks, int nextStart, int i, BciBlock loopHeader) {
         int next = nextStart;
-        BciBlock last = loopHeader;
+        int endOfLoop = nextStart - 1;
         for (int j = i + 1; j < blocks.length; ++j) {
             BciBlock other = blocks[j];
             if (other != null && (other.loops & (1L << loopHeader.loopId)) != 0) {
                 other.setId(next);
+                endOfLoop = next;
                 newBlocks[next++] = other;
-                last = other;
                 blocks[j] = null;
                 if (other.isLoopHeader) {
                     next = handleLoopHeader(newBlocks, next, j, other);
                 }
             }
         }
-        last.isLastLoopEnd = true;
+        loopHeader.loopEnd = endOfLoop;
         return next;
     }
 
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri Feb 06 03:24:50 2015 +0100
@@ -170,6 +170,12 @@
             }
         }
 
+        private static class ExplodedLoopContext {
+            private BciBlock header;
+            private int targetPeelIteration;
+            private int peelIteration;
+        }
+
         public class BytecodeParser extends AbstractBytecodeParser<ValueNode, HIRFrameStateBuilder> implements GraphBuilderContext {
 
             private BciBlock[] loopHeaders;
@@ -192,6 +198,8 @@
 
             private FixedWithNextNode lastInstr;                 // the last instruction added
             private final boolean explodeLoops;
+            private Stack<ExplodedLoopContext> explodeLoopsContext;
+            private int nextPeelIteration = 1;
 
             public BytecodeParser(MetaAccessProvider metaAccess, ResolvedJavaMethod method, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, int entryBCI) {
                 super(metaAccess, method, graphBuilderConfig, optimisticOpts);
@@ -268,15 +276,18 @@
                     }
 
                     currentBlock = blockMap.startBlock;
-                    blockMap.startBlock.entryState = frameState;
-                    if (blockMap.startBlock.isLoopHeader) {
+                    blockMap.startBlock.setEntryState(0, frameState);
+                    if (blockMap.startBlock.isLoopHeader && !explodeLoops) {
                         appendGoto(createTarget(blockMap.startBlock, frameState));
                     } else {
-                        blockMap.startBlock.firstInstruction = lastInstr;
+                        blockMap.startBlock.setFirstInstruction(0, lastInstr);
                     }
 
-                    for (BciBlock block : blockMap.getBlocks()) {
-                        processBlock(this, block);
+                    int index = 0;
+                    BciBlock[] blocks = blockMap.getBlocks();
+                    while (index < blocks.length) {
+                        BciBlock block = blocks[index];
+                        index = iterateBlock(blocks, block);
                     }
                     processBlock(this, returnBlock);
                     processBlock(this, unwindBlock);
@@ -286,6 +297,50 @@
                 }
             }
 
+            private int iterateBlock(BciBlock[] blocks, BciBlock block) {
+                if (block.isLoopHeader && this.explodeLoops) {
+                    return iterateExplodedLoopHeader(blocks, block);
+                } else {
+                    processBlock(this, block);
+                    return block.getId() + 1;
+                }
+            }
+
+            private int iterateExplodedLoopHeader(BciBlock[] blocks, BciBlock header) {
+                if (explodeLoopsContext == null) {
+                    explodeLoopsContext = new Stack<>();
+                }
+
+                ExplodedLoopContext context = new ExplodedLoopContext();
+                context.header = header;
+                context.peelIteration = this.getCurrentDimension();
+                context.targetPeelIteration = -1;
+                explodeLoopsContext.push(context);
+                Debug.dump(currentGraph, "before loop explosion " + context.peelIteration);
+
+                while (true) {
+
+                    processBlock(this, header);
+                    for (int j = header.getId() + 1; j <= header.loopEnd; ++j) {
+                        BciBlock block = blocks[j];
+                        iterateBlock(blocks, block);
+                    }
+
+                    if (context.targetPeelIteration != -1) {
+                        // We were reaching the backedge during explosion. Explode further.
+                        Debug.dump(currentGraph, "Before loop explosion " + context.targetPeelIteration);
+                        context.peelIteration = context.targetPeelIteration;
+                        context.targetPeelIteration = -1;
+                    } else {
+                        // We did not reach the backedge. Exit.
+                        Debug.dump(currentGraph, "after loop explosion " + context.peelIteration);
+                        break;
+                    }
+                }
+                explodeLoopsContext.pop();
+                return header.loopEnd + 1;
+            }
+
             private BciBlock returnBlock(int bci) {
                 if (returnBlock == null) {
                     returnBlock = new BciBlock();
@@ -448,7 +503,7 @@
 
             @Override
             protected ValueNode genLoadIndexed(ValueNode array, ValueNode index, Kind kind) {
-                return new LoadIndexedNode(array, index, kind);
+                return LoadIndexedNode.create(array, index, kind, metaAccess, constantReflectionProvider);
             }
 
             @Override
@@ -458,7 +513,7 @@
 
             @Override
             protected ValueNode genIntegerAdd(Kind kind, ValueNode x, ValueNode y) {
-                return new AddNode(x, y);
+                return AddNode.create(x, y);
             }
 
             @Override
@@ -584,7 +639,7 @@
 
             @Override
             protected ValueNode genIntegerLessThan(ValueNode x, ValueNode y) {
-                return new IntegerLessThanNode(x, y);
+                return IntegerLessThanNode.create(x, y, constantReflectionProvider);
             }
 
             @Override
@@ -656,7 +711,7 @@
             protected void emitBoundsCheck(ValueNode index, ValueNode length) {
                 AbstractBeginNode trueSucc = currentGraph.add(new BeginNode());
                 BytecodeExceptionNode exception = currentGraph.add(new BytecodeExceptionNode(metaAccess, ArrayIndexOutOfBoundsException.class, index));
-                append(new IfNode(currentGraph.unique(new IntegerBelowNode(index, length)), trueSucc, exception, 0.99));
+                append(new IfNode(currentGraph.unique(IntegerBelowNode.create(index, length, constantReflectionProvider)), trueSucc, exception, 0.99));
                 lastInstr = trueSucc;
 
                 exception.setStateAfter(frameState.create(bci()));
@@ -665,7 +720,7 @@
 
             @Override
             protected ValueNode genArrayLength(ValueNode x) {
-                return new ArrayLengthNode(x);
+                return ArrayLengthNode.create(x, constantReflectionProvider);
             }
 
             @Override
@@ -1030,7 +1085,7 @@
             }
 
             private Target checkLoopExit(FixedNode target, BciBlock targetBlock, HIRFrameStateBuilder state) {
-                if (currentBlock != null) {
+                if (currentBlock != null && !explodeLoops) {
                     long exits = currentBlock.loops & ~targetBlock.loops;
                     if (exits != 0) {
                         LoopExitNode firstLoopExit = null;
@@ -1061,7 +1116,7 @@
                         }
                         HIRFrameStateBuilder newState = state.copy();
                         for (BciBlock loop : exitLoops) {
-                            LoopBeginNode loopBegin = (LoopBeginNode) loop.firstInstruction;
+                            LoopBeginNode loopBegin = (LoopBeginNode) loop.getFirstInstruction(this.getCurrentDimension());
                             LoopExitNode loopExit = currentGraph.add(new LoopExitNode(loopBegin));
                             if (lastLoopExit != null) {
                                 lastLoopExit.setNext(loopExit);
@@ -1071,7 +1126,7 @@
                             }
                             lastLoopExit = loopExit;
                             Debug.log("Target %s (%s) Exits %s, scanning framestates...", targetBlock, target, loop);
-                            newState.insertLoopProxies(loopExit, (HIRFrameStateBuilder) loop.entryState);
+                            newState.insertLoopProxies(loopExit, (HIRFrameStateBuilder) loop.getEntryState(this.getCurrentDimension()));
                             loopExit.setStateAfter(newState.create(bci));
                         }
 
@@ -1096,53 +1151,88 @@
                 assert block != null && state != null;
                 assert !block.isExceptionEntry || state.stackSize() == 1;
 
-                if (block.firstInstruction == null) {
+                int operatingDimension = this.getCurrentDimension();
+                if (this.explodeLoops && this.explodeLoopsContext != null && !this.explodeLoopsContext.isEmpty()) {
+                    int i;
+                    for (i = explodeLoopsContext.size() - 1; i >= 0; --i) {
+                        ExplodedLoopContext context = explodeLoopsContext.elementAt(i);
+                        if (context.header == block) {
+
+                            // We have a hit on our current explosion context loop begin.
+                            if (context.targetPeelIteration == -1) {
+                                // This is the first hit => allocate a new dimension and at the same
+                                // time mark the context loop begin as hit during the current
+                                // iteration.
+                                context.targetPeelIteration = nextPeelIteration++;
+                                if (nextPeelIteration > GraalOptions.MaximumLoopExplosionCount.getValue()) {
+                                    throw new BailoutException("too many loop explosion interations - does the explosion not terminate?");
+                                }
+                            }
+
+                            // Operate on the target dimension.
+                            operatingDimension = context.targetPeelIteration;
+                            break;
+                        } else if (block.getId() > context.header.getId() && block.getId() <= context.header.loopEnd) {
+                            // We hit the range of this context.
+                            operatingDimension = context.peelIteration;
+                            break;
+                        }
+                    }
+
+                    if (i == -1) {
+                        // I did not find a dimension.
+                        operatingDimension = 0;
+                    }
+                }
+
+                if (block.getFirstInstruction(operatingDimension) == null) {
                     /*
                      * This is the first time we see this block as a branch target. Create and
                      * return a placeholder that later can be replaced with a MergeNode when we see
                      * this block again.
                      */
                     FixedNode targetNode;
-                    block.firstInstruction = currentGraph.add(new BeginNode());
-                    targetNode = block.firstInstruction;
+                    block.setFirstInstruction(operatingDimension, currentGraph.add(new BeginNode()));
+                    targetNode = block.getFirstInstruction(operatingDimension);
                     Target target = checkLoopExit(targetNode, block, state);
                     FixedNode result = target.fixed;
-                    block.entryState = target.state == state ? state.copy() : target.state;
-                    block.entryState.clearNonLiveLocals(block, liveness, true);
+                    AbstractFrameStateBuilder<?, ?> entryState = target.state == state ? state.copy() : target.state;
+                    block.setEntryState(operatingDimension, entryState);
+                    entryState.clearNonLiveLocals(block, liveness, true);
 
                     Debug.log("createTarget %s: first visit, result: %s", block, targetNode);
                     return result;
                 }
 
                 // We already saw this block before, so we have to merge states.
-                if (!((HIRFrameStateBuilder) block.entryState).isCompatibleWith(state)) {
+                if (!((HIRFrameStateBuilder) block.getEntryState(operatingDimension)).isCompatibleWith(state)) {
                     throw new BailoutException("stacks do not match; bytecodes would not verify");
                 }
 
-                if (block.firstInstruction instanceof LoopBeginNode) {
-                    assert block.isLoopHeader && currentBlock.getId() >= block.getId() : "must be backward branch";
+                if (block.getFirstInstruction(operatingDimension) instanceof LoopBeginNode) {
+                    assert this.explodeLoops || (block.isLoopHeader && currentBlock.getId() >= block.getId()) : "must be backward branch";
                     /*
                      * Backward loop edge. We need to create a special LoopEndNode and merge with
                      * the loop begin node created before.
                      */
-                    LoopBeginNode loopBegin = (LoopBeginNode) block.firstInstruction;
+                    LoopBeginNode loopBegin = (LoopBeginNode) block.getFirstInstruction(operatingDimension);
                     Target target = checkLoopExit(currentGraph.add(new LoopEndNode(loopBegin)), block, state);
                     FixedNode result = target.fixed;
-                    ((HIRFrameStateBuilder) block.entryState).merge(loopBegin, target.state);
+                    ((HIRFrameStateBuilder) block.getEntryState(operatingDimension)).merge(loopBegin, target.state);
 
                     Debug.log("createTarget %s: merging backward branch to loop header %s, result: %s", block, loopBegin, result);
                     return result;
                 }
                 assert currentBlock == null || currentBlock.getId() < block.getId() : "must not be backward branch";
-                assert block.firstInstruction.next() == null : "bytecodes already parsed for block";
+                assert block.getFirstInstruction(operatingDimension).next() == null : "bytecodes already parsed for block";
 
-                if (block.firstInstruction instanceof AbstractBeginNode && !(block.firstInstruction instanceof AbstractMergeNode)) {
+                if (block.getFirstInstruction(operatingDimension) instanceof AbstractBeginNode && !(block.getFirstInstruction(operatingDimension) instanceof AbstractMergeNode)) {
                     /*
                      * This is the second time we see this block. Create the actual MergeNode and
                      * the End Node for the already existing edge. For simplicity, we leave the
                      * placeholder in the graph and just append the new nodes after the placeholder.
                      */
-                    AbstractBeginNode placeholder = (AbstractBeginNode) block.firstInstruction;
+                    AbstractBeginNode placeholder = (AbstractBeginNode) block.getFirstInstruction(operatingDimension);
 
                     // The EndNode for the already existing edge.
                     AbstractEndNode end = currentGraph.add(new EndNode());
@@ -1160,16 +1250,16 @@
                     mergeNode.addForwardEnd(end);
                     mergeNode.setNext(next);
 
-                    block.firstInstruction = mergeNode;
+                    block.setFirstInstruction(operatingDimension, mergeNode);
                 }
 
-                AbstractMergeNode mergeNode = (AbstractMergeNode) block.firstInstruction;
+                AbstractMergeNode mergeNode = (AbstractMergeNode) block.getFirstInstruction(operatingDimension);
 
                 // The EndNode for the newly merged edge.
                 AbstractEndNode newEnd = currentGraph.add(new EndNode());
                 Target target = checkLoopExit(newEnd, block, state);
                 FixedNode result = target.fixed;
-                ((HIRFrameStateBuilder) block.entryState).merge(mergeNode, target.state);
+                ((HIRFrameStateBuilder) block.getEntryState(operatingDimension)).merge(mergeNode, target.state);
                 mergeNode.addForwardEnd(newEnd);
 
                 Debug.log("createTarget %s: merging state, result: %s", block, result);
@@ -1199,14 +1289,14 @@
 
             protected void processBlock(BytecodeParser parser, BciBlock block) {
                 // Ignore blocks that have no predecessors by the time their bytecodes are parsed
-                if (block == null || block.firstInstruction == null) {
+                if (block == null || block.getFirstInstruction(this.getCurrentDimension()) == null) {
                     Debug.log("Ignoring block %s", block);
                     return;
                 }
-                try (Indent indent = Debug.logAndIndent("Parsing block %s  firstInstruction: %s  loopHeader: %b", block, block.firstInstruction, block.isLoopHeader)) {
+                try (Indent indent = Debug.logAndIndent("Parsing block %s  firstInstruction: %s  loopHeader: %b", block, block.getFirstInstruction(this.getCurrentDimension()), block.isLoopHeader)) {
 
-                    lastInstr = block.firstInstruction;
-                    frameState = (HIRFrameStateBuilder) block.entryState;
+                    lastInstr = block.getFirstInstruction(this.getCurrentDimension());
+                    frameState = (HIRFrameStateBuilder) block.getEntryState(this.getCurrentDimension());
                     parser.setCurrentFrameState(frameState);
                     currentBlock = block;
 
@@ -1341,7 +1431,7 @@
 
             @Override
             protected void iterateBytecodesForBlock(BciBlock block) {
-                if (block.isLoopHeader) {
+                if (block.isLoopHeader && !explodeLoops) {
                     // Create the loop header block, which later will merge the backward branches of
                     // the loop.
                     AbstractEndNode preLoopEnd = currentGraph.add(new EndNode());
@@ -1360,12 +1450,12 @@
                      * merge to the loop header. This ensures that the loop header has exactly one
                      * non-loop predecessor.
                      */
-                    block.firstInstruction = loopBegin;
+                    block.setFirstInstruction(this.getCurrentDimension(), loopBegin);
                     /*
                      * We need to preserve the frame state builder of the loop header so that we can
                      * merge values for phi functions, so make a copy of it.
                      */
-                    block.entryState = frameState.copy();
+                    block.setEntryState(this.getCurrentDimension(), frameState.copy());
 
                     Debug.log("  created loop header %s", loopBegin);
                 }
@@ -1544,6 +1634,13 @@
                 frameState.push(kind, value);
             }
 
+            private int getCurrentDimension() {
+                if (this.explodeLoopsContext == null || this.explodeLoopsContext.isEmpty()) {
+                    return 0;
+                } else {
+                    return this.explodeLoopsContext.peek().peelIteration;
+                }
+            }
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/AddNode.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/AddNode.java	Fri Feb 06 03:24:50 2015 +0100
@@ -39,6 +39,17 @@
         super(ArithmeticOpTable::getAdd, x, y);
     }
 
+    public static ValueNode create(ValueNode x, ValueNode y) {
+        BinaryOp<Add> op = ArithmeticOpTable.forStamp(x.stamp()).getAdd();
+        Stamp stamp = op.foldStamp(x.stamp(), y.stamp());
+        ConstantNode tryConstantFold = tryConstantFold(op, x, y, stamp);
+        if (tryConstantFold != null) {
+            return tryConstantFold;
+        } else {
+            return new AddNode(x, y);
+        }
+    }
+
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
         ValueNode ret = super.canonical(tool, forX, forY);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/BinaryArithmeticNode.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/BinaryArithmeticNode.java	Fri Feb 06 03:24:50 2015 +0100
@@ -61,13 +61,21 @@
 
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
-        if (forX.isConstant() && forY.isConstant()) {
-            Constant ret = getOp(forX, forY).foldConstant(forX.asConstant(), forY.asConstant());
-            return ConstantNode.forPrimitive(stamp(), ret);
+        ValueNode result = tryConstantFold(getOp(forX, forY), forX, forY, stamp());
+        if (result != null) {
+            return result;
         }
         return this;
     }
 
+    public static <OP> ConstantNode tryConstantFold(BinaryOp<OP> op, ValueNode forX, ValueNode forY, Stamp stamp) {
+        if (forX.isConstant() && forY.isConstant()) {
+            Constant ret = op.foldConstant(forX.asConstant(), forY.asConstant());
+            return ConstantNode.forPrimitive(stamp, ret);
+        }
+        return null;
+    }
+
     @Override
     public boolean inferStamp() {
         return updateStamp(getOp(getX(), getY()).foldStamp(getX().stamp(), getY().stamp()));
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerBelowNode.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerBelowNode.java	Fri Feb 06 03:24:50 2015 +0100
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.nodes.calc;
 
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.calc.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.spi.*;
@@ -38,12 +39,33 @@
         assert y.stamp() instanceof IntegerStamp;
     }
 
+    public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
+        LogicNode result = CompareNode.tryConstantFold(Condition.BT, x, y, constantReflection, false);
+        if (result != null) {
+            return result;
+        } else {
+            result = findSynonym(x, y);
+            if (result != null) {
+                return result;
+            }
+            return new IntegerBelowNode(x, y);
+        }
+    }
+
     @Override
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode forY) {
         ValueNode result = super.canonical(tool, forX, forY);
         if (result != this) {
             return result;
         }
+        LogicNode synonym = findSynonym(forX, forY);
+        if (synonym != null) {
+            return synonym;
+        }
+        return this;
+    }
+
+    private static LogicNode findSynonym(ValueNode forX, ValueNode forY) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return LogicConstantNode.contradiction();
         } else if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
@@ -61,7 +83,7 @@
             // 0 |<| y is the same as 0 != y
             return new LogicNegationNode(CompareNode.createCompareNode(Condition.EQ, forX, forY));
         }
-        return this;
+        return null;
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerLessThanNode.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerLessThanNode.java	Fri Feb 06 03:24:50 2015 +0100
@@ -40,6 +40,19 @@
         assert !y.getKind().isNumericFloat() && y.getKind() != Kind.Object;
     }
 
+    public static LogicNode create(ValueNode x, ValueNode y, ConstantReflectionProvider constantReflection) {
+        LogicNode result = CompareNode.tryConstantFold(Condition.LT, x, y, constantReflection, false);
+        if (result != null) {
+            return result;
+        } else {
+            result = findSynonym(x, y);
+            if (result != null) {
+                return result;
+            }
+            return new IntegerLessThanNode(x, y);
+        }
+    }
+
     @Override
     protected ValueNode optimizeNormalizeCmp(Constant constant, NormalizeCompareNode normalizeNode, boolean mirrored) {
         PrimitiveConstant primitive = (PrimitiveConstant) constant;
@@ -63,6 +76,19 @@
         if (result != this) {
             return result;
         }
+        ValueNode synonym = findSynonym(forX, forY);
+        if (synonym != null) {
+            return synonym;
+        }
+        if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
+            if (IntegerStamp.sameSign((IntegerStamp) forX.stamp(), (IntegerStamp) forY.stamp())) {
+                return new IntegerBelowNode(forX, forY);
+            }
+        }
+        return this;
+    }
+
+    private static LogicNode findSynonym(ValueNode forX, ValueNode forY) {
         if (GraphUtil.unproxify(forX) == GraphUtil.unproxify(forY)) {
             return LogicConstantNode.contradiction();
         } else if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
@@ -74,12 +100,7 @@
                 return LogicConstantNode.contradiction();
             }
         }
-        if (forX.stamp() instanceof IntegerStamp && forY.stamp() instanceof IntegerStamp) {
-            if (IntegerStamp.sameSign((IntegerStamp) forX.stamp(), (IntegerStamp) forY.stamp())) {
-                return new IntegerBelowNode(forX, forY);
-            }
-        }
-        return this;
+        return null;
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java	Fri Feb 06 03:24:50 2015 +0100
@@ -51,6 +51,14 @@
         this.array = array;
     }
 
+    public static ValueNode create(ValueNode forValue, ConstantReflectionProvider constantReflection) {
+        ValueNode length = readArrayLength(forValue, constantReflection);
+        if (length != null) {
+            return length;
+        }
+        return new ArrayLengthNode(forValue);
+    }
+
     public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
         ValueNode length = readArrayLength(forValue, tool.getConstantReflection());
         if (length != null) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java	Fri Feb 06 03:24:50 2015 +0100
@@ -48,6 +48,14 @@
         this(createStamp(array, elementKind), array, index, elementKind);
     }
 
+    public static ValueNode create(ValueNode array, ValueNode index, Kind elementKind, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) {
+        ValueNode constant = tryConstantFold(array, index, metaAccess, constantReflection);
+        if (constant != null) {
+            return constant;
+        }
+        return new LoadIndexedNode(array, index, elementKind);
+    }
+
     protected LoadIndexedNode(Stamp stamp, ValueNode array, ValueNode index, Kind elementKind) {
         super(stamp, array, index, elementKind);
     }
@@ -79,15 +87,23 @@
     }
 
     public Node canonical(CanonicalizerTool tool) {
-        if (array().isConstant() && !array().isNullConstant() && index().isConstant()) {
-            JavaConstant arrayConstant = array().asJavaConstant();
+        ValueNode constant = tryConstantFold(array(), index(), tool.getMetaAccess(), tool.getConstantReflection());
+        if (constant != null) {
+            return constant;
+        }
+        return this;
+    }
+
+    private static ValueNode tryConstantFold(ValueNode array, ValueNode index, MetaAccessProvider metaAccess, ConstantReflectionProvider constantReflection) {
+        if (array.isConstant() && !array.isNullConstant() && index.isConstant()) {
+            JavaConstant arrayConstant = array.asJavaConstant();
             if (arrayConstant != null) {
-                JavaConstant constant = tool.getConstantReflection().readConstantArrayElement(arrayConstant, index().asJavaConstant().asInt());
+                JavaConstant constant = constantReflection.readConstantArrayElement(arrayConstant, index.asJavaConstant().asInt());
                 if (constant != null) {
-                    return ConstantNode.forConstant(constant, tool.getMetaAccess());
+                    return ConstantNode.forConstant(constant, metaAccess);
                 }
             }
         }
-        return this;
+        return null;
     }
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Thu Feb 05 04:16:48 2015 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri Feb 06 03:24:50 2015 +0100
@@ -148,12 +148,17 @@
         public boolean apply(GraphBuilderContext builder, ValueNode receiver, ResolvedJavaField field) {
             if (receiver.isConstant()) {
                 JavaConstant asJavaConstant = receiver.asJavaConstant();
-                JavaConstant result = providers.getConstantReflection().readConstantFieldValue(field, asJavaConstant);
-                if (result != null) {
-                    ConstantNode constantNode = builder.append(ConstantNode.forConstant(result, providers.getMetaAccess()));
-                    builder.push(constantNode.getKind().getStackKind(), constantNode);
-                    return true;
-                }
+                return tryConstantFold(builder, field, asJavaConstant);
+            }
+            return false;
+        }
+
+        private boolean tryConstantFold(GraphBuilderContext builder, ResolvedJavaField field, JavaConstant asJavaConstant) {
+            JavaConstant result = providers.getConstantReflection().readConstantFieldValue(field, asJavaConstant);
+            if (result != null) {
+                ConstantNode constantNode = builder.append(ConstantNode.forConstant(result, providers.getMetaAccess()));
+                builder.push(constantNode.getKind().getStackKind(), constantNode);
+                return true;
             }
             return false;
         }
@@ -164,7 +169,7 @@
                 builder.push(trueNode.getKind().getStackKind(), trueNode);
                 return true;
             }
-            return false;
+            return tryConstantFold(builder, staticField, null);
         }
     }