changeset 13692:c215dec9d3cf

allow multiple ReturnNodes per graph
author Lukas Stadler <lukas.stadler@jku.at>
date Wed, 15 Jan 2014 16:11:56 +0100
parents 056d9d7dc061
children 4efb3399a36e
files graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FloatingReadTest.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StraighteningTest.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EAMergingTest.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/UnsafeEATest.java graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java
diffstat 11 files changed, 191 insertions(+), 165 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java	Wed Jan 15 16:11:56 2014 +0100
@@ -46,6 +46,11 @@
         return ret.result();
     }
 
+    private static IfNode getIfNode(StructuredGraph graph) {
+        assertTrue(graph.start().next() instanceof IfNode);
+        return (IfNode) graph.start().next();
+    }
+
     @Test
     public void testCanonicalComparison() {
         StructuredGraph referenceGraph = parse("referenceCanonicalComparison");
@@ -98,11 +103,9 @@
         for (int i = 1; i <= 4; i++) {
             StructuredGraph graph = getCanonicalizedGraph("integerTest" + i);
 
-            ValueNode result = getResult(graph);
-            assertTrue(result instanceof ConditionalNode);
-            ConditionalNode mat = (ConditionalNode) result;
-            assertTrue(mat.condition() instanceof IntegerTestNode);
-            IntegerTestNode test = (IntegerTestNode) mat.condition();
+            IfNode ifNode = getIfNode(graph);
+            assertTrue(ifNode.condition() instanceof IntegerTestNode);
+            IntegerTestNode test = (IntegerTestNode) ifNode.condition();
             ParameterNode param0 = graph.getParameter(0);
             ParameterNode param1 = graph.getParameter(1);
             assertTrue((test.x() == param0 && test.y() == param1) || (test.x() == param1 && test.y() == param0));
@@ -133,8 +136,8 @@
         assertTrue(result.isConstant() && result.asConstant().asLong() == 1);
         result = getResult(getCanonicalizedGraph("integerTestCanonicalization2"));
         assertTrue(result.isConstant() && result.asConstant().asLong() == 1);
-        result = getResult(getCanonicalizedGraph("integerTestCanonicalization3"));
-        assertTrue(result instanceof ConditionalNode);
+        StructuredGraph graph = getCanonicalizedGraph("integerTestCanonicalization3");
+        assertEquals(2, graph.getNodes(ReturnNode.class).count());
     }
 
     public static int integerTestCanonicalization1(boolean b) {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FloatingReadTest.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FloatingReadTest.java	Wed Jan 15 16:11:56 2014 +0100
@@ -68,6 +68,7 @@
 
             for (Node n : graph.getNodes()) {
                 if (n instanceof ReturnNode) {
+                    assert returnNode == null;
                     returnNode = (ReturnNode) n;
                 } else if (n instanceof MonitorExit) {
                     monitorexit = (MonitorExit) n;
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java	Wed Jan 15 16:11:56 2014 +0100
@@ -115,7 +115,7 @@
         for (TestMode mode : TestMode.values()) {
             SchedulePhase schedule = getFinalSchedule("testSplit1Snippet", mode);
             assertReadWithinStartBlock(schedule, true);
-            assertReadWithinReturnBlock(schedule, false);
+            assertReadWithinAllReturnBlocks(schedule, false);
         }
     }
 
@@ -138,7 +138,7 @@
     public void testSplit2() {
         SchedulePhase schedule = getFinalSchedule("testSplit2Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertReadWithinStartBlock(schedule, false);
-        assertReadWithinReturnBlock(schedule, true);
+        assertReadWithinAllReturnBlocks(schedule, true);
     }
 
     /**
@@ -163,7 +163,7 @@
         SchedulePhase schedule = getFinalSchedule("testLoop1Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(6, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, true);
-        assertReadWithinReturnBlock(schedule, false);
+        assertReadWithinAllReturnBlocks(schedule, false);
     }
 
     /**
@@ -188,7 +188,7 @@
         SchedulePhase schedule = getFinalSchedule("testLoop2Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(6, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
-        assertReadWithinReturnBlock(schedule, true);
+        assertReadWithinAllReturnBlocks(schedule, true);
     }
 
     /**
@@ -210,7 +210,7 @@
         SchedulePhase schedule = getFinalSchedule("testLoop3Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(7, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, true);
-        assertReadWithinReturnBlock(schedule, false);
+        assertReadWithinAllReturnBlocks(schedule, false);
     }
 
     public String testStringReplaceSnippet(String input) {
@@ -246,7 +246,7 @@
         SchedulePhase schedule = getFinalSchedule("testLoop5Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(7, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
-        assertReadWithinReturnBlock(schedule, false);
+        assertReadWithinAllReturnBlocks(schedule, false);
     }
 
     /**
@@ -261,10 +261,11 @@
     public void testArrayCopy() {
         SchedulePhase schedule = getFinalSchedule("testArrayCopySnippet", TestMode.INLINED_WITHOUT_FRAMESTATES);
         StructuredGraph graph = schedule.getCFG().getStartBlock().getBeginNode().graph();
-        ReturnNode ret = graph.getNodes().filter(ReturnNode.class).first();
+        assertEquals(1, graph.getNodes(ReturnNode.class).count());
+        ReturnNode ret = graph.getNodes(ReturnNode.class).first();
         assertTrue(ret.result() instanceof FloatingReadNode);
         assertEquals(schedule.getCFG().blockFor(ret), schedule.getCFG().blockFor(ret.result()));
-        assertReadWithinReturnBlock(schedule, true);
+        assertReadWithinAllReturnBlocks(schedule, true);
     }
 
     /**
@@ -305,7 +306,7 @@
         assertEquals(4, schedule.getCFG().getBlocks().length);
         assertEquals(1, schedule.getCFG().graph.getNodes().filter(FloatingReadNode.class).count());
         assertReadWithinStartBlock(schedule, false);
-        assertReadWithinReturnBlock(schedule, false);
+        assertReadWithinAllReturnBlocks(schedule, false);
         assertReadAndWriteInSameBlock(schedule, false);
     }
 
@@ -326,7 +327,7 @@
         SchedulePhase schedule = getFinalSchedule("testIfRead3Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(4, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
-        assertReadWithinReturnBlock(schedule, true);
+        assertReadWithinAllReturnBlocks(schedule, true);
     }
 
     /**
@@ -345,9 +346,9 @@
     @Test
     public void testIfRead4() {
         SchedulePhase schedule = getFinalSchedule("testIfRead4Snippet", TestMode.WITHOUT_FRAMESTATES);
-        assertEquals(4, schedule.getCFG().getBlocks().length);
+        assertEquals(3, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
-        assertReadWithinReturnBlock(schedule, false);
+        assertReadWithinAllReturnBlocks(schedule, false);
         assertReadAndWriteInSameBlock(schedule, true);
     }
 
@@ -366,7 +367,7 @@
         SchedulePhase schedule = getFinalSchedule("testIfRead5Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(4, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
-        assertReadWithinReturnBlock(schedule, true);
+        assertReadWithinAllReturnBlocks(schedule, true);
         assertReadAndWriteInSameBlock(schedule, false);
     }
 
@@ -432,7 +433,7 @@
     public void testBlockSchedule2() {
         SchedulePhase schedule = getFinalSchedule("testBlockSchedule2Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL, SchedulingStrategy.LATEST);
         assertReadWithinStartBlock(schedule, false);
-        assertReadWithinReturnBlock(schedule, false);
+        assertReadWithinAllReturnBlocks(schedule, false);
         assertReadAndWriteInSameBlock(schedule, false);
     }
 
@@ -454,7 +455,7 @@
          * read of container.b for increment operation should be in return block. TODO: not sure
          * though, could be replaced by read of container.b of the loop header...
          */
-        assertReadWithinReturnBlock(schedule, true);
+        assertReadWithinAllReturnBlocks(schedule, true);
     }
 
     public static void testProxy2Snippet() {
@@ -476,7 +477,7 @@
     public void testProxy2() {
         SchedulePhase schedule = getFinalSchedule("testProxy2Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertReadWithinStartBlock(schedule, false);
-        assertReadWithinReturnBlock(schedule, false);
+        assertReadWithinAllReturnBlocks(schedule, false);
     }
 
     private int hash = 0;
@@ -499,7 +500,7 @@
     public void testStringHashCode() {
         SchedulePhase schedule = getFinalSchedule("testStringHashCodeSnippet", TestMode.WITHOUT_FRAMESTATES);
         assertReadWithinStartBlock(schedule, true);
-        assertReadWithinReturnBlock(schedule, false);
+        assertReadWithinAllReturnBlocks(schedule, false);
 
         hash = 0x1337;
         value[0] = 'a';
@@ -531,30 +532,26 @@
     public void testLoop4() {
         SchedulePhase schedule = getFinalSchedule("testLoop4Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertReadWithinStartBlock(schedule, false);
-        assertReadWithinReturnBlock(schedule, false);
+        assertReadWithinAllReturnBlocks(schedule, false);
     }
 
-    private void assertReadWithinReturnBlock(SchedulePhase schedule, boolean withinReturnBlock) {
+    private void assertReadWithinAllReturnBlocks(SchedulePhase schedule, boolean withinReturnBlock) {
         StructuredGraph graph = schedule.getCFG().graph;
-        assertEquals(graph.getNodes().filter(ReturnNode.class).count(), 1);
+        assertTrue(graph.getNodes(ReturnNode.class).isNotEmpty());
 
-        Block end = null;
-        outer: for (Block b : schedule.getCFG().getBlocks()) {
-            for (Node n : b.getNodes()) {
-                if (n instanceof ReturnNode) {
-                    end = b;
-                    break outer;
+        int withRead = 0;
+        int returnBlocks = 0;
+        for (ReturnNode returnNode : graph.getNodes(ReturnNode.class)) {
+            Block block = schedule.getCFG().getNodeToBlock().get(returnNode);
+            for (Node node : schedule.getBlockToNodesMap().get(block)) {
+                if (node instanceof FloatingReadNode) {
+                    withRead++;
+                    break;
                 }
             }
+            returnBlocks++;
         }
-        assertNotNull("no block with ReturnNode found", end);
-        boolean readEncountered = false;
-        for (Node node : schedule.getBlockToNodesMap().get(end)) {
-            if (node instanceof FloatingReadNode) {
-                readEncountered = true;
-            }
-        }
-        assertEquals(readEncountered, withinReturnBlock);
+        assertEquals(withRead == returnBlocks, withinReturnBlock);
     }
 
     private void assertReadWithinStartBlock(SchedulePhase schedule, boolean withinStartBlock) {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StraighteningTest.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/StraighteningTest.java	Wed Jan 15 16:11:56 2014 +0100
@@ -76,7 +76,6 @@
         test("test1Snippet");
     }
 
-    @Test(expected = AssertionError.class)
     public void test2() {
         test("test2Snippet");
     }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EAMergingTest.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EAMergingTest.java	Wed Jan 15 16:11:56 2014 +0100
@@ -33,8 +33,9 @@
     @Test
     public void testSimpleMerge() {
         testEscapeAnalysis("simpleMergeSnippet", null, false);
-        assertTrue(returnNode.result() instanceof PhiNode);
-        PhiNode phi = (PhiNode) returnNode.result();
+        assertEquals(1, returnNodes.size());
+        assertTrue(returnNodes.get(0).result() instanceof PhiNode);
+        PhiNode phi = (PhiNode) returnNodes.get(0).result();
         assertTrue(phi.valueAt(0) instanceof ParameterNode);
         assertTrue(phi.valueAt(1) instanceof ParameterNode);
     }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java	Wed Jan 15 16:11:56 2014 +0100
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.compiler.test.ea;
 
+import java.util.*;
+
 import org.junit.*;
 
 import com.oracle.graal.api.code.*;
@@ -119,7 +121,7 @@
 
     protected StructuredGraph graph;
     protected HighTierContext context;
-    protected ReturnNode returnNode;
+    protected List<ReturnNode> returnNodes;
 
     /**
      * Runs Escape Analysis on the given snippet and makes sure that no allocations remain in the
@@ -134,8 +136,10 @@
     protected void testEscapeAnalysis(String snippet, final Constant expectedConstantResult, final boolean iterativeEscapeAnalysis) {
         prepareGraph(snippet, iterativeEscapeAnalysis);
         if (expectedConstantResult != null) {
-            Assert.assertTrue(returnNode.result().toString(), returnNode.result().isConstant());
-            Assert.assertEquals(expectedConstantResult, returnNode.result().asConstant());
+            for (ReturnNode returnNode : returnNodes) {
+                Assert.assertTrue(returnNode.result().toString(), returnNode.result().isConstant());
+                Assert.assertEquals(expectedConstantResult, returnNode.result().asConstant());
+            }
         }
         int newInstanceCount = graph.getNodes().filter(NewInstanceNode.class).count() + graph.getNodes().filter(NewArrayNode.class).count() +
                         graph.getNodes().filter(CommitAllocationNode.class).count();
@@ -153,8 +157,7 @@
             new DeadCodeEliminationPhase().apply(graph);
             new CanonicalizerPhase(true).apply(graph, context);
             new PartialEscapePhase(iterativeEscapeAnalysis, false, new CanonicalizerPhase(true)).apply(graph, context);
-            Assert.assertEquals(1, graph.getNodes().filter(ReturnNode.class).count());
-            returnNode = graph.getNodes().filter(ReturnNode.class).first();
+            returnNodes = graph.getNodes(ReturnNode.class).snapshot();
         } catch (Throwable e) {
             throw Debug.handle(e);
         }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java	Wed Jan 15 16:11:56 2014 +0100
@@ -213,8 +213,9 @@
         prepareGraph("testFullyUnrolledLoopSnippet", false);
         new LoopFullUnrollPhase(new CanonicalizerPhase(true)).apply(graph, context);
         new PartialEscapePhase(false, new CanonicalizerPhase(true)).apply(graph, context);
-        Assert.assertTrue(returnNode.result() instanceof AllocatedObjectNode);
-        CommitAllocationNode commit = ((AllocatedObjectNode) returnNode.result()).getCommit();
+        Assert.assertEquals(1, returnNodes.size());
+        Assert.assertTrue(returnNodes.get(0).result() instanceof AllocatedObjectNode);
+        CommitAllocationNode commit = ((AllocatedObjectNode) returnNodes.get(0).result()).getCommit();
         Assert.assertEquals(2, commit.getValues().size());
         Assert.assertEquals(1, commit.getVirtualObjects().size());
         Assert.assertTrue("non-cyclic data structure expected", commit.getVirtualObjects().get(0) != commit.getValues().get(0));
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/UnsafeEATest.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/UnsafeEATest.java	Wed Jan 15 16:11:56 2014 +0100
@@ -86,8 +86,9 @@
     @Test
     public void testMergedDouble() {
         testEscapeAnalysis("testMergedDoubleSnippet", null, false);
-        Assert.assertTrue(returnNode.result() instanceof PhiNode);
-        PhiNode phi = (PhiNode) returnNode.result();
+        Assert.assertEquals(1, returnNodes.size());
+        Assert.assertTrue(returnNodes.get(0).result() instanceof PhiNode);
+        PhiNode phi = (PhiNode) returnNodes.get(0).result();
         Assert.assertTrue(phi.valueAt(0) instanceof LoadFieldNode);
         Assert.assertTrue(phi.valueAt(1) instanceof LoadFieldNode);
     }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Wed Jan 15 16:11:56 2014 +0100
@@ -120,7 +120,6 @@
 
         private ValueNode methodSynchronizedObject;
         private ExceptionDispatchBlock unwindBlock;
-        private Block returnBlock;
 
         private FixedWithNextNode lastInstr;                 // the last instruction added
 
@@ -198,7 +197,6 @@
             this.stream = new BytecodeStream(method.getCode());
             this.constantPool = method.getConstantPool();
             unwindBlock = null;
-            returnBlock = null;
             methodSynchronizedObject = null;
             this.currentGraph = graph;
             this.frameState = new FrameStateBuilder(method, graph, graphBuilderConfig.eagerResolving());
@@ -270,7 +268,6 @@
             for (Block block : blockMap.blocks) {
                 processBlock(block);
             }
-            processBlock(returnBlock);
             processBlock(unwindBlock);
 
             Debug.dump(currentGraph, "After bytecode parsing");
@@ -305,16 +302,6 @@
             return unwindBlock;
         }
 
-        private Block returnBlock(int bci) {
-            if (returnBlock == null) {
-                returnBlock = new Block();
-                returnBlock.startBci = bci;
-                returnBlock.endBci = bci;
-                returnBlock.blockID = Integer.MAX_VALUE;
-            }
-            return returnBlock;
-        }
-
         public BytecodeStream stream() {
             return stream;
         }
@@ -1264,11 +1251,18 @@
         }
 
         private void genReturn(ValueNode x) {
+            frameState.setRethrowException(false);
             frameState.clearStack();
-            if (x != null) {
-                frameState.push(x.kind(), x);
+            if (graphBuilderConfig.eagerInfopointMode()) {
+                append(new InfopointNode(InfopointReason.METHOD_END, frameState.create(bci())));
             }
-            appendGoto(createTarget(returnBlock(bci()), frameState));
+
+            synchronizedEpilogue(FrameState.AFTER_BCI, x);
+            if (frameState.lockDepth() != 0) {
+                throw new BailoutException("unbalanced monitors");
+            }
+
+            append(new ReturnNode(x));
         }
 
         private MonitorEnterNode genMonitorEnter(ValueNode x) {
@@ -1648,10 +1642,7 @@
                 ((MergeNode) lastInstr).setStateAfter(frameState.create(bci));
             }
 
-            if (block == returnBlock) {
-                frameState.setRethrowException(false);
-                createReturn();
-            } else if (block == unwindBlock) {
+            if (block == unwindBlock) {
                 frameState.setRethrowException(false);
                 createUnwind();
             } else if (block instanceof ExceptionDispatchBlock) {
@@ -1692,26 +1683,12 @@
             append(new UnwindNode(exception));
         }
 
-        private void createReturn() {
-            Kind returnKind = method.getSignature().getReturnKind().getStackKind();
-            ValueNode x = returnKind == Kind.Void ? null : frameState.pop(returnKind);
-            assert frameState.stackSize() == 0;
-
-            if (graphBuilderConfig.eagerInfopointMode()) {
-                append(new InfopointNode(InfopointReason.METHOD_END, frameState.create(bci())));
-            }
-
-            synchronizedEpilogue(FrameState.AFTER_BCI, x);
-            if (frameState.lockDepth() != 0) {
-                throw new BailoutException("unbalanced monitors");
-            }
-
-            append(new ReturnNode(x));
-        }
-
         private void synchronizedEpilogue(int bci, ValueNode returnValue) {
             if (Modifier.isSynchronized(method.getModifiers())) {
                 MonitorExitNode monitorExit = genMonitorExit(methodSynchronizedObject, returnValue);
+                if (returnValue != null) {
+                    frameState.push(returnValue.kind(), returnValue);
+                }
                 monitorExit.setStateAfter(frameState.create(bci));
                 assert !frameState.rethrowException();
             }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Wed Jan 15 16:11:56 2014 +0100
@@ -1295,8 +1295,10 @@
      */
     public static Map<Node, Node> inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) {
         final NodeInputList<ValueNode> parameters = invoke.callTarget().arguments();
-        StructuredGraph graph = invoke.asNode().graph();
+        FixedNode invokeNode = invoke.asNode();
+        StructuredGraph graph = invokeNode.graph();
         assert inlineGraph.getGuardsStage().ordinal() >= graph.getGuardsStage().ordinal();
+        Kind returnKind = invokeNode.kind();
 
         FrameState stateAfter = invoke.stateAfter();
         assert stateAfter == null || stateAfter.isAlive();
@@ -1305,7 +1307,7 @@
         }
 
         ArrayList<Node> nodes = new ArrayList<>(inlineGraph.getNodes().count());
-        ReturnNode returnNode = null;
+        ArrayList<ReturnNode> returnNodes = new ArrayList<>(4);
         UnwindNode unwindNode = null;
         final StartNode entryPointNode = inlineGraph.start();
         FixedNode firstCFGNode = entryPointNode.next();
@@ -1318,8 +1320,7 @@
             } else {
                 nodes.add(node);
                 if (node instanceof ReturnNode) {
-                    assert returnNode == null;
-                    returnNode = (ReturnNode) node;
+                    returnNodes.add((ReturnNode) node);
                 } else if (node instanceof UnwindNode) {
                     assert unwindNode == null;
                     unwindNode = (UnwindNode) node;
@@ -1327,7 +1328,7 @@
             }
         }
 
-        final AbstractBeginNode prevBegin = AbstractBeginNode.prevBegin(invoke.asNode());
+        final AbstractBeginNode prevBegin = AbstractBeginNode.prevBegin(invokeNode);
         DuplicationReplacement localReplacement = new DuplicationReplacement() {
 
             public Node replacement(Node node) {
@@ -1340,12 +1341,12 @@
             }
         };
 
-        assert invoke.asNode().successors().first() != null : invoke;
-        assert invoke.asNode().predecessor() != null;
+        assert invokeNode.successors().first() != null : invoke;
+        assert invokeNode.predecessor() != null;
 
         Map<Node, Node> duplicates = graph.addDuplicates(nodes, inlineGraph, inlineGraph.getNodeCount(), localReplacement);
         FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
-        invoke.asNode().replaceAtPredecessor(firstCFGNodeDuplicate);
+        invokeNode.replaceAtPredecessor(firstCFGNodeDuplicate);
 
         FrameState stateAtExceptionEdge = null;
         if (invoke instanceof InvokeWithExceptionNode) {
@@ -1400,7 +1401,8 @@
                 if (frameState != null) {
                     assert frameState.bci != FrameState.BEFORE_BCI : frameState;
                     if (frameState.bci == FrameState.AFTER_BCI) {
-                        frameState.replaceAndDelete(stateAfter);
+                        frameState.replaceAndDelete(returnKind == Kind.Void ? stateAfter : stateAfter.duplicateModified(stateAfter.bci, stateAfter.rethrowException(), returnKind,
+                                        frameState.stackAt(0)));
                     } else if (frameState.bci == FrameState.AFTER_EXCEPTION_BCI) {
                         if (frameState.isAlive()) {
                             assert stateAtExceptionEdge != null;
@@ -1413,7 +1415,7 @@
                         if (frameState.outerFrameState() == null) {
                             assert frameState.bci == FrameState.INVALID_FRAMESTATE_BCI || frameState.method() == inlineGraph.method();
                             if (outerFrameState == null) {
-                                outerFrameState = stateAfter.duplicateModified(invoke.bci(), stateAfter.rethrowException(), invoke.asNode().kind());
+                                outerFrameState = stateAfter.duplicateModified(invoke.bci(), stateAfter.rethrowException(), invokeNode.kind());
                                 outerFrameState.setDuringCall(true);
                             }
                             frameState.setOuterFrameState(outerFrameState);
@@ -1430,27 +1432,55 @@
         } else {
             assert checkContainsOnlyInvalidOrAfterFrameState(duplicates);
         }
-        Node returnValue = null;
-        if (returnNode != null) {
-            if (returnNode.result() instanceof ParameterNode) {
-                returnValue = localReplacement.replacement(returnNode.result());
-            } else if (returnNode.result() != null) {
-                returnValue = duplicates.get(returnNode.result());
+        if (!returnNodes.isEmpty()) {
+            FixedNode n = invoke.next();
+            invoke.setNext(null);
+            if (returnNodes.size() == 1) {
+                ReturnNode returnNode = (ReturnNode) duplicates.get(returnNodes.get(0));
+                Node returnValue = returnNode.result();
+                invokeNode.replaceAtUsages(returnValue);
+                returnNode.clearInputs();
+                returnNode.replaceAndDelete(n);
+            } else {
+                ArrayList<ReturnNode> returnDuplicates = new ArrayList<>(returnNodes.size());
+                for (ReturnNode returnNode : returnNodes) {
+                    returnDuplicates.add((ReturnNode) duplicates.get(returnNode));
+                }
+                MergeNode merge = graph.add(new MergeNode());
+                merge.setStateAfter(stateAfter);
+                ValueNode returnValue = mergeReturns(merge, returnDuplicates);
+                invokeNode.replaceAtUsages(returnValue);
+                merge.setNext(n);
             }
-            invoke.asNode().replaceAtUsages(returnValue);
-            Node returnDuplicate = duplicates.get(returnNode);
-            returnDuplicate.clearInputs();
-            Node n = invoke.next();
-            invoke.setNext(null);
-            returnDuplicate.replaceAndDelete(n);
         }
 
-        invoke.asNode().replaceAtUsages(null);
-        GraphUtil.killCFG(invoke.asNode());
+        invokeNode.replaceAtUsages(null);
+        GraphUtil.killCFG(invokeNode);
 
         return duplicates;
     }
 
+    public static ValueNode mergeReturns(MergeNode merge, List<? extends ReturnNode> returnNodes) {
+        PhiNode returnValuePhi = null;
+
+        for (ReturnNode returnNode : returnNodes) {
+            // create and wire up a new EndNode
+            EndNode endNode = merge.graph().add(new EndNode());
+            merge.addForwardEnd(endNode);
+
+            if (returnNode.result() != null) {
+                if (returnValuePhi == null) {
+                    returnValuePhi = merge.graph().addWithoutUnique(new PhiNode(returnNode.result().kind(), merge));
+                }
+                returnValuePhi.addInput(returnNode.result());
+            }
+            returnNode.clearInputs();
+            returnNode.replaceAndDelete(endNode);
+
+        }
+        return returnValuePhi;
+    }
+
     private static boolean checkContainsOnlyInvalidOrAfterFrameState(Map<Node, Node> duplicates) {
         for (Node node : duplicates.values()) {
             if (node instanceof FrameState) {
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Wed Jan 15 16:08:57 2014 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Wed Jan 15 16:11:56 2014 +0100
@@ -36,9 +36,8 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.Debug.Scope;
 import com.oracle.graal.debug.internal.*;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.Graph.Mark;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.loop.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.GuardsStage;
@@ -623,43 +622,49 @@
         assert checkAllVarargPlaceholdersAreDeleted(parameterCount, placeholders);
 
         new FloatingReadPhase(FloatingReadPhase.ExecutionMode.ANALYSIS_ONLY).apply(snippetCopy);
-        this.memoryMap = null;
 
         this.snippet = snippetCopy;
-        ReturnNode retNode = null;
+        List<ReturnNode> returnNodes = new ArrayList<>(4);
+        List<MemoryMapNode> memMaps = new ArrayList<>(4);
         StartNode entryPointNode = snippet.start();
-        nodes = new ArrayList<>(snippet.getNodeCount());
-        boolean seenReturn = false;
-        boolean containsMemoryMap = false;
-        for (Node node : snippet.getNodes()) {
-            if (node == entryPointNode || node == entryPointNode.stateAfter()) {
-                // Do nothing.
-            } else {
-                nodes.add(node);
-                if (node instanceof ReturnNode) {
-                    retNode = (ReturnNode) node;
-                    NodeIterable<MemoryMapNode> memstates = retNode.inputs().filter(MemoryMapNode.class);
-                    assert memstates.count() == 1;
-                    memoryMap = memstates.first();
-                    retNode.replaceFirstInput(memoryMap, null);
-                    memoryMap.safeDelete();
-
-                    assert !seenReturn : "can handle only one ReturnNode";
-                    seenReturn = true;
-                } else if (node instanceof MemoryMapNode) {
-                    containsMemoryMap = true;
-                }
+        for (ReturnNode retNode : snippet.getNodes(ReturnNode.class)) {
+            MemoryMapNode memMap = retNode.getMemoryMap();
+            memMaps.add(memMap);
+            retNode.setMemoryMap(null);
+            returnNodes.add(retNode);
+            if (memMap.usages().isEmpty()) {
+                memMap.safeDelete();
             }
         }
-        assert !containsMemoryMap;
+        assert snippet.getNodes().filter(MemoryMapNode.class).isEmpty();
+        if (returnNodes.isEmpty()) {
+            this.returnNode = null;
+            this.memoryMap = null;
+        } else if (returnNodes.size() == 1) {
+            this.returnNode = returnNodes.get(0);
+            this.memoryMap = memMaps.get(0);
+        } else {
+            MergeNode merge = snippet.add(new MergeNode());
+            ValueNode returnValue = InliningUtil.mergeReturns(merge, returnNodes);
+            this.returnNode = snippet.add(new ReturnNode(returnValue));
+            this.memoryMap = FloatingReadPhase.mergeMemoryMaps(merge, memMaps);
+            merge.setNext(this.returnNode);
+        }
 
         this.sideEffectNodes = curSideEffectNodes;
         this.deoptNodes = curDeoptNodes;
         this.stampNodes = curStampNodes;
-        this.returnNode = retNode;
+
+        nodes = new ArrayList<>(snippet.getNodeCount());
+        for (Node node : snippet.getNodes()) {
+            if (node != entryPointNode && node != entryPointNode.stateAfter()) {
+                nodes.add(node);
+            }
+        }
 
         Debug.metric(debugValueName("SnippetTemplateNodeCount", args)).add(nodes.size());
         args.info.notifyNewTemplate();
+        Debug.dump(snippet, "SnippetTemplate final state");
     }
 
     private static boolean checkAllVarargPlaceholdersAreDeleted(int parameterCount, VarargsPlaceholderNode[] placeholders) {
@@ -737,7 +742,7 @@
     /**
      * map of killing locations to memory checkpoints (nodes).
      */
-    private MemoryMapNode memoryMap;
+    private final MemoryMapNode memoryMap;
 
     /**
      * Times instantiations of this template.
@@ -1019,25 +1024,18 @@
                 }
             }
 
-            for (ValueNode stampNode : stampNodes) {
-                Node stampDup = duplicates.get(stampNode);
-                ((ValueNode) stampDup).setStamp(((ValueNode) replacee).stamp());
-            }
+            updateStamps(replacee, duplicates);
 
             // Replace all usages of the replacee with the value returned by the snippet
             ValueNode returnValue = null;
             if (returnNode != null && !(replacee instanceof ControlSinkNode)) {
-                if (returnNode.result() instanceof ParameterNode) {
-                    returnValue = (ValueNode) replacements.get(returnNode.result());
-                } else if (returnNode.result() != null) {
-                    returnValue = (ValueNode) duplicates.get(returnNode.result());
-                }
-                Node returnDuplicate = duplicates.get(returnNode);
+                ReturnNode returnDuplicate = (ReturnNode) duplicates.get(returnNode);
+                returnValue = returnDuplicate.result();
                 MemoryMapNode mmap = new DuplicateMapper(duplicates, replaceeGraph.start());
                 if (returnValue == null && replacee.usages().isNotEmpty() && replacee instanceof MemoryCheckpoint) {
                     replacer.replace(replacee, (ValueNode) returnDuplicate.predecessor(), mmap);
                 } else {
-                    assert returnValue != null || replacee.usages().isEmpty() : this + " " + returnValue + " " + returnNode + " " + replacee.usages();
+                    assert returnValue != null || replacee.usages().isEmpty();
                     replacer.replace(replacee, returnValue, mmap);
                 }
                 if (returnDuplicate.isAlive()) {
@@ -1056,6 +1054,30 @@
         }
     }
 
+    private void propagateStamp(Node node) {
+        if (node instanceof PhiNode) {
+            PhiNode phi = (PhiNode) node;
+            if (phi.inferPhiStamp()) {
+                for (Node usage : node.usages()) {
+                    propagateStamp(usage);
+                }
+            }
+        }
+    }
+
+    private void updateStamps(ValueNode replacee, Map<Node, Node> duplicates) {
+        for (ValueNode stampNode : stampNodes) {
+            Node stampDup = duplicates.get(stampNode);
+            ((ValueNode) stampDup).setStamp(replacee.stamp());
+        }
+        for (ParameterNode paramNode : snippet.getNodes(ParameterNode.class)) {
+            for (Node usage : paramNode.usages()) {
+                Node usageDup = duplicates.get(usage);
+                propagateStamp(usageDup);
+            }
+        }
+    }
+
     /**
      * Gets a copy of the specialized graph.
      */
@@ -1102,23 +1124,14 @@
                     ((StateSplit) sideEffectDup).setStateAfter(((StateSplit) replacee).stateAfter());
                 }
             }
-            for (ValueNode stampNode : stampNodes) {
-                Node stampDup = duplicates.get(stampNode);
-                ((ValueNode) stampDup).setStamp(((ValueNode) replacee).stamp());
-            }
+            updateStamps(replacee, duplicates);
 
             // Replace all usages of the replacee with the value returned by the snippet
-            assert returnNode != null : replaceeGraph;
-            ValueNode returnValue = null;
-            if (returnNode.result() instanceof ParameterNode) {
-                returnValue = (ValueNode) replacements.get(returnNode.result());
-            } else {
-                returnValue = (ValueNode) duplicates.get(returnNode.result());
-            }
+            ReturnNode returnDuplicate = (ReturnNode) duplicates.get(returnNode);
+            ValueNode returnValue = returnDuplicate.result();
             assert returnValue != null || replacee.usages().isEmpty();
             replacer.replace(replacee, returnValue, new DuplicateMapper(duplicates, replaceeGraph.start()));
 
-            Node returnDuplicate = duplicates.get(returnNode);
             if (returnDuplicate.isAlive()) {
                 returnDuplicate.clearInputs();
                 returnDuplicate.replaceAndDelete(next);