changeset 19633:cb701331de39

Merge.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Fri, 27 Feb 2015 16:36:38 +0100
parents afad10e79e13 (diff) 73811d1b4cd0 (current diff)
children 7b42a78ea270
files graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMonitorValue.java graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java
diffstat 24 files changed, 731 insertions(+), 125 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/BytecodeFrame.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/BytecodeFrame.java	Fri Feb 27 16:36:38 2015 +0100
@@ -170,7 +170,7 @@
                 Kind kind = values[i].getKind();
                 if (kind.needsTwoSlots()) {
                     assert values.length > i + 1 : String.format("missing second word %s", this);
-                    assert values[i + 1] == null || values[i + 1].getKind() == Kind.Illegal;
+                    assert values[i + 1] == null || values[i + 1].getKind() == Kind.Illegal : this;
                 }
             }
         }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Fri Feb 27 16:36:38 2015 +0100
@@ -303,6 +303,7 @@
 
     protected static String getCanonicalGraphString(StructuredGraph graph, boolean excludeVirtual, boolean checkConstants) {
         SchedulePhase schedule = new SchedulePhase();
+        schedule.setScheduleConstants(true);
         schedule.apply(graph);
 
         NodeMap<Integer> canonicalId = graph.createNodeMap();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LongNodeChainTest.java	Fri Feb 27 16:36:38 2015 +0100
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.test;
+
+import org.junit.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.schedule.*;
+import com.oracle.graal.phases.schedule.SchedulePhase.SchedulingStrategy;
+import com.oracle.graal.phases.tiers.*;
+
+public class LongNodeChainTest extends GraalCompilerTest {
+
+    public static final int N = 100000;
+
+    @Ignore
+    @Test
+    public void testLongAddChain() {
+        longAddChain(true);
+        longAddChain(false);
+    }
+
+    private void longAddChain(boolean reverse) {
+        HighTierContext context = new HighTierContext(getProviders(), null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
+        StructuredGraph graph = new StructuredGraph(AllowAssumptions.NO);
+        ValueNode constant = graph.unique(ConstantNode.forPrimitive(JavaConstant.INT_1));
+        ValueNode value = null;
+        if (reverse) {
+            AddNode addNode = graph.unique(new AddNode(constant, constant));
+            value = addNode;
+            for (int i = 1; i < N; ++i) {
+                AddNode newAddNode = graph.addWithoutUnique(new AddNode(constant, constant));
+                addNode.setY(newAddNode);
+                addNode = newAddNode;
+            }
+        } else {
+            value = constant;
+            for (int i = 0; i < N; ++i) {
+                value = graph.unique(new AddNode(constant, value));
+            }
+        }
+        ReturnNode returnNode = graph.add(new ReturnNode(value));
+        graph.start().setNext(returnNode);
+
+        for (SchedulingStrategy s : SchedulingStrategy.values()) {
+            new SchedulePhase(s).apply(graph);
+        }
+
+        new CanonicalizerPhase(false).apply(graph, context);
+        JavaConstant asConstant = (JavaConstant) returnNode.result().asConstant();
+        Assert.assertEquals(N + 1, asConstant.asInt());
+    }
+}
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/DebugInfoBuilder.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/DebugInfoBuilder.java	Fri Feb 27 16:36:38 2015 +0100
@@ -113,6 +113,7 @@
         }
         objectStates.clear();
 
+        assert frame.validateFormat();
         return newLIRFrameState(exceptionEdge, frame, virtualObjectsArray);
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.graph.test/src/com/oracle/graal/graph/test/NodeValidationChecksTest.java	Fri Feb 27 16:36:38 2015 +0100
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015, 2015, 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.graph.test;
+
+import org.junit.*;
+
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodeinfo.*;
+
+public class NodeValidationChecksTest {
+
+    @NodeInfo
+    static final class TestNode extends Node {
+        public static final NodeClass<TestNode> TYPE = NodeClass.create(TestNode.class);
+
+        @Input TestNode input;
+        @Successor TestNode successor;
+
+        public TestNode(TestNode input, TestNode successor) {
+            super(TYPE);
+            this.input = input;
+            this.successor = successor;
+        }
+    }
+
+    @Test
+    public void testInputNotAlive() {
+        Graph graph = new Graph();
+        TestNode node = new TestNode(null, null);
+        try {
+            graph.add(new TestNode(node, null));
+            Assert.fail("Exception expected.");
+        } catch (IllegalStateException e) {
+            Assert.assertTrue(e.getMessage().contains("Input"));
+            Assert.assertTrue(e.getMessage().contains("not alive"));
+        }
+    }
+
+    @Test
+    public void testSuccessorNotAlive() {
+        Graph graph = new Graph();
+        TestNode node = new TestNode(null, null);
+        try {
+            graph.add(new TestNode(null, node));
+            Assert.fail("Exception expected.");
+        } catch (IllegalStateException e) {
+            Assert.assertTrue(e.getMessage().contains("Successor"));
+            Assert.assertTrue(e.getMessage().contains("not alive"));
+        }
+    }
+}
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Edges.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Edges.java	Fri Feb 27 16:36:38 2015 +0100
@@ -61,20 +61,20 @@
         }
     }
 
-    private static Node getNode(Node node, long offset) {
+    private static Node getNodeUnsafe(Node node, long offset) {
         return (Node) unsafe.getObject(node, offset);
     }
 
     @SuppressWarnings("unchecked")
-    private static NodeList<Node> getNodeList(Node node, long offset) {
+    private static NodeList<Node> getNodeListUnsafe(Node node, long offset) {
         return (NodeList<Node>) unsafe.getObject(node, offset);
     }
 
-    private static void putNode(Node node, long offset, Node value) {
+    private static void putNodeUnsafe(Node node, long offset, Node value) {
         unsafe.putObject(node, offset, value);
     }
 
-    private static void putNodeList(Node node, long offset, NodeList<?> value) {
+    private static void putNodeListUnsafe(Node node, long offset, NodeList<?> value) {
         unsafe.putObject(node, offset, value);
     }
 
@@ -93,9 +93,8 @@
      * @param index the index of a non-list the edge (must be less than {@link #getDirectCount()})
      * @return the Node at the other edge of the requested edge
      */
-    public Node getNode(Node node, int index) {
-        assert index >= 0 && index < directCount;
-        return getNode(node, offsets[index]);
+    public static Node getNode(Node node, long[] offsets, int index) {
+        return getNodeUnsafe(node, offsets[index]);
     }
 
     /**
@@ -106,9 +105,8 @@
      *            {@link #getDirectCount()})
      * @return the {@link NodeList} at the other edge of the requested edge
      */
-    public NodeList<Node> getNodeList(Node node, int index) {
-        assert index >= directCount && index < getCount();
-        return getNodeList(node, offsets[index]);
+    public static NodeList<Node> getNodeList(Node node, long[] offsets, int index) {
+        return getNodeListUnsafe(node, offsets[index]);
     }
 
     /**
@@ -119,18 +117,22 @@
      * @param node the node whose edges are to be cleared
      */
     public void clear(Node node) {
+        final long[] curOffsets = this.offsets;
+        final Type curType = this.type;
         int index = 0;
-        while (index < getDirectCount()) {
-            initializeNode(node, index++, null);
+        int curDirectCount = getDirectCount();
+        while (index < curDirectCount) {
+            initializeNode(node, curOffsets, index++, null);
         }
-        while (index < getCount()) {
-            NodeList<Node> list = getNodeList(node, index);
+        int curCount = getCount();
+        while (index < curCount) {
+            NodeList<Node> list = getNodeList(node, curOffsets, index);
             if (list != null) {
                 int size = list.initialSize;
-                NodeList<Node> newList = type == Edges.Type.Inputs ? new NodeInputList<>(node, size) : new NodeSuccessorList<>(node, size);
+                NodeList<Node> newList = curType == Edges.Type.Inputs ? new NodeInputList<>(node, size) : new NodeSuccessorList<>(node, size);
 
                 // replacing with a new list object is the expected behavior!
-                initializeList(node, index, newList);
+                initializeList(node, curOffsets, index, newList);
             }
             index++;
         }
@@ -145,12 +147,14 @@
      */
     public void initializeLists(Node node, Node prototype) {
         int index = getDirectCount();
+        final long[] curOffsets = this.offsets;
+        final Edges.Type curType = this.type;
         while (index < getCount()) {
-            NodeList<Node> list = getNodeList(prototype, index);
+            NodeList<Node> list = getNodeList(prototype, curOffsets, index);
             if (list != null) {
                 int size = list.initialSize;
-                NodeList<Node> newList = type == Edges.Type.Inputs ? new NodeInputList<>(node, size) : new NodeSuccessorList<>(node, size);
-                initializeList(node, index, newList);
+                NodeList<Node> newList = curType == Edges.Type.Inputs ? new NodeInputList<>(node, size) : new NodeSuccessorList<>(node, size);
+                initializeList(node, curOffsets, index, newList);
             }
             index++;
         }
@@ -167,16 +171,20 @@
         assert fromNode != toNode;
         assert fromNode.getNodeClass().getClazz() == toNode.getNodeClass().getClazz();
         int index = 0;
-        while (index < getDirectCount()) {
-            initializeNode(toNode, index, getNode(fromNode, index));
+        final long[] curOffsets = this.offsets;
+        final Type curType = this.type;
+        int curDirectCount = getDirectCount();
+        while (index < curDirectCount) {
+            initializeNode(toNode, curOffsets, index, getNode(fromNode, curOffsets, index));
             index++;
         }
-        while (index < getCount()) {
-            NodeList<Node> list = getNodeList(toNode, index);
-            NodeList<Node> fromList = getNodeList(fromNode, index);
+        int curCount = getCount();
+        while (index < curCount) {
+            NodeList<Node> list = getNodeList(toNode, curOffsets, index);
+            NodeList<Node> fromList = getNodeList(fromNode, curOffsets, index);
             if (list == null || list == fromList) {
-                list = type == Edges.Type.Inputs ? new NodeInputList<>(toNode, fromList) : new NodeSuccessorList<>(toNode, fromList);
-                initializeList(toNode, index, list);
+                list = curType == Edges.Type.Inputs ? new NodeInputList<>(toNode, fromList) : new NodeSuccessorList<>(toNode, fromList);
+                initializeList(toNode, curOffsets, index, list);
             } else {
                 list.copy(fromList);
             }
@@ -195,17 +203,20 @@
      */
     public boolean replaceFirst(Node node, Node key, Node replacement) {
         int index = 0;
-        while (index < getDirectCount()) {
-            Node edge = getNode(node, index);
+        final long[] curOffsets = this.getOffsets();
+        int curDirectCount = getDirectCount();
+        while (index < curDirectCount) {
+            Node edge = getNode(node, curOffsets, index);
             if (edge == key) {
                 assert replacement == null || getType(index).isAssignableFrom(replacement.getClass()) : "Can not assign " + replacement.getClass() + " to " + getType(index) + " in " + node;
-                initializeNode(node, index, replacement);
+                initializeNode(node, curOffsets, index, replacement);
                 return true;
             }
             index++;
         }
-        while (index < getCount()) {
-            NodeList<Node> list = getNodeList(node, index);
+        int curCount = getCount();
+        while (index < curCount) {
+            NodeList<Node> list = getNodeList(node, curOffsets, index);
             if (list != null) {
                 if (list.replaceFirst(key, replacement)) {
                     return true;
@@ -229,13 +240,12 @@
      * @param index the index of the edge (between 0 and {@link #getCount()})
      * @param value the node to be written to the edge
      */
-    public void initializeNode(Node node, int index, Node value) {
-        putNode(node, offsets[index], value);
+    public static void initializeNode(Node node, long[] offsets, int index, Node value) {
+        putNodeUnsafe(node, offsets[index], value);
     }
 
-    public void initializeList(Node node, int index, NodeList<Node> value) {
-        assert index >= directCount;
-        putNodeList(node, offsets[index], value);
+    public static void initializeList(Node node, long[] offsets, int index, NodeList<Node> value) {
+        putNodeListUnsafe(node, offsets[index], value);
     }
 
     /**
@@ -248,21 +258,22 @@
      */
     public void setNode(Node node, int index, Node value) {
         assert index < directCount;
-        Node old = getNode(node, offsets[index]);
-        putNode(node, offsets[index], value);
+        Node old = getNodeUnsafe(node, offsets[index]);
+        putNodeUnsafe(node, offsets[index], value);
         update(node, old, value);
     }
 
     protected abstract void update(Node node, Node oldValue, Node newValue);
 
     public boolean contains(Node node, Node value) {
+        final long[] curOffsets = this.offsets;
         for (int i = 0; i < directCount; i++) {
-            if (getNode(node, i) == value) {
+            if (getNode(node, curOffsets, i) == value) {
                 return true;
             }
         }
         for (int i = directCount; i < getCount(); i++) {
-            NodeList<?> curList = getNodeList(node, i);
+            NodeList<?> curList = getNodeList(node, curOffsets, i);
             if (curList != null && curList.contains(value)) {
                 return true;
             }
@@ -276,15 +287,16 @@
     public boolean areEqualIn(Node node, Node other) {
         assert node.getNodeClass().getClazz() == other.getNodeClass().getClazz();
         int index = 0;
+        final long[] curOffsets = this.offsets;
         while (index < directCount) {
-            if (getNode(other, index) != getNode(node, index)) {
+            if (getNode(other, curOffsets, index) != getNode(node, curOffsets, index)) {
                 return false;
             }
             index++;
         }
         while (index < getCount()) {
-            NodeList<Node> list = getNodeList(other, index);
-            if (!Objects.equals(list, getNodeList(node, index))) {
+            NodeList<Node> list = getNodeList(other, curOffsets, index);
+            if (!Objects.equals(list, getNodeList(node, curOffsets, index))) {
                 return false;
             }
             index++;
@@ -306,6 +318,9 @@
         NodeList<Node> list;
         protected boolean needsForward;
         protected Node nextElement;
+        protected final int directCount;
+        protected final int count;
+        protected final long[] offsets;
 
         /**
          * Creates an iterator that will iterate over some given edges in a given node.
@@ -316,14 +331,17 @@
             index = NOT_ITERABLE;
             subIndex = 0;
             needsForward = true;
+            this.directCount = edges.getDirectCount();
+            this.offsets = edges.getOffsets();
+            this.count = edges.getCount();
         }
 
         void forward() {
             needsForward = false;
-            if (index < edges.getDirectCount()) {
+            if (index < directCount) {
                 index++;
-                while (index < edges.getDirectCount()) {
-                    nextElement = edges.getNode(node, index);
+                while (index < directCount) {
+                    nextElement = Edges.getNode(node, offsets, index);
                     if (nextElement != null) {
                         return;
                     }
@@ -340,7 +358,7 @@
         private void forwardNodeList() {
             do {
                 if (subIndex == 0) {
-                    list = edges.getNodeList(node, index);
+                    list = Edges.getNodeList(node, offsets, index);
                 }
                 if (list != null) {
                     while (subIndex < list.size()) {
@@ -361,7 +379,7 @@
                 forward();
             }
             needsForward = true;
-            if (index < edges.getCount()) {
+            if (index < count) {
                 return nextElement;
             }
             throw new NoSuchElementException();
@@ -385,7 +403,7 @@
                 forward();
             }
             needsForward = true;
-            if (index < edges.getDirectCount()) {
+            if (index < directCount) {
                 return new Position(edges, index, NOT_ITERABLE);
             } else {
                 return new Position(edges, index, subIndex);
@@ -399,6 +417,7 @@
     }
 
     private static class AllEdgesIterator extends EdgesIterator {
+
         AllEdgesIterator(Node node, Edges edges) {
             super(node, edges);
         }
@@ -406,10 +425,10 @@
         @Override
         void forward() {
             needsForward = false;
-            if (index < edges.getDirectCount()) {
+            if (index < directCount) {
                 index++;
                 if (index < edges.getDirectCount()) {
-                    nextElement = edges.getNode(node, index);
+                    nextElement = Edges.getNode(node, edges.getOffsets(), index);
                     return;
                 }
             } else {
@@ -417,7 +436,7 @@
             }
             while (index < edges.getCount()) {
                 if (subIndex == 0) {
-                    list = edges.getNodeList(node, index);
+                    list = Edges.getNodeList(node, edges.getOffsets(), index);
                 }
                 if (list != null) {
                     if (subIndex < list.size()) {
@@ -468,6 +487,9 @@
         }
     }
 
+    static int cnt1;
+    static int cnt2;
+
     public NodeClassIterable getIterable(final Node node) {
         return new NodeClassIterable() {
 
@@ -498,8 +520,9 @@
     public void accept(Node node, BiConsumer<Node, Node> consumer) {
         int index = 0;
         int curDirectCount = this.directCount;
+        final long[] curOffsets = this.offsets;
         while (index < curDirectCount) {
-            Node curNode = getNode(node, index);
+            Node curNode = getNode(node, curOffsets, index);
             if (curNode != null) {
                 consumer.accept(node, curNode);
             }
@@ -507,7 +530,7 @@
         }
         int count = getCount();
         while (index < count) {
-            NodeList<Node> list = getNodeList(node, index);
+            NodeList<Node> list = getNodeList(node, curOffsets, index);
             if (list != null) {
                 for (int i = 0; i < list.size(); ++i) {
                     Node curNode = list.get(i);
@@ -519,4 +542,8 @@
             index++;
         }
     }
+
+    public long[] getOffsets() {
+        return this.offsets;
+    }
 }
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Fri Feb 27 16:36:38 2015 +0100
@@ -521,8 +521,18 @@
         assert assertTrue(id == INITIAL_ID, "unexpected id: %d", id);
         this.graph = newGraph;
         newGraph.register(this);
-        this.acceptInputs((n, i) -> n.updateUsages(null, i));
-        this.acceptSuccessors((n, s) -> n.updatePredecessor(null, s));
+        this.acceptInputs((n, i) -> {
+            if (!i.isAlive()) {
+                throw new IllegalStateException(String.format("Input %s of newly created node %s is not alive.", i, n));
+            }
+            n.updateUsages(null, i);
+        });
+        this.acceptSuccessors((n, s) -> {
+            if (!s.isAlive()) {
+                throw new IllegalStateException(String.format("Successor %s of newly created node %s is not alive.", s, n));
+            }
+            n.updatePredecessor(null, s);
+        });
     }
 
     public final NodeClass<? extends Node> getNodeClass() {
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Fri Feb 27 16:36:38 2015 +0100
@@ -585,8 +585,9 @@
         int index = 0;
         Type curType = edges.type();
         int directCount = edges.getDirectCount();
+        final long[] curOffsets = edges.getOffsets();
         while (index < directCount) {
-            Node edge = edges.getNode(node, index);
+            Node edge = Edges.getNode(node, curOffsets, index);
             if (edge != null) {
                 Node newEdge = duplicationReplacement.replacement(edge, curType);
                 if (curType == Edges.Type.Inputs) {
@@ -595,15 +596,15 @@
                     node.updatePredecessor(null, newEdge);
                 }
                 assert assertUpdateValid(node, edges, index, newEdge);
-                edges.initializeNode(node, index, newEdge);
+                Edges.initializeNode(node, curOffsets, index, newEdge);
             }
             index++;
         }
 
         while (index < edges.getCount()) {
-            NodeList<Node> list = edges.getNodeList(node, index);
+            NodeList<Node> list = Edges.getNodeList(node, curOffsets, index);
             if (list != null) {
-                edges.initializeList(node, index, updateEdgeListCopy(node, list, duplicationReplacement, curType));
+                Edges.initializeList(node, curOffsets, index, updateEdgeListCopy(node, list, duplicationReplacement, curType));
             }
             index++;
         }
@@ -659,9 +660,10 @@
 
     private void initNullEdgeLists(Node node, Edges.Type type) {
         Edges edges = getEdges(type);
+        final long[] curOffsets = edges.getOffsets();
         for (int inputPos = edges.getDirectCount(); inputPos < edges.getCount(); inputPos++) {
-            if (edges.getNodeList(node, inputPos) == null) {
-                edges.initializeList(node, inputPos, type == Edges.Type.Inputs ? new NodeInputList<>(node) : new NodeSuccessorList<>(node));
+            if (Edges.getNodeList(node, curOffsets, inputPos) == null) {
+                Edges.initializeList(node, curOffsets, inputPos, type == Edges.Type.Inputs ? new NodeInputList<>(node) : new NodeSuccessorList<>(node));
             }
         }
     }
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Position.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Position.java	Fri Feb 27 16:36:38 2015 +0100
@@ -53,9 +53,9 @@
 
     public Node get(Node node) {
         if (index < edges.getDirectCount()) {
-            return edges.getNode(node, index);
+            return Edges.getNode(node, edges.getOffsets(), index);
         } else {
-            return edges.getNodeList(node, index).get(subIndex);
+            return Edges.getNodeList(node, edges.getOffsets(), index).get(subIndex);
         }
     }
 
@@ -75,15 +75,15 @@
         if (index < edges.getDirectCount()) {
             edges.setNode(node, index, value);
         } else {
-            edges.getNodeList(node, index).set(subIndex, value);
+            Edges.getNodeList(node, edges.getOffsets(), index).set(subIndex, value);
         }
     }
 
     public void initialize(Node node, Node value) {
         if (index < edges.getDirectCount()) {
-            edges.initializeNode(node, index, value);
+            Edges.initializeNode(node, edges.getOffsets(), index, value);
         } else {
-            edges.getNodeList(node, index).initialize(subIndex, value);
+            Edges.getNodeList(node, edges.getOffsets(), index).initialize(subIndex, value);
         }
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Fri Feb 27 16:36:38 2015 +0100
@@ -251,7 +251,7 @@
 
     /**
      * Computes the size of the memory chunk allocated for an array. This size accounts for the
-     * array header size, boy size and any padding after the last element to satisfy object
+     * array header size, body size and any padding after the last element to satisfy object
      * alignment requirements.
      *
      * @param length the number of elements in the array
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Fri Feb 27 16:36:38 2015 +0100
@@ -179,7 +179,7 @@
 
         private static class ExplodedLoopContext {
             private BciBlock header;
-            private int targetPeelIteration;
+            private int[] targetPeelIteration;
             private int peelIteration;
         }
 
@@ -203,6 +203,8 @@
 
             private FixedWithNextNode lastInstr;                 // the last instruction added
             private final boolean explodeLoops;
+            private final boolean mergeExplosions;
+            private final Map<HIRFrameStateBuilder, Integer> mergeExplosionsMap;
             private Stack<ExplodedLoopContext> explodeLoopsContext;
             private int nextPeelIteration = 1;
             private boolean controlFlowSplit;
@@ -230,8 +232,17 @@
                 LoopExplosionPlugin loopExplosionPlugin = graphBuilderConfig.getLoopExplosionPlugin();
                 if (loopExplosionPlugin != null) {
                     explodeLoops = loopExplosionPlugin.shouldExplodeLoops(method);
+                    if (explodeLoops) {
+                        mergeExplosions = loopExplosionPlugin.shouldMergeExplosions(method);
+                        mergeExplosionsMap = new HashMap<>();
+                    } else {
+                        mergeExplosions = false;
+                        mergeExplosionsMap = null;
+                    }
                 } else {
                     explodeLoops = false;
+                    mergeExplosions = false;
+                    mergeExplosionsMap = null;
                 }
             }
 
@@ -342,34 +353,49 @@
                 ExplodedLoopContext context = new ExplodedLoopContext();
                 context.header = header;
                 context.peelIteration = this.getCurrentDimension();
-                context.targetPeelIteration = -1;
+                if (this.mergeExplosions) {
+                    this.addToMergeCache(getEntryState(context.header, context.peelIteration), context.peelIteration);
+                }
                 explodeLoopsContext.push(context);
                 if (Debug.isDumpEnabled() && DumpDuringGraphBuilding.getValue()) {
                     Debug.dump(currentGraph, "before loop explosion dimension " + context.peelIteration);
                 }
 
+                peelIteration(blocks, header, context);
+                explodeLoopsContext.pop();
+                return header.loopEnd + 1;
+            }
+
+            private void addToMergeCache(HIRFrameStateBuilder key, int dimension) {
+                mergeExplosionsMap.put(key, dimension);
+            }
+
+            private void peelIteration(BciBlock[] blocks, BciBlock header, ExplodedLoopContext context) {
                 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) {
+                    int[] targets = context.targetPeelIteration;
+                    if (targets != null) {
                         // We were reaching the backedge during explosion. Explode further.
-                        context.peelIteration = context.targetPeelIteration;
-                        context.targetPeelIteration = -1;
-                        if (Debug.isDumpEnabled() && DumpDuringGraphBuilding.getValue()) {
-                            Debug.dump(currentGraph, "next loop explosion iteration " + context.peelIteration);
+                        for (int i = 0; i < targets.length; ++i) {
+                            context.peelIteration = targets[i];
+                            context.targetPeelIteration = null;
+                            if (Debug.isDumpEnabled() && DumpDuringGraphBuilding.getValue()) {
+                                Debug.dump(currentGraph, "next loop explosion iteration " + context.peelIteration);
+                            }
+                            if (i < targets.length - 1) {
+                                peelIteration(blocks, header, context);
+                            }
                         }
                     } else {
                         // We did not reach the backedge. Exit.
                         break;
                     }
                 }
-                explodeLoopsContext.pop();
-                return header.loopEnd + 1;
             }
 
             /**
@@ -1356,7 +1382,7 @@
                 assert block != null && state != null;
                 assert !block.isExceptionEntry || state.stackSize() == 1;
 
-                int operatingDimension = findOperatingDimension(block);
+                int operatingDimension = findOperatingDimension(block, state);
 
                 if (getFirstInstruction(block, operatingDimension) == null) {
                     /*
@@ -1401,8 +1427,8 @@
                     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 getFirstInstruction(block, operatingDimension).next() == null : "bytecodes already parsed for block";
+                assert currentBlock == null || currentBlock.getId() < block.getId() || this.mergeExplosions : "must not be backward branch";
+                assert getFirstInstruction(block, operatingDimension).next() == null || this.mergeExplosions : "bytecodes already parsed for block";
 
                 if (getFirstInstruction(block, operatingDimension) instanceof AbstractBeginNode && !(getFirstInstruction(block, operatingDimension) instanceof AbstractMergeNode)) {
                     /*
@@ -1443,37 +1469,52 @@
                 return result;
             }
 
-            private int findOperatingDimension(BciBlock block) {
+            private int findOperatingDimension(BciBlock block, HIRFrameStateBuilder state) {
                 if (this.explodeLoops && this.explodeLoopsContext != null && !this.explodeLoopsContext.isEmpty()) {
-                    return findOperatingDimensionWithLoopExplosion(block);
+                    return findOperatingDimensionWithLoopExplosion(block, state);
                 }
                 return this.getCurrentDimension();
             }
 
-            private int findOperatingDimensionWithLoopExplosion(BciBlock block) {
+            private int findOperatingDimensionWithLoopExplosion(BciBlock block, HIRFrameStateBuilder state) {
                 int i;
                 for (i = explodeLoopsContext.size() - 1; i >= 0; --i) {
                     ExplodedLoopContext context = explodeLoopsContext.elementAt(i);
                     if (context.header == block) {
 
+                        if (this.mergeExplosions) {
+                            state.clearNonLiveLocals(block, liveness, true);
+                            Integer cachedDimension = mergeExplosionsMap.get(state);
+                            if (cachedDimension != null) {
+                                return cachedDimension;
+                            }
+                        }
+
                         // 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 > MaximumLoopExplosionCount.getValue()) {
-                                String message = "too many loop explosion interations - does the explosion not terminate for method " + method + "?";
-                                if (FailedLoopExplosionIsFatal.getValue()) {
-                                    throw new RuntimeException(message);
-                                } else {
-                                    throw bailout(message);
-                                }
+                        if (context.targetPeelIteration == null) {
+                            context.targetPeelIteration = new int[1];
+                        } else {
+                            context.targetPeelIteration = Arrays.copyOf(context.targetPeelIteration, context.targetPeelIteration.length + 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.
+                        if (this.mergeExplosions) {
+                            this.addToMergeCache(state.copy(), nextPeelIteration);
+                        }
+                        context.targetPeelIteration[context.targetPeelIteration.length - 1] = nextPeelIteration++;
+                        if (nextPeelIteration > MaximumLoopExplosionCount.getValue()) {
+                            String message = "too many loop explosion interations - does the explosion not terminate for method " + method + "?";
+                            if (FailedLoopExplosionIsFatal.getValue()) {
+                                throw new RuntimeException(message);
+                            } else {
+                                throw bailout(message);
                             }
                         }
 
                         // Operate on the target dimension.
-                        return context.targetPeelIteration;
+                        return context.targetPeelIteration[context.targetPeelIteration.length - 1];
                     } else if (block.getId() > context.header.getId() && block.getId() <= context.header.loopEnd) {
                         // We hit the range of this context.
                         return context.peelIteration;
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPlugin.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPlugin.java	Fri Feb 27 16:36:38 2015 +0100
@@ -73,6 +73,8 @@
 
     public interface LoopExplosionPlugin extends GraphBuilderPlugin {
         boolean shouldExplodeLoops(ResolvedJavaMethod method);
+
+        boolean shouldMergeExplosions(ResolvedJavaMethod method);
     }
 
     public interface ParameterPlugin extends GraphBuilderPlugin {
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java	Fri Feb 27 16:36:38 2015 +0100
@@ -873,4 +873,61 @@
         assert xpeek() == null;
         return true;
     }
+
+    @Override
+    public int hashCode() {
+        int result = hashCode(locals, locals.length);
+        result *= 13;
+        result += hashCode(stack, this.stackSize);
+        return result;
+    }
+
+    private static int hashCode(Object[] a, int length) {
+        int result = 1;
+        for (int i = 0; i < length; ++i) {
+            Object element = a[i];
+            result = 31 * result + (element == null ? 0 : System.identityHashCode(element));
+        }
+        return result;
+    }
+
+    private static boolean equals(ValueNode[] a, ValueNode[] b, int length) {
+        for (int i = 0; i < length; ++i) {
+            if (a[i] != b[i]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public boolean equals(Object otherObject) {
+        if (otherObject instanceof HIRFrameStateBuilder) {
+            HIRFrameStateBuilder other = (HIRFrameStateBuilder) otherObject;
+            if (other.method != method) {
+                return false;
+            }
+            if (other.stackSize != stackSize) {
+                return false;
+            }
+            if (other.checkTypes != checkTypes) {
+                return false;
+            }
+            if (other.rethrowException != rethrowException) {
+                return false;
+            }
+            if (other.graph != graph) {
+                return false;
+            }
+            if (other.outerFrameStateSupplier != outerFrameStateSupplier) {
+                return false;
+            }
+            if (other.locals.length != locals.length) {
+                return false;
+            }
+            return equals(other.locals, locals, locals.length) && equals(other.stack, stack, stackSize) && equals(other.lockedObjects, lockedObjects, lockedObjects.length) &&
+                            equals(other.monitorIds, monitorIds, monitorIds.length);
+        }
+        return false;
+    }
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/elem/InlineableGraph.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/elem/InlineableGraph.java	Fri Feb 27 16:36:38 2015 +0100
@@ -211,7 +211,7 @@
             if (context.getGraphBuilderSuite() != null) {
                 context.getGraphBuilderSuite().apply(newGraph, context);
             }
-            assert newGraph.start().next() != null : "graph needs to be populated by the GraphBuilderSuite";
+            assert newGraph.start().next() != null : "graph needs to be populated by the GraphBuilderSuite " + method + ", " + method.canBeInlined();
 
             new DeadCodeEliminationPhase(Optional).apply(newGraph);
 
--- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java	Fri Feb 27 16:36:38 2015 +0100
@@ -441,11 +441,12 @@
     private void writeEdges(Node node, Edges.Type type) throws IOException {
         NodeClass<?> nodeClass = node.getNodeClass();
         Edges edges = nodeClass.getEdges(type);
+        final long[] curOffsets = edges.getOffsets();
         for (int i = 0; i < edges.getDirectCount(); i++) {
-            writeNodeRef(edges.getNode(node, i));
+            writeNodeRef(Edges.getNode(node, curOffsets, i));
         }
         for (int i = edges.getDirectCount(); i < edges.getCount(); i++) {
-            NodeList<Node> list = edges.getNodeList(node, i);
+            NodeList<Node> list = Edges.getNodeList(node, curOffsets, i);
             if (list == null) {
                 writeShort((char) 0);
             } else {
--- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinter.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/CFGPrinter.java	Fri Feb 27 16:36:38 2015 +0100
@@ -211,6 +211,8 @@
         begin("block");
 
         out.print("name \"").print(blockToString(block)).println('"');
+        out.println("from_bci -1");
+        out.println("to_bci -1");
 
         out.print("predecessors ");
         for (AbstractBlockBase<?> pred : block.getPredecessors()) {
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/EdgesTest.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/EdgesTest.java	Fri Feb 27 16:36:38 2015 +0100
@@ -78,20 +78,20 @@
 
     /**
      * Checks that there are no checkcasts in the compiled version of
-     * {@link Edges#getNode(Node, int)}.
+     * {@link Edges#getNode(Node, long[], int)}.
      */
     @Test
     public void test0() {
-        testMethod(getMethod("getNode", Node.class, int.class), inputs, node, 0);
+        testMethod(getMethod("getNode", Node.class, long[].class, int.class), inputs, node, 0);
     }
 
     /**
      * Checks that there are no checkcasts in the compiled version of
-     * {@link Edges#getNodeList(Node, int)}.
+     * {@link Edges#getNodeList(Node, long[], int)}.
      */
     @Test
     public void test1() {
-        testMethod(getMethod("getNodeList", Node.class, int.class), inputs, node, 2);
+        testMethod(getMethod("getNodeList", Node.class, long[].class, int.class), inputs, node, 2);
     }
 
     /**
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/EdgesSubstitutions.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/EdgesSubstitutions.java	Fri Feb 27 16:36:38 2015 +0100
@@ -40,22 +40,12 @@
 public class EdgesSubstitutions {
 
     @MethodSubstitution
-    private static Node getNode(Node node, long offset) {
+    private static Node getNodeUnsafe(Node node, long offset) {
         return PiNode.piCast(UnsafeLoadNode.load(node, offset, Kind.Object, LocationIdentity.ANY_LOCATION), Node.class);
     }
 
     @MethodSubstitution
-    private static NodeList<?> getNodeList(Node node, long offset) {
+    private static NodeList<?> getNodeListUnsafe(Node node, long offset) {
         return PiNode.piCast(UnsafeLoadNode.load(node, offset, Kind.Object, LocationIdentity.ANY_LOCATION), NodeList.class);
     }
-
-    @MethodSubstitution
-    private static void putNode(Node node, long offset, Node value) {
-        UnsafeStoreNode.store(node, offset, value, Kind.Object, LocationIdentity.ANY_LOCATION);
-    }
-
-    @MethodSubstitution
-    private static void putNodeList(Node node, long offset, NodeList<?> value) {
-        UnsafeStoreNode.store(node, offset, value, Kind.Object, LocationIdentity.ANY_LOCATION);
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/BytecodeInterpreterPartialEvaluationTest.java	Fri Feb 27 16:36:38 2015 +0100
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2015, 2015, 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.truffle.test;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.nodes.*;
+
+public class BytecodeInterpreterPartialEvaluationTest extends PartialEvaluationTest {
+
+    public static class Bytecode {
+        public static final byte CONST = 0;
+        public static final byte RETURN = 1;
+        public static final byte ADD = 2;
+        public static final byte IFZERO = 3;
+        public static final byte POP = 4;
+        public static final byte JMP = 5;
+        public static final byte DUP = 6;
+    }
+
+    public static boolean TRACE = false;
+
+    public static class Program extends RootNode {
+        private final String name;
+        @CompilationFinal private final byte[] bytecodes;
+        @CompilationFinal private final FrameSlot[] locals;
+        @CompilationFinal private final FrameSlot[] stack;
+
+        public Program(String name, byte[] bytecodes, int maxLocals, int maxStack) {
+            this.name = name;
+            this.bytecodes = bytecodes;
+            locals = new FrameSlot[maxLocals];
+            stack = new FrameSlot[maxStack];
+            for (int i = 0; i < maxLocals; ++i) {
+                locals[i] = this.getFrameDescriptor().addFrameSlot("local" + i);
+                locals[i].setKind(FrameSlotKind.Int);
+            }
+            for (int i = 0; i < maxStack; ++i) {
+                stack[i] = this.getFrameDescriptor().addFrameSlot("stack" + i);
+                stack[i].setKind(FrameSlotKind.Int);
+            }
+        }
+
+        protected void setInt(VirtualFrame frame, int stackIndex, int value) {
+            frame.setInt(stack[stackIndex], value);
+        }
+
+        protected int getInt(VirtualFrame frame, int stackIndex) {
+            try {
+                return frame.getInt(stack[stackIndex]);
+            } catch (FrameSlotTypeException e) {
+                throw new IllegalStateException("Error accessing stack slot " + stackIndex);
+            }
+        }
+
+        @Override
+        public String toString() {
+            return name;
+        }
+
+        public void trace(String format, Object... args) {
+            if (CompilerDirectives.inInterpreter() && TRACE) {
+                System.out.println(String.format(format, args));
+            }
+        }
+
+        @Override
+        @ExplodeLoop(merge = true)
+        public Object execute(VirtualFrame frame) {
+            trace("Start program");
+            int topOfStack = -1;
+            int bci = 0;
+            while (true) {
+                CompilerAsserts.partialEvaluationConstant(bci);
+                byte bc = bytecodes[bci];
+                byte value = 0;
+                switch (bc) {
+                    case Bytecode.CONST:
+                        value = bytecodes[bci + 1];
+                        trace("%d: CONST %s", bci, value);
+                        setInt(frame, ++topOfStack, value);
+                        bci = bci + 2;
+                        continue;
+
+                    case Bytecode.RETURN:
+                        trace("%d: RETURN", bci);
+                        return getInt(frame, topOfStack);
+
+                    case Bytecode.ADD: {
+                        int left = getInt(frame, topOfStack);
+                        int right = getInt(frame, topOfStack - 1);
+                        trace("%d: ADD %d %d", bci, left, right);
+                        setInt(frame, topOfStack - 1, left + right);
+                        topOfStack--;
+                        bci = bci + 1;
+                        continue;
+                    }
+
+                    case Bytecode.IFZERO:
+                        trace("%d: IFZERO", bci);
+                        if (getInt(frame, topOfStack--) == 0) {
+                            bci = bytecodes[bci + 1];
+                            continue;
+                        } else {
+                            bci = bci + 2;
+                            continue;
+                        }
+
+                    case Bytecode.POP:
+                        trace("%d: POP", bci);
+                        topOfStack--;
+                        bci++;
+                        continue;
+
+                    case Bytecode.JMP:
+                        trace("%d: JMP", bci);
+                        bci = bytecodes[bci + 1];
+                        continue;
+
+                    case Bytecode.DUP:
+                        trace("%d: DUP", bci);
+                        setInt(frame, topOfStack + 1, getInt(frame, topOfStack));
+                        topOfStack++;
+                        bci++;
+                        continue;
+                }
+            }
+        }
+    }
+
+    public static Object constant42() {
+        return 42;
+    }
+
+    private static void assertReturns42(Program program) {
+        Assert.assertEquals(Integer.valueOf(42), Truffle.getRuntime().createCallTarget(program).call());
+    }
+
+    private void assertPartialEvalEqualsAndRunsCorrect(Program program) {
+        assertReturns42(program);
+        assertPartialEvalEquals("constant42", program);
+    }
+
+    @Test
+    public void constReturnProgram() {
+        byte[] bytecodes = new byte[]{
+        /* 0: */Bytecode.CONST,
+        /* 1: */42,
+        /* 2: */Bytecode.RETURN};
+        assertPartialEvalEqualsAndRunsCorrect(new Program("constReturnProgram", bytecodes, 0, 2));
+    }
+
+    @Test
+    public void constAddProgram() {
+        byte[] bytecodes = new byte[]{
+        /* 0: */Bytecode.CONST,
+        /* 1: */40,
+        /* 2: */Bytecode.CONST,
+        /* 3: */2,
+        /* 4: */Bytecode.ADD,
+        /* 5: */Bytecode.RETURN};
+        assertPartialEvalEqualsAndRunsCorrect(new Program("constAddProgram", bytecodes, 0, 2));
+    }
+
+    @Test
+    public void simpleIfProgram() {
+        byte[] bytecodes = new byte[]{
+        /* 0: */Bytecode.CONST,
+        /* 1: */40,
+        /* 2: */Bytecode.CONST,
+        /* 3: */1,
+        /* 4: */Bytecode.IFZERO,
+        /* 5: */8,
+        /* 6: */Bytecode.CONST,
+        /* 7: */42,
+        /* 8: */Bytecode.RETURN};
+        assertPartialEvalEqualsAndRunsCorrect(new Program("simpleIfProgram", bytecodes, 0, 3));
+    }
+
+    @Test
+    public void ifAndPopProgram() {
+        byte[] bytecodes = new byte[]{
+        /* 0: */Bytecode.CONST,
+        /* 1: */40,
+        /* 2: */Bytecode.CONST,
+        /* 3: */1,
+        /* 4: */Bytecode.IFZERO,
+        /* 5: */9,
+        /* 6: */Bytecode.POP,
+        /* 7: */Bytecode.CONST,
+        /* 8: */42,
+        /* 9: */Bytecode.RETURN};
+        assertPartialEvalEqualsAndRunsCorrect(new Program("ifAndPopProgram", bytecodes, 0, 3));
+    }
+
+    @Test
+    @Ignore
+    public void simpleLoopProgram() {
+        byte[] bytecodes = new byte[]{
+        /* 0: */Bytecode.CONST,
+        /* 1: */42,
+        /* 2: */Bytecode.CONST,
+        /* 3: */-12,
+        /* 4: */Bytecode.CONST,
+        /* 5: */1,
+        /* 6: */Bytecode.ADD,
+        /* 7: */Bytecode.DUP,
+        /* 8: */Bytecode.IFZERO,
+        /* 9: */12,
+        /* 10: */Bytecode.JMP,
+        /* 11: */4,
+        /* 12: */Bytecode.POP,
+        /* 13: */Bytecode.RETURN};
+        assertPartialEvalEqualsAndRunsCorrect(new Program("ifAndPopProgram", bytecodes, 0, 3));
+    }
+
+    @Test(timeout = 1000)
+    public void manyIfsProgram() {
+        byte[] bytecodes = new byte[]{
+        /* 0: */Bytecode.CONST,
+        /* 1: */40,
+        /* 2: */Bytecode.CONST,
+        /* 3: */1,
+        /* 4: */Bytecode.IFZERO,
+        /* 5: */8,
+        /* 6: */Bytecode.CONST,
+        /* 7: */1,
+        /* 8: */Bytecode.IFZERO,
+        /* 9: */12,
+        /* 10: */Bytecode.CONST,
+        /* 11: */1,
+        /* 12: */Bytecode.IFZERO,
+        /* 13: */16,
+        /* 14: */Bytecode.CONST,
+        /* 15: */1,
+        /* 16: */Bytecode.IFZERO,
+        /* 17: */20,
+        /* 18: */Bytecode.CONST,
+        /* 19: */1,
+        /* 20: */Bytecode.IFZERO,
+        /* 21: */24,
+        /* 22: */Bytecode.CONST,
+        /* 23: */1,
+        /* 24: */Bytecode.IFZERO,
+        /* 25: */28,
+        /* 26: */Bytecode.CONST,
+        /* 27: */1,
+        /* 28: */Bytecode.IFZERO,
+        /* 29: */32,
+        /* 30: */Bytecode.CONST,
+        /* 31: */1,
+        /* 32: */Bytecode.IFZERO,
+        /* 33: */36,
+        /* 34: */Bytecode.CONST,
+        /* 35: */1,
+        /* 36: */Bytecode.IFZERO,
+        /* 37: */40,
+        /* 38: */Bytecode.CONST,
+        /* 39: */1,
+        /* 40: */Bytecode.IFZERO,
+        /* 41: */44,
+        /* 42: */Bytecode.CONST,
+        /* 43: */42,
+        /* 44: */Bytecode.RETURN};
+        assertPartialEvalEqualsAndRunsCorrect(new Program("manyIfsProgram", bytecodes, 0, 3));
+    }
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Fri Feb 27 16:36:38 2015 +0100
@@ -238,6 +238,14 @@
             return method.getAnnotation(ExplodeLoop.class) != null;
         }
 
+        public boolean shouldMergeExplosions(ResolvedJavaMethod method) {
+            ExplodeLoop explodeLoop = method.getAnnotation(ExplodeLoop.class);
+            if (explodeLoop != null) {
+                return explodeLoop.merge();
+            }
+            return false;
+        }
+
     }
 
     @SuppressWarnings("unused")
@@ -351,6 +359,13 @@
                 }
             }
         }
+
+        if (!TruffleCompilerOptions.TruffleInlineAcrossTruffleBoundary.getValue()) {
+            // Do not inline across Truffle boundaries.
+            for (MethodCallTargetNode mct : graph.getNodes(MethodCallTargetNode.TYPE)) {
+                mct.invoke().setUseForInlining(false);
+            }
+        }
     }
 
     private void injectConstantCallTarget(final StructuredGraph graph, final OptimizedCallTarget constantCallTarget, PhaseContext baseContext) {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Fri Feb 27 16:36:38 2015 +0100
@@ -43,7 +43,6 @@
 import com.oracle.graal.lir.phases.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
-import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.tiers.*;
@@ -140,13 +139,6 @@
                 return;
             }
 
-            if (!TruffleCompilerOptions.TruffleInlineAcrossTruffleBoundary.getValue()) {
-                // Do not inline across Truffle boundaries.
-                for (MethodCallTargetNode mct : graph.getNodes(MethodCallTargetNode.TYPE)) {
-                    mct.invoke().setUseForInlining(false);
-                }
-            }
-
             compilationNotify.notifyCompilationTruffleTierFinished(compilable, graph);
             CompilationResult compilationResult = compileMethodHelper(graph, compilable.toString(), graphBuilderSuite, compilable.getSpeculationLog(), compilable);
             compilationNotify.notifyCompilationSuccess(compilable, graph, compilationResult);
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java	Fri Feb 27 16:36:38 2015 +0100
@@ -186,6 +186,22 @@
                 return true;
             }
         });
+
+        r = new Registration(plugins, metaAccess, CompilerAsserts.class);
+        r.register1("partialEvaluationConstant", Object.class, new InvocationPlugin() {
+            public boolean apply(GraphBuilderContext builder, ValueNode value) {
+                ValueNode curValue = value;
+                if (curValue instanceof BoxNode) {
+                    BoxNode boxNode = (BoxNode) curValue;
+                    curValue = boxNode.getValue();
+                }
+                if (curValue.isConstant()) {
+                    return true;
+                } else {
+                    throw builder.bailout("Partial evaluation did not reduce value to a constant, is a regular compiler node: " + curValue);
+                }
+            }
+        });
     }
 
     public static void registerOptimizedCallTargetPlugins(MetaAccessProvider metaAccess, InvocationPlugins plugins) {
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/CompilerAsserts.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/CompilerAsserts.java	Fri Feb 27 16:36:38 2015 +0100
@@ -64,4 +64,13 @@
             neverPartOfCompilation("Value is not compilation constant");
         }
     }
+
+    /**
+     * Assertion that the corresponding value is reduced to a constant during the initial partial
+     * evaluation phase.
+     *
+     * @param value the value that must be constant during compilation
+     */
+    public static <T> void partialEvaluationConstant(Object value) {
+    }
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/ExplodeLoop.java	Fri Feb 27 14:57:27 2015 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/ExplodeLoop.java	Fri Feb 27 16:36:38 2015 +0100
@@ -33,4 +33,5 @@
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
 public @interface ExplodeLoop {
+    boolean merge() default false;
 }