changeset 18975:88083bb2e0f8

Merge.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Tue, 27 Jan 2015 16:58:48 +0100
parents c597c72e163b (diff) c1f8125b4207 (current diff)
children 3faa4f98d5c8
files
diffstat 41 files changed, 401 insertions(+), 336 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue Jan 27 16:58:48 2015 +0100
@@ -319,7 +319,7 @@
                             canonicalId.set(node, id);
                         }
                         String name = node instanceof ConstantNode && checkConstants ? node.toString(Verbosity.Name) : node.getClass().getSimpleName();
-                        result.append("  " + id + "|" + name + (excludeVirtual ? "\n" : "    (" + node.usages().count() + ")\n"));
+                        result.append("  " + id + "|" + name + (excludeVirtual ? "\n" : "    (" + node.getUsageCount() + ")\n"));
                     }
                 }
             }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraphScheduleTest.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraphScheduleTest.java	Tue Jan 27 16:58:48 2015 +0100
@@ -34,7 +34,7 @@
 public class GraphScheduleTest extends GraalCompilerTest {
 
     protected void assertOrderedAfterSchedule(StructuredGraph graph, Node a, Node b) {
-        SchedulePhase ibp = new SchedulePhase();
+        SchedulePhase ibp = new SchedulePhase(SchedulePhase.SchedulingStrategy.LATEST);
         ibp.apply(graph);
         assertOrderedAfterSchedule(ibp, a, b);
     }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java	Tue Jan 27 16:58:48 2015 +0100
@@ -45,7 +45,6 @@
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.common.inlining.*;
 import com.oracle.graal.phases.schedule.*;
-import com.oracle.graal.phases.schedule.SchedulePhase.MemoryScheduling;
 import com.oracle.graal.phases.schedule.SchedulePhase.SchedulingStrategy;
 import com.oracle.graal.phases.tiers.*;
 
@@ -459,7 +458,7 @@
 
     @Test
     public void testBlockSchedule2() {
-        SchedulePhase schedule = getFinalSchedule("testBlockSchedule2Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL, SchedulingStrategy.LATEST);
+        SchedulePhase schedule = getFinalSchedule("testBlockSchedule2Snippet", TestMode.WITHOUT_FRAMESTATES, SchedulingStrategy.LATEST);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinAllReturnBlocks(schedule, false);
         assertReadAndWriteInSameBlock(schedule, false);
@@ -593,14 +592,10 @@
     }
 
     private SchedulePhase getFinalSchedule(final String snippet, final TestMode mode) {
-        return getFinalSchedule(snippet, mode, MemoryScheduling.OPTIMAL);
+        return getFinalSchedule(snippet, mode, SchedulingStrategy.LATEST_OUT_OF_LOOPS);
     }
 
-    private SchedulePhase getFinalSchedule(final String snippet, final TestMode mode, final MemoryScheduling memsched) {
-        return getFinalSchedule(snippet, mode, memsched, SchedulingStrategy.LATEST_OUT_OF_LOOPS);
-    }
-
-    private SchedulePhase getFinalSchedule(final String snippet, final TestMode mode, final MemoryScheduling memsched, final SchedulingStrategy schedulingStrategy) {
+    private SchedulePhase getFinalSchedule(final String snippet, final TestMode mode, final SchedulingStrategy schedulingStrategy) {
         final StructuredGraph graph = parseEager(snippet);
         try (Scope d = Debug.scope("FloatingReadTest", graph)) {
             try (OverrideScope s = OptionValue.override(OptScheduleOutOfLoops, schedulingStrategy == SchedulingStrategy.LATEST_OUT_OF_LOOPS, OptImplicitNullChecks, false)) {
@@ -633,7 +628,7 @@
                 new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext);
                 new LoweringPhase(canonicalizer, LoweringTool.StandardLoweringStage.LOW_TIER).apply(graph, midContext);
 
-                SchedulePhase schedule = new SchedulePhase(schedulingStrategy, memsched);
+                SchedulePhase schedule = new SchedulePhase(schedulingStrategy);
                 schedule.apply(graph);
                 assertDeepEquals(1, graph.getNodes().filter(StartNode.class).count());
                 return schedule;
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TypeSystemTest.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TypeSystemTest.java	Tue Jan 27 16:58:48 2015 +0100
@@ -217,7 +217,7 @@
     }
 
     private static void outputNode(Node node) {
-        TTY.print("  " + node + "    (usage count: " + node.usages().count() + ") (inputs:");
+        TTY.print("  " + node + "    (usage count: " + node.getUsageCount() + ") (inputs:");
         for (Node input : node.inputs()) {
             TTY.print(" " + input.toString(Verbosity.Id));
         }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Tue Jan 27 16:58:48 2015 +0100
@@ -158,7 +158,7 @@
      */
     public void setMatchResult(ValueNode x, Value operand) {
         assert operand.equals(ComplexMatchValue.INTERIOR_MATCH) || operand instanceof ComplexMatchValue;
-        assert operand instanceof ComplexMatchValue || x.usages().count() == 1 : "interior matches must be single user";
+        assert operand instanceof ComplexMatchValue || x.getUsageCount() == 1 : "interior matches must be single user";
         assert nodeOperands != null && nodeOperands.get(x) == null : "operand cannot be set twice";
         assert !(x instanceof VirtualObjectNode);
         nodeOperands.set(x, operand);
@@ -256,7 +256,7 @@
                 if (LogVerbose.getValue()) {
                     int i = 0;
                     for (ValueNode node : nodes) {
-                        Debug.log("%d: (%s) %1S", i++, node.usages().count(), node);
+                        Debug.log("%d: (%s) %1S", i++, node.getUsageCount(), node);
                     }
                 }
 
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchContext.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchContext.java	Tue Jan 27 16:58:48 2015 +0100
@@ -109,7 +109,7 @@
                     Debug.log("unexpected node %s", node);
                     for (int j = startIndex; j <= endIndex; j++) {
                         ValueNode theNode = nodes.get(j);
-                        Debug.log("%s(%s) %1s", (consumed != null && consumed.contains(theNode) || theNode == root) ? "*" : " ", theNode.usages().count(), theNode);
+                        Debug.log("%s(%s) %1s", (consumed != null && consumed.contains(theNode) || theNode == root) ? "*" : " ", theNode.getUsageCount(), theNode);
                     }
                 }
                 return Result.notSafe(node, rule.getPattern());
@@ -147,7 +147,7 @@
      * @return Result.OK if the node can be safely consumed.
      */
     public Result consume(ValueNode node) {
-        assert node.usages().count() <= 1 : "should have already been checked";
+        assert node.getUsageCount() <= 1 : "should have already been checked";
 
         // Check NOT_IN_BLOCK first since that usually implies ALREADY_USED
         int index = nodes.indexOf(node);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java	Tue Jan 27 16:58:48 2015 +0100
@@ -251,7 +251,7 @@
         }
 
         if (singleUser && !atRoot) {
-            if (node.usages().count() > 1) {
+            if (node.getUsageCount() > 1) {
                 return Result.tooManyUsers(node, statement.getPattern());
             }
         }
--- a/graal/com.oracle.graal.graph.test/src/com/oracle/graal/graph/test/NodeUsagesTests.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.graph.test/src/com/oracle/graal/graph/test/NodeUsagesTests.java	Tue Jan 27 16:58:48 2015 +0100
@@ -62,7 +62,7 @@
         Use use1 = graph.add(new Use(null, def0, null));
         Use use2 = graph.add(new Use(null, null, def0));
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -74,7 +74,7 @@
 
         assertThat(def0.usages(), isEmpty());
 
-        assertEquals(3, def1.usages().count());
+        assertEquals(3, def1.getUsageCount());
         assertThat(def1.usages(), contains(use0));
         assertThat(def1.usages(), contains(use1));
         assertThat(def1.usages(), contains(use2));
@@ -91,7 +91,7 @@
         Use use1 = graph.add(new Use(null, def0, null));
         Use use2 = graph.add(new Use(null, null, def0));
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -103,7 +103,7 @@
 
         assertThat(def0.usages(), isEmpty());
 
-        assertEquals(3, def1.usages().count());
+        assertEquals(3, def1.getUsageCount());
         assertThat(def1.usages(), contains(use0));
         assertThat(def1.usages(), contains(use1));
         assertThat(def1.usages(), contains(use2));
@@ -120,7 +120,7 @@
         Use use1 = graph.add(new Use(null, def0, null));
         Use use2 = graph.add(new Use(null, null, def0));
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -132,7 +132,7 @@
 
         assertThat(def1.usages(), isEmpty());
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -149,7 +149,7 @@
         Use use1 = graph.add(new Use(null, def0, null));
         Use use2 = graph.add(new Use(null, null, def0));
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -159,12 +159,12 @@
 
         def0.replaceAtMatchingUsages(def1, u -> u == use1);
 
-        assertEquals(1, def1.usages().count());
+        assertEquals(1, def1.getUsageCount());
         assertThat(def1.usages(), contains(use1));
 
         assertThat(def1.usages(), isNotEmpty());
 
-        assertEquals(2, def0.usages().count());
+        assertEquals(2, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use2));
 
@@ -180,7 +180,7 @@
         Use use1 = graph.add(new Use(null, def0, null));
         Use use2 = graph.add(new Use(null, null, def0));
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -190,12 +190,12 @@
 
         def0.replaceAtMatchingUsages(def1, u -> u == use2);
 
-        assertEquals(1, def1.usages().count());
+        assertEquals(1, def1.getUsageCount());
         assertThat(def1.usages(), contains(use2));
 
         assertThat(def1.usages(), isNotEmpty());
 
-        assertEquals(2, def0.usages().count());
+        assertEquals(2, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
 
@@ -211,7 +211,7 @@
         Use use1 = graph.add(new Use(null, def0, null));
         Use use2 = graph.add(new Use(null, null, def0));
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -221,12 +221,12 @@
 
         def0.replaceAtMatchingUsages(def1, u -> u == use0);
 
-        assertEquals(1, def1.usages().count());
+        assertEquals(1, def1.getUsageCount());
         assertThat(def1.usages(), contains(use0));
 
         assertThat(def1.usages(), isNotEmpty());
 
-        assertEquals(2, def0.usages().count());
+        assertEquals(2, def0.getUsageCount());
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
 
@@ -242,7 +242,7 @@
         Use use1 = graph.add(new Use(null, def0, null));
         Use use2 = graph.add(new Use(null, null, def0));
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -252,12 +252,12 @@
 
         def0.replaceAtMatchingUsages(def1, u -> u != use1);
 
-        assertEquals(1, def0.usages().count());
+        assertEquals(1, def0.getUsageCount());
         assertThat(def0.usages(), contains(use1));
 
         assertThat(def0.usages(), isNotEmpty());
 
-        assertEquals(2, def1.usages().count());
+        assertEquals(2, def1.getUsageCount());
         assertThat(def1.usages(), contains(use0));
         assertThat(def1.usages(), contains(use2));
 
@@ -274,7 +274,7 @@
         Use use2 = graph.add(new Use(null, null, def0));
         Use use3 = graph.add(new Use(null, null, def0));
 
-        assertEquals(4, def0.usages().count());
+        assertEquals(4, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -285,12 +285,12 @@
 
         def0.replaceAtMatchingUsages(def1, u -> u != use1);
 
-        assertEquals(1, def0.usages().count());
+        assertEquals(1, def0.getUsageCount());
         assertThat(def0.usages(), contains(use1));
 
         assertThat(def0.usages(), isNotEmpty());
 
-        assertEquals(3, def1.usages().count());
+        assertEquals(3, def1.getUsageCount());
         assertThat(def1.usages(), contains(use0));
         assertThat(def1.usages(), contains(use2));
         assertThat(def1.usages(), contains(use3));
@@ -308,7 +308,7 @@
         Use use2 = graph.add(new Use(null, null, def0));
         Use use3 = graph.add(new Use(null, null, def0));
 
-        assertEquals(4, def0.usages().count());
+        assertEquals(4, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -319,12 +319,12 @@
 
         def0.replaceAtMatchingUsages(def1, u -> u != use2);
 
-        assertEquals(1, def0.usages().count());
+        assertEquals(1, def0.getUsageCount());
         assertThat(def0.usages(), contains(use2));
 
         assertThat(def0.usages(), isNotEmpty());
 
-        assertEquals(3, def1.usages().count());
+        assertEquals(3, def1.getUsageCount());
         assertThat(def1.usages(), contains(use0));
         assertThat(def1.usages(), contains(use1));
         assertThat(def1.usages(), contains(use3));
@@ -342,7 +342,7 @@
         Use use2 = graph.add(new Use(null, null, def0));
         Use use3 = graph.add(new Use(null, null, def0));
 
-        assertEquals(4, def0.usages().count());
+        assertEquals(4, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -353,12 +353,12 @@
 
         def0.replaceAtMatchingUsages(def1, u -> u == use2);
 
-        assertEquals(1, def1.usages().count());
+        assertEquals(1, def1.getUsageCount());
         assertThat(def1.usages(), contains(use2));
 
         assertThat(def1.usages(), isNotEmpty());
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use3));
@@ -375,7 +375,7 @@
         Use use1 = graph.add(new Use(null, def0, null));
         Use use2 = graph.add(new Use(null, null, def0));
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -385,12 +385,12 @@
 
         def0.replaceAtMatchingUsages(def1, u -> u != use2);
 
-        assertEquals(1, def0.usages().count());
+        assertEquals(1, def0.getUsageCount());
         assertThat(def0.usages(), contains(use2));
 
         assertThat(def0.usages(), isNotEmpty());
 
-        assertEquals(2, def1.usages().count());
+        assertEquals(2, def1.getUsageCount());
         assertThat(def1.usages(), contains(use0));
         assertThat(def1.usages(), contains(use1));
 
@@ -406,7 +406,7 @@
         Use use1 = graph.add(new Use(null, def0, null));
         Use use2 = graph.add(new Use(null, null, def0));
 
-        assertEquals(3, def0.usages().count());
+        assertEquals(3, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
         assertThat(def0.usages(), contains(use1));
         assertThat(def0.usages(), contains(use2));
@@ -416,12 +416,12 @@
 
         def0.replaceAtMatchingUsages(def1, u -> u != use0);
 
-        assertEquals(1, def0.usages().count());
+        assertEquals(1, def0.getUsageCount());
         assertThat(def0.usages(), contains(use0));
 
         assertThat(def0.usages(), isNotEmpty());
 
-        assertEquals(2, def1.usages().count());
+        assertEquals(2, def1.getUsageCount());
         assertThat(def1.usages(), contains(use1));
         assertThat(def1.usages(), contains(use2));
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java	Tue Jan 27 16:58:48 2015 +0100
@@ -120,31 +120,31 @@
                 instanceofSnippets.lower((InstanceOfDynamicNode) n, tool);
             }
         } else if (n instanceof NewInstanceNode) {
-            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+            if (graph.getGuardsStage().areFrameStatesAtDeopts()) {
                 newObjectSnippets.lower((NewInstanceNode) n, registers, tool);
             }
         } else if (n instanceof DynamicNewInstanceNode) {
-            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+            if (graph.getGuardsStage().areFrameStatesAtDeopts()) {
                 newObjectSnippets.lower((DynamicNewInstanceNode) n, registers, tool);
             }
         } else if (n instanceof NewArrayNode) {
-            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+            if (graph.getGuardsStage().areFrameStatesAtDeopts()) {
                 newObjectSnippets.lower((NewArrayNode) n, registers, runtime, tool);
             }
         } else if (n instanceof DynamicNewArrayNode) {
-            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+            if (graph.getGuardsStage().areFrameStatesAtDeopts()) {
                 newObjectSnippets.lower((DynamicNewArrayNode) n, registers, tool);
             }
         } else if (n instanceof VerifyHeapNode) {
-            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+            if (graph.getGuardsStage().areFrameStatesAtDeopts()) {
                 newObjectSnippets.lower((VerifyHeapNode) n, registers, runtime, tool);
             }
         } else if (n instanceof MonitorEnterNode) {
-            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+            if (graph.getGuardsStage().areFrameStatesAtDeopts()) {
                 monitorSnippets.lower((MonitorEnterNode) n, registers, tool);
             }
         } else if (n instanceof MonitorExitNode) {
-            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+            if (graph.getGuardsStage().areFrameStatesAtDeopts()) {
                 monitorSnippets.lower((MonitorExitNode) n, tool);
             }
         } else if (n instanceof G1PreWriteBarrier) {
@@ -162,7 +162,7 @@
         } else if (n instanceof G1ArrayRangePostWriteBarrier) {
             writeBarrierSnippets.lower((G1ArrayRangePostWriteBarrier) n, registers, tool);
         } else if (n instanceof NewMultiArrayNode) {
-            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+            if (graph.getGuardsStage().areFrameStatesAtDeopts()) {
                 newObjectSnippets.lower((NewMultiArrayNode) n, tool);
             }
         } else if (n instanceof LoadExceptionObjectNode) {
@@ -307,7 +307,7 @@
     @Override
     protected void lowerUnsafeLoadNode(UnsafeLoadNode load, LoweringTool tool) {
         StructuredGraph graph = load.graph();
-        if (load.getGuardingCondition() == null && graph.getGuardsStage().ordinal() > StructuredGraph.GuardsStage.FLOATING_GUARDS.ordinal() && addReadBarrier(load)) {
+        if (load.getGuardingCondition() == null && !graph.getGuardsStage().allowsFloatingGuards() && addReadBarrier(load)) {
             unsafeLoadSnippets.lower(load, tool);
         } else {
             super.lowerUnsafeLoadNode(load, tool);
@@ -367,7 +367,7 @@
 
     private void lowerDynamicCounterNode(DynamicCounterNode n) {
         StructuredGraph graph = n.graph();
-        if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+        if (graph.getGuardsStage().areFrameStatesAtDeopts()) {
             BenchmarkCounters.lower(n, registers, runtime.getConfig(), runtime.getTarget().wordKind);
         }
     }
@@ -391,7 +391,7 @@
 
     private void lowerBytecodeExceptionNode(BytecodeExceptionNode node) {
         StructuredGraph graph = node.graph();
-        if (graph.getGuardsStage() == StructuredGraph.GuardsStage.FLOATING_GUARDS) {
+        if (graph.getGuardsStage().allowsFloatingGuards()) {
             if (OmitHotExceptionStacktrace.getValue()) {
                 Throwable exception;
                 if (node.getExceptionClass() == NullPointerException.class) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/WriteBarrier.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/WriteBarrier.java	Tue Jan 27 16:58:48 2015 +0100
@@ -62,7 +62,7 @@
 
     @Override
     public void lower(LoweringTool tool) {
-        assert graph().getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA;
+        assert graph().getGuardsStage().areFrameStatesAtDeopts();
         tool.getLowerer().lower(this, tool);
     }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopyCallNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopyCallNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -117,7 +117,7 @@
 
     @Override
     public void lower(LoweringTool tool) {
-        if (graph().getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+        if (graph().getGuardsStage().areFrameStatesAtDeopts()) {
             updateAlignedDisjoint();
             ForeignCallDescriptor desc = HotSpotHostForeignCallsProvider.lookupArraycopyDescriptor(elementKind, isAligned(), isDisjoint(), isUninitialized());
             StructuredGraph graph = graph();
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/CheckcastArrayCopyCallNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -99,7 +99,7 @@
 
     @Override
     public void lower(LoweringTool tool) {
-        if (graph().getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+        if (graph().getGuardsStage().areFrameStatesAtDeopts()) {
             ForeignCallDescriptor desc = HotSpotHostForeignCallsProvider.lookupCheckcastArraycopyDescriptor(isUninit());
             StructuredGraph graph = graph();
             ValueNode srcAddr = computeBase(getSource(), getSourcePosition());
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/UnsafeArrayCopyNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/UnsafeArrayCopyNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -95,7 +95,7 @@
 
     @Override
     public void lower(LoweringTool tool) {
-        if (graph().getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+        if (graph().getGuardsStage().areFrameStatesAtDeopts()) {
             UnsafeArrayCopySnippets.Templates templates = tool.getReplacements().getSnippetTemplateCache(UnsafeArrayCopySnippets.Templates.class);
             templates.lower(this, tool);
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractEndNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractEndNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -47,7 +47,7 @@
 
     @Override
     public boolean verify() {
-        assertTrue(usages().count() <= 1, "at most one usage");
+        assertTrue(getUsageCount() <= 1, "at most one usage");
         return super.verify();
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FixedGuardNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FixedGuardNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -76,7 +76,7 @@
          * deoptimizing without invalidating the code. Conditional elimination will eliminate the
          * guard if it's truly redundant in this case.
          */
-        if (graph().getGuardsStage() == StructuredGraph.GuardsStage.FLOATING_GUARDS && getAction() != DeoptimizationAction.None) {
+        if (graph().getGuardsStage().allowsFloatingGuards() && getAction() != DeoptimizationAction.None) {
             ValueNode guard = tool.createGuard(this, condition(), getReason(), getAction(), isNegated()).asNode();
             this.replaceAtUsages(guard);
             ValueAnchorNode newAnchor = graph().add(new ValueAnchorNode(guard.asNode()));
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java	Tue Jan 27 16:58:48 2015 +0100
@@ -458,8 +458,10 @@
 
     @Override
     public void applyToNonVirtual(NodeClosure<? super ValueNode> closure) {
-        for (ValueNode value : values.nonNull()) {
-            closure.apply(this, value);
+        for (ValueNode value : values) {
+            if (value != null) {
+                closure.apply(this, value);
+            }
         }
 
         if (monitorIds != null) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -657,7 +657,7 @@
         }
 
         CompareNode compare = (CompareNode) condition();
-        if (compare.usages().count() != 1) {
+        if (compare.getUsageCount() != 1) {
             return false;
         }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Tue Jan 27 16:58:48 2015 +0100
@@ -65,7 +65,19 @@
          * introduced any more. {@link FrameState} nodes are now associated with
          * {@link DeoptimizingNode} nodes.
          */
-        AFTER_FSA
+        AFTER_FSA;
+
+        public boolean allowsFloatingGuards() {
+            return this == FLOATING_GUARDS;
+        }
+
+        public boolean areFrameStatesAtDeopts() {
+            return this == AFTER_FSA;
+        }
+
+        public boolean areFrameStatesAtSideEffects() {
+            return !this.areFrameStatesAtDeopts();
+        }
     }
 
     public static final int INVOCATION_ENTRY_BCI = -1;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValueNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValueNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -48,12 +48,12 @@
         return stamp;
     }
 
-    public void setStamp(Stamp stamp) {
+    public final void setStamp(Stamp stamp) {
         this.stamp = stamp;
     }
 
     @Override
-    public StructuredGraph graph() {
+    public final StructuredGraph graph() {
         return (StructuredGraph) super.graph();
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/VirtualState.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/VirtualState.java	Tue Jan 27 16:58:48 2015 +0100
@@ -32,9 +32,9 @@
 @NodeInfo(allowedUsageTypes = {InputType.State})
 public abstract class VirtualState extends Node {
 
-    public interface NodeClosure<T extends Node> {
+    public abstract static class NodeClosure<T extends Node> {
 
-        void apply(Node usage, T node);
+        public abstract void apply(Node usage, T node);
     }
 
     public interface VirtualClosure {
@@ -54,4 +54,9 @@
 
     public abstract boolean isPartOfThisState(VirtualState state);
 
+    @Override
+    public final StructuredGraph graph() {
+        return (StructuredGraph) super.graph();
+    }
+
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/WeakCounterNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/WeakCounterNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -45,7 +45,7 @@
 
     @Override
     public void simplify(SimplifierTool tool) {
-        if (checkedValue instanceof FloatingNode && checkedValue.usages().count() == 1) {
+        if (checkedValue instanceof FloatingNode && checkedValue.getUsageCount() == 1) {
             tool.addToWorkList(checkedValue);
             graph().removeFixed(this);
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BranchProbabilityNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BranchProbabilityNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -113,7 +113,7 @@
     }
 
     private boolean isSubstitutionGraph() {
-        return usages().count() == 1 && usages().first() instanceof ReturnNode;
+        return getUsageCount() == 1 && usages().first() instanceof ReturnNode;
     }
 
     /**
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -69,7 +69,7 @@
 
     @Override
     public boolean verify() {
-        assert usages().count() <= 1 : "call target may only be used by a single invoke";
+        assert getUsageCount() <= 1 : "call target may only be used by a single invoke";
         for (Node n : usages()) {
             assertTrue(n instanceof Invoke, "call target can only be used from an invoke (%s)", n);
         }
@@ -171,7 +171,7 @@
         if (singleImplementor != null && !singleImplementor.equals(declaredReceiverType)) {
             ResolvedJavaMethod singleImplementorMethod = singleImplementor.resolveMethod(targetMethod(), invoke().getContextType(), true);
             if (singleImplementorMethod != null) {
-                assert graph().getGuardsStage().ordinal() < StructuredGraph.GuardsStage.FIXED_DEOPTS.ordinal() : "Graph already fixed!";
+                assert graph().getGuardsStage().allowsFloatingGuards() : "Graph already fixed!";
                 /**
                  * We have an invoke on an interface with a single implementor. We can replace this
                  * with an invoke virtual.
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LoweringTool.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LoweringTool.java	Tue Jan 27 16:58:48 2015 +0100
@@ -24,9 +24,7 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.extended.*;
 
 public interface LoweringTool {
@@ -47,8 +45,6 @@
 
     Assumptions assumptions();
 
-    Block getBlockFor(Node node);
-
     /**
      * Gets the closest fixed node preceding the node currently being lowered.
      */
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FrameStateAssignmentPhase.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FrameStateAssignmentPhase.java	Tue Jan 27 16:58:48 2015 +0100
@@ -106,8 +106,8 @@
 
     @Override
     protected void run(StructuredGraph graph) {
-        assert graph.getGuardsStage().ordinal() >= GuardsStage.FIXED_DEOPTS.ordinal() && checkFixedDeopts(graph);
-        if (graph.getGuardsStage().ordinal() < GuardsStage.AFTER_FSA.ordinal()) {
+        assert !graph.getGuardsStage().allowsFloatingGuards() && checkFixedDeopts(graph);
+        if (graph.getGuardsStage().areFrameStatesAtSideEffects()) {
             ReentrantNodeIterator.apply(new FrameStateAssignmentClosure(), graph.start(), null);
             graph.setGuardsStage(GuardsStage.AFTER_FSA);
             graph.getNodes(FrameState.class).filter(state -> state.hasNoUsages()).forEach(GraphUtil::killWithUnusedFloatingInputs);
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GuardLoweringPhase.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GuardLoweringPhase.java	Tue Jan 27 16:58:48 2015 +0100
@@ -193,7 +193,7 @@
 
     @Override
     protected void run(StructuredGraph graph, MidTierContext context) {
-        if (graph.getGuardsStage().ordinal() < GuardsStage.FIXED_DEOPTS.ordinal()) {
+        if (graph.getGuardsStage().allowsFloatingGuards()) {
             SchedulePhase schedule = new SchedulePhase(SchedulingStrategy.EARLIEST);
             schedule.apply(graph);
 
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java	Tue Jan 27 16:58:48 2015 +0100
@@ -77,14 +77,12 @@
         private final NodeBitMap activeGuards;
         private AnchoringNode guardAnchor;
         private FixedWithNextNode lastFixedNode;
-        private ControlFlowGraph cfg;
 
-        public LoweringToolImpl(PhaseContext context, AnchoringNode guardAnchor, NodeBitMap activeGuards, FixedWithNextNode lastFixedNode, ControlFlowGraph cfg) {
+        public LoweringToolImpl(PhaseContext context, AnchoringNode guardAnchor, NodeBitMap activeGuards, FixedWithNextNode lastFixedNode) {
             this.context = context;
             this.guardAnchor = guardAnchor;
             this.activeGuards = activeGuards;
             this.lastFixedNode = lastFixedNode;
-            this.cfg = cfg;
         }
 
         @Override
@@ -141,7 +139,7 @@
                 }
             }
             StructuredGraph graph = before.graph();
-            if (condition.graph().getGuardsStage().ordinal() >= StructuredGraph.GuardsStage.FIXED_DEOPTS.ordinal()) {
+            if (!condition.graph().getGuardsStage().allowsFloatingGuards()) {
                 FixedGuardNode fixedGuard = graph.add(new FixedGuardNode(condition, deoptReason, action, negated));
                 graph.addBeforeFixed(before, fixedGuard);
                 DummyGuardHandle handle = graph.add(new DummyGuardHandle(fixedGuard));
@@ -158,11 +156,6 @@
             }
         }
 
-        @Override
-        public Block getBlockFor(Node node) {
-            return cfg.blockFor(node);
-        }
-
         public FixedWithNextNode lastFixedNode() {
             return lastFixedNode;
         }
@@ -298,7 +291,7 @@
 
         private AnchoringNode process(final Block b, final NodeBitMap activeGuards, final AnchoringNode startAnchor) {
 
-            final LoweringToolImpl loweringTool = new LoweringToolImpl(context, startAnchor, activeGuards, b.getBeginNode(), schedule.getCFG());
+            final LoweringToolImpl loweringTool = new LoweringToolImpl(context, startAnchor, activeGuards, b.getBeginNode());
 
             // Lower the instructions of this block.
             List<ValueNode> nodes = schedule.nodesFor(b);
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/OptimizeGuardAnchorsPhase.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/OptimizeGuardAnchorsPhase.java	Tue Jan 27 16:58:48 2015 +0100
@@ -96,7 +96,7 @@
         int successorCount = controlSplit.successors().count();
         List<GuardNode> otherGuards = new ArrayList<>(successorCount - 1);
         for (GuardNode guard : successor.guards().snapshot()) {
-            if (guard.isDeleted() || guard.condition().usages().count() < successorCount) {
+            if (guard.isDeleted() || guard.condition().getUsageCount() < successorCount) {
                 continue;
             }
             for (GuardNode conditonGuard : guard.condition().usages().filter(GuardNode.class)) {
@@ -129,10 +129,10 @@
     private static BeginNode findMinimumUsagesSuccessor(ControlSplitNode controlSplit) {
         NodePosIterator successors = controlSplit.successors().iterator();
         BeginNode min = (BeginNode) successors.next();
-        int minUsages = min.usages().count();
+        int minUsages = min.getUsageCount();
         while (successors.hasNext()) {
             BeginNode successor = (BeginNode) successors.next();
-            int count = successor.usages().count();
+            int count = successor.getUsageCount();
             if (count < minUsages) {
                 minUsages = count;
                 min = successor;
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java	Tue Jan 27 16:58:48 2015 +0100
@@ -29,7 +29,6 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.StructuredGraph.GuardsStage;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.util.*;
@@ -47,7 +46,7 @@
         if (context.getTarget().implicitNullCheckLimit <= 0) {
             return;
         }
-        assert graph.getGuardsStage().ordinal() >= GuardsStage.AFTER_FSA.ordinal();
+        assert graph.getGuardsStage().areFrameStatesAtDeopts();
 
         for (DeoptimizeNode deopt : graph.getNodes(DeoptimizeNode.class)) {
             tryUseTrappingNullCheck(deopt, deopt.predecessor(), deopt.reason(), deopt.getSpeculation());
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ReentrantBlockIterator.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ReentrantBlockIterator.java	Tue Jan 27 16:58:48 2015 +0100
@@ -23,8 +23,8 @@
 package com.oracle.graal.phases.graph;
 
 import java.util.*;
+import java.util.function.*;
 
-import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.cfg.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
@@ -61,7 +61,7 @@
     }
 
     public static <StateT> LoopInfo<StateT> processLoop(BlockIteratorClosure<StateT> closure, Loop<Block> loop, StateT initialState) {
-        Map<FixedNode, StateT> blockEndStates = apply(closure, loop.getHeader(), initialState, CollectionsFactory.newSet(loop.getBlocks()));
+        Map<FixedNode, StateT> blockEndStates = apply(closure, loop.getHeader(), initialState, block -> !(block.getLoop() == loop || block.isLoopHeader()));
 
         List<Block> predecessors = loop.getHeader().getPredecessors();
         LoopInfo<StateT> info = new LoopInfo<>(predecessors.size() - 1, loop.getExits().size());
@@ -84,7 +84,7 @@
         apply(closure, start, closure.getInitialState(), null);
     }
 
-    public static <StateT> Map<FixedNode, StateT> apply(BlockIteratorClosure<StateT> closure, Block start, StateT initialState, Set<Block> boundary) {
+    public static <StateT> Map<FixedNode, StateT> apply(BlockIteratorClosure<StateT> closure, Block start, StateT initialState, Predicate<Block> stopAtBlock) {
         Deque<Block> blockQueue = new ArrayDeque<>();
         /*
          * States are stored on EndNodes before merges, and on BeginNodes after ControlSplitNodes.
@@ -95,81 +95,50 @@
         Block current = start;
 
         while (true) {
-            if (boundary != null && !boundary.contains(current)) {
+            Block next = null;
+            if (stopAtBlock != null && stopAtBlock.test(current)) {
                 states.put(current.getBeginNode(), state);
             } else {
                 state = closure.processBlock(current, state);
 
-                if (current.getSuccessors().isEmpty()) {
+                List<Block> successors = current.getSuccessors();
+                if (successors.isEmpty()) {
                     // nothing to do...
-                } else if (current.getSuccessors().size() == 1) {
-                    Block successor = current.getSuccessors().get(0);
+                } else if (successors.size() == 1) {
+                    Block successor = successors.get(0);
                     if (successor.isLoopHeader()) {
                         if (current.isLoopEnd()) {
                             // nothing to do... loop ends only lead to loop begins we've already
                             // visited
                             states.put(current.getEndNode(), state);
                         } else {
-                            // recurse into the loop
-                            Loop<Block> loop = successor.getLoop();
-                            LoopBeginNode loopBegin = (LoopBeginNode) loop.getHeader().getBeginNode();
-                            assert successor.getBeginNode() == loopBegin;
-
-                            List<StateT> exitStates = closure.processLoop(loop, state);
-
-                            int i = 0;
-                            assert loop.getExits().size() == exitStates.size();
-                            for (Block exit : loop.getExits()) {
-                                states.put(exit.getBeginNode(), exitStates.get(i++));
-                                blockQueue.addFirst(exit);
-                            }
+                            recurseIntoLoop(closure, blockQueue, states, state, successor);
                         }
                     } else if (current.getEndNode() instanceof AbstractEndNode) {
-                        assert successor.getPredecessors().size() > 1 : "invalid block schedule at " + successor.getBeginNode();
                         AbstractEndNode end = (AbstractEndNode) current.getEndNode();
 
                         // add the end node and see if the merge is ready for processing
                         MergeNode merge = end.merge();
-                        boolean endsVisited = true;
-                        for (AbstractEndNode forwardEnd : merge.forwardEnds()) {
-                            if (forwardEnd != current.getEndNode() && !states.containsKey(forwardEnd)) {
-                                endsVisited = false;
-                                break;
-                            }
-                        }
-                        if (endsVisited) {
-                            ArrayList<StateT> mergedStates = new ArrayList<>(merge.forwardEndCount());
-                            for (Block predecessor : successor.getPredecessors()) {
-                                assert predecessor == current || states.containsKey(predecessor.getEndNode());
-                                StateT endState = predecessor == current ? state : states.remove(predecessor.getEndNode());
-                                mergedStates.add(endState);
-                            }
+                        if (allEndsVisited(states, current, merge)) {
+                            ArrayList<StateT> mergedStates = mergeStates(states, state, current, successor, merge);
                             state = closure.merge(successor, mergedStates);
-                            current = successor;
-                            continue;
+                            next = successor;
                         } else {
                             assert !states.containsKey(end);
                             states.put(end, state);
                         }
                     } else {
-                        assert successor.getPredecessors().size() == 1 : "invalid block schedule at " + successor.getBeginNode();
-                        current = successor;
-                        continue;
+                        next = successor;
                     }
                 } else {
-                    assert current.getSuccessors().size() > 1;
-                    for (int i = 1; i < current.getSuccessors().size(); i++) {
-                        Block successor = current.getSuccessors().get(i);
-                        blockQueue.addFirst(successor);
-                        states.put(successor.getBeginNode(), closure.cloneState(state));
-                    }
-                    current = current.getSuccessors().get(0);
-                    continue;
+                    next = processMultipleSuccessors(closure, blockQueue, states, state, successors);
                 }
             }
 
             // get next queued block
-            if (blockQueue.isEmpty()) {
+            if (next != null) {
+                current = next;
+            } else if (blockQueue.isEmpty()) {
                 return states;
             } else {
                 current = blockQueue.removeFirst();
@@ -179,4 +148,49 @@
             }
         }
     }
+
+    private static <StateT> boolean allEndsVisited(Map<FixedNode, StateT> states, Block current, MergeNode merge) {
+        for (AbstractEndNode forwardEnd : merge.forwardEnds()) {
+            if (forwardEnd != current.getEndNode() && !states.containsKey(forwardEnd)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static <StateT> Block processMultipleSuccessors(BlockIteratorClosure<StateT> closure, Deque<Block> blockQueue, Map<FixedNode, StateT> states, StateT state, List<Block> successors) {
+        assert successors.size() > 1;
+        for (int i = 1; i < successors.size(); i++) {
+            Block successor = successors.get(i);
+            blockQueue.addFirst(successor);
+            states.put(successor.getBeginNode(), closure.cloneState(state));
+        }
+        return successors.get(0);
+    }
+
+    private static <StateT> ArrayList<StateT> mergeStates(Map<FixedNode, StateT> states, StateT state, Block current, Block successor, MergeNode merge) {
+        ArrayList<StateT> mergedStates = new ArrayList<>(merge.forwardEndCount());
+        for (Block predecessor : successor.getPredecessors()) {
+            assert predecessor == current || states.containsKey(predecessor.getEndNode());
+            StateT endState = predecessor == current ? state : states.remove(predecessor.getEndNode());
+            mergedStates.add(endState);
+        }
+        return mergedStates;
+    }
+
+    private static <StateT> void recurseIntoLoop(BlockIteratorClosure<StateT> closure, Deque<Block> blockQueue, Map<FixedNode, StateT> states, StateT state, Block successor) {
+        // recurse into the loop
+        Loop<Block> loop = successor.getLoop();
+        LoopBeginNode loopBegin = (LoopBeginNode) loop.getHeader().getBeginNode();
+        assert successor.getBeginNode() == loopBegin;
+
+        List<StateT> exitStates = closure.processLoop(loop, state);
+
+        int i = 0;
+        assert loop.getExits().size() == exitStates.size();
+        for (Block exit : loop.getExits()) {
+            states.put(exit.getBeginNode(), exitStates.get(i++));
+            blockQueue.addFirst(exit);
+        }
+    }
 }
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Tue Jan 27 16:58:48 2015 +0100
@@ -77,11 +77,6 @@
         LATEST_OUT_OF_LOOPS
     }
 
-    public static enum MemoryScheduling {
-        NONE,
-        OPTIMAL
-    }
-
     private class KillSet implements Iterable<LocationIdentity> {
         private List<LocationIdentity> list;
 
@@ -272,22 +267,15 @@
     private BlockMap<List<ValueNode>> blockToNodesMap;
     private BlockMap<KillSet> blockToKillSet;
     private final SchedulingStrategy selectedStrategy;
-    private final MemoryScheduling memsched;
 
     public SchedulePhase() {
         this(OptScheduleOutOfLoops.getValue() ? SchedulingStrategy.LATEST_OUT_OF_LOOPS : SchedulingStrategy.LATEST);
     }
 
     public SchedulePhase(SchedulingStrategy strategy) {
-        this.memsched = MemoryScheduling.OPTIMAL;
         this.selectedStrategy = strategy;
     }
 
-    public SchedulePhase(SchedulingStrategy strategy, MemoryScheduling memsched) {
-        this.selectedStrategy = strategy;
-        this.memsched = memsched;
-    }
-
     @Override
     protected void run(StructuredGraph graph) {
         assert GraphOrder.assertNonCyclicGraph(graph);
@@ -295,7 +283,7 @@
         earliestCache = graph.createNodeMap();
         blockToNodesMap = new BlockMap<>(cfg);
 
-        if (memsched == MemoryScheduling.OPTIMAL && selectedStrategy != SchedulingStrategy.EARLIEST) {
+        if (selectedStrategy != SchedulingStrategy.EARLIEST) {
             blockToKillSet = new BlockMap<>(cfg);
         }
 
@@ -324,7 +312,7 @@
 
     private void printScheduleHelper(String desc) {
         Formatter buf = new Formatter();
-        buf.format("=== %s / %s / %s (%s) ===%n", getCFG().getStartBlock().getBeginNode().graph(), selectedStrategy, memsched, desc);
+        buf.format("=== %s / %s / %s ===%n", getCFG().getStartBlock().getBeginNode().graph(), selectedStrategy, desc);
         for (Block b : getCFG().getBlocks()) {
             buf.format("==== b: %s (loopDepth: %s). ", b, b.getLoopDepth());
             buf.format("dom: %s. ", b.getDominator());
@@ -438,7 +426,7 @@
                 break;
             case LATEST:
             case LATEST_OUT_OF_LOOPS:
-                boolean scheduleRead = memsched == MemoryScheduling.OPTIMAL && node instanceof FloatingReadNode && !((FloatingReadNode) node).location().getLocationIdentity().isImmutable();
+                boolean scheduleRead = node instanceof FloatingReadNode && !((FloatingReadNode) node).location().getLocationIdentity().isImmutable();
                 if (scheduleRead) {
                     FloatingReadNode read = (FloatingReadNode) node;
                     block = optimalBlock(read, strategy);
@@ -472,7 +460,7 @@
                 throw new GraalInternalError("unknown scheduling strategy");
         }
         if (!dominates(earliestBlock, block)) {
-            throw new SchedulingError("%s: Graph cannot be scheduled : inconsistent for %s, %d usages, (%s needs to dominate %s)", node.graph(), node, node.usages().count(), earliestBlock, block);
+            throw new SchedulingError("%s: Graph cannot be scheduled : inconsistent for %s, %d usages, (%s needs to dominate %s)", node.graph(), node, node.getUsageCount(), earliestBlock, block);
         }
         cfg.getNodeToBlock().set(node, block);
         blockToNodesMap.get(block).add(node);
@@ -506,8 +494,6 @@
      *
      */
     private Block optimalBlock(FloatingReadNode n, SchedulingStrategy strategy) {
-        assert memsched == MemoryScheduling.OPTIMAL;
-
         LocationIdentity locid = n.location().getLocationIdentity();
         assert !locid.isImmutable();
 
@@ -535,9 +521,6 @@
                 // the dominated block is not a successor -> we have a split
                 assert dominatedBlock.getBeginNode() instanceof MergeNode;
 
-                HashSet<Block> region = computeRegion(currentBlock, dominatedBlock);
-                Debug.log("> merge.  %s: region for %s -> %s: %s", n, currentBlock, dominatedBlock, region);
-
                 NewMemoryScheduleClosure closure = null;
                 if (currentBlock == upperBoundBlock) {
                     assert earliestBlock == upperBoundBlock;
@@ -547,7 +530,7 @@
                     closure = new NewMemoryScheduleClosure();
                 }
                 Map<FixedNode, KillSet> states;
-                states = ReentrantBlockIterator.apply(closure, currentBlock, new KillSet(), region);
+                states = ReentrantBlockIterator.apply(closure, currentBlock, new KillSet(), block -> block == dominatedBlock);
 
                 KillSet mergeState = states.get(dominatedBlock.getBeginNode());
                 if (mergeState.isKilled(locid)) {
@@ -587,31 +570,6 @@
     }
 
     /**
-     * compute a set that contains all blocks in a region spanned by dominatorBlock and
-     * dominatedBlock (exclusive the dominatedBlock).
-     */
-    private static HashSet<Block> computeRegion(Block dominatorBlock, Block dominatedBlock) {
-        HashSet<Block> region = new HashSet<>();
-        Queue<Block> workList = new LinkedList<>();
-
-        region.add(dominatorBlock);
-        workList.addAll(dominatorBlock.getSuccessors());
-        while (workList.size() > 0) {
-            Block current = workList.poll();
-            if (current != dominatedBlock) {
-                region.add(current);
-                for (Block b : current.getSuccessors()) {
-                    if (!region.contains(b) && !workList.contains(b)) {
-                        workList.offer(b);
-                    }
-                }
-            }
-        }
-        assert !region.contains(dominatedBlock) && region.containsAll(dominatedBlock.getPredecessors());
-        return region;
-    }
-
-    /**
      * Calculates the last block that the given node could be scheduled in, i.e., the common
      * dominator of all usages. To do so all usages are also assigned to blocks.
      *
@@ -993,16 +951,14 @@
         SortState state = new SortState(b, visited, beforeLastLocation, new ArrayList<>(blockToNodesMap.get(b).size() + 2));
         List<ValueNode> instructions = blockToNodesMap.get(b);
 
-        if (memsched == MemoryScheduling.OPTIMAL) {
-            for (ValueNode i : instructions) {
-                if (i instanceof FloatingReadNode) {
-                    FloatingReadNode frn = (FloatingReadNode) i;
-                    if (!frn.location().getLocationIdentity().isImmutable()) {
-                        state.addRead(frn);
-                        if (nodesFor(b).contains(frn.getLastLocationAccess())) {
-                            assert !state.isBeforeLastLocation(frn);
-                            state.markBeforeLastLocation(frn);
-                        }
+        for (ValueNode i : instructions) {
+            if (i instanceof FloatingReadNode) {
+                FloatingReadNode frn = (FloatingReadNode) i;
+                if (!frn.location().getLocationIdentity().isImmutable()) {
+                    state.addRead(frn);
+                    if (nodesFor(b).contains(frn.getLastLocationAccess())) {
+                        assert !state.isBeforeLastLocation(frn);
+                        state.markBeforeLastLocation(frn);
                     }
                 }
             }
@@ -1094,7 +1050,7 @@
             }
         }
 
-        if (memsched == MemoryScheduling.OPTIMAL && state.readsSize() != 0) {
+        if (state.readsSize() != 0) {
             if (i instanceof MemoryCheckpoint.Single) {
                 LocationIdentity identity = ((MemoryCheckpoint.Single) i).getLocationIdentity();
                 processKillLocation(i, identity, state);
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java	Tue Jan 27 16:58:48 2015 +0100
@@ -34,7 +34,6 @@
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
 import com.oracle.graal.phases.schedule.*;
-import com.oracle.graal.phases.schedule.SchedulePhase.MemoryScheduling;
 import com.oracle.graal.phases.schedule.SchedulePhase.SchedulingStrategy;
 
 public final class GraphOrder {
@@ -135,7 +134,7 @@
      */
     public static boolean assertSchedulableGraph(final StructuredGraph graph) {
         try {
-            final SchedulePhase schedule = new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, MemoryScheduling.NONE);
+            final SchedulePhase schedule = new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS);
             final Map<LoopBeginNode, NodeBitMap> loopEntryStates = Node.newIdentityMap();
             schedule.apply(graph, false);
 
@@ -164,6 +163,7 @@
 
                         if (pendingStateAfter != null && node instanceof FixedNode) {
                             pendingStateAfter.applyToNonVirtual(new NodeClosure<Node>() {
+                                @Override
                                 public void apply(Node usage, Node nonVirtualNode) {
                                     assert currentState.isMarked(nonVirtualNode) || nonVirtualNode instanceof VirtualObjectNode : nonVirtualNode + " not available at virtualstate " + usage +
                                                     " before " + node + " in block " + block + " \n" + list;
@@ -197,8 +197,11 @@
                             for (Node input : node.inputs()) {
                                 if (input != stateAfter) {
                                     if (input instanceof FrameState) {
-                                        ((FrameState) input).applyToNonVirtual((usage, nonVirtual) -> {
-                                            assert currentState.isMarked(nonVirtual) : nonVirtual + " not available at " + node + " in block " + block + "\n" + list;
+                                        ((FrameState) input).applyToNonVirtual(new VirtualState.NodeClosure<Node>() {
+                                            @Override
+                                            public void apply(Node usage, Node nonVirtual) {
+                                                assert currentState.isMarked(nonVirtual) : nonVirtual + " not available at " + node + " in block " + block + "\n" + list;
+                                            }
                                         });
                                     } else {
                                         assert currentState.isMarked(input) || input instanceof VirtualObjectNode : input + " not available at " + node + " in block " + block + "\n" + list;
@@ -221,6 +224,7 @@
                     }
                     if (pendingStateAfter != null) {
                         pendingStateAfter.applyToNonVirtual(new NodeClosure<Node>() {
+                            @Override
                             public void apply(Node usage, Node nonVirtualNode) {
                                 assert currentState.isMarked(nonVirtualNode) || nonVirtualNode instanceof VirtualObjectNode : nonVirtualNode + " not available at virtualstate " + usage +
                                                 " at end of block " + block + " \n" + list;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultJavaLoweringProvider.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultJavaLoweringProvider.java	Tue Jan 27 16:58:48 2015 +0100
@@ -228,7 +228,7 @@
 
     protected void lowerLoadHubNode(LoadHubNode loadHub) {
         StructuredGraph graph = loadHub.graph();
-        if (graph.getGuardsStage().ordinal() < StructuredGraph.GuardsStage.FIXED_DEOPTS.ordinal()) {
+        if (graph.getGuardsStage().allowsFloatingGuards()) {
             return;
         }
         ValueNode hub = createReadHub(graph, loadHub.getValue(), loadHub.getGuard());
@@ -270,7 +270,7 @@
             ReadNode memoryRead = createUnsafeRead(graph, load, valueAnchorNode);
             graph.replaceFixedWithFixed(load, valueAnchorNode);
             graph.addAfterFixed(valueAnchorNode, memoryRead);
-        } else if (graph.getGuardsStage().ordinal() > StructuredGraph.GuardsStage.FLOATING_GUARDS.ordinal()) {
+        } else if (!graph.getGuardsStage().allowsFloatingGuards()) {
             assert load.getKind() != Kind.Illegal;
             ReadNode memoryRead = createUnsafeRead(graph, load, null);
             // An unsafe read must not float outside its block otherwise
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPhase.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/NodeIntrinsificationPhase.java	Tue Jan 27 16:58:48 2015 +0100
@@ -407,7 +407,7 @@
             Debug.log("%s: Checkcast used in a return with forNodeIntrinsic stamp", Debug.contextSnapshot(JavaMethod.class));
         } else if (usage instanceof IsNullNode) {
             if (!usage.hasNoUsages()) {
-                assert usage.usages().count() == 1 && usage.usages().first().predecessor() == input : usage + " " + input;
+                assert usage.getUsageCount() == 1 && usage.usages().first().predecessor() == input : usage + " " + input;
                 graph.replaceFloating((FloatingNode) usage, LogicConstantNode.contradiction(graph));
                 Debug.log("%s: Replaced IsNull with false", Debug.contextSnapshot(JavaMethod.class));
             } else {
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Tue Jan 27 16:58:48 2015 +0100
@@ -675,7 +675,7 @@
 
         GuardsStage guardsStage = args.cacheKey.guardsStage;
         // Perform lowering on the snippet
-        if (guardsStage.ordinal() >= GuardsStage.FIXED_DEOPTS.ordinal()) {
+        if (!guardsStage.allowsFloatingGuards()) {
             new GuardLoweringPhase().apply(snippetCopy, null);
         }
         snippetCopy.setGuardsStage(guardsStage);
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -134,9 +134,9 @@
             new RemoveValueProxyPhase().apply(replacementGraph);
         }
         GuardsStage guardsStage = graph().getGuardsStage();
-        if (guardsStage.ordinal() >= GuardsStage.FIXED_DEOPTS.ordinal()) {
+        if (!guardsStage.allowsFloatingGuards()) {
             new GuardLoweringPhase().apply(replacementGraph, null);
-            if (guardsStage.ordinal() >= GuardsStage.AFTER_FSA.ordinal()) {
+            if (guardsStage.areFrameStatesAtDeopts()) {
                 new FrameStateAssignmentPhase().apply(replacementGraph);
             }
         }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java	Tue Jan 27 16:58:48 2015 +0100
@@ -177,9 +177,7 @@
 
                 assert info.exitStates.size() == loop.getExits().size();
                 loopEntryStates.put((LoopBeginNode) loop.getHeader().getBeginNode(), loopEntryState);
-                for (int i = 0; i < loop.getExits().size(); i++) {
-                    assert info.exitStates.get(i) != null : "no loop exit state at " + loop.getExits().get(i) + " / " + loop.getHeader();
-                }
+                assert assertExitStatesNonEmpty(loop, info);
 
                 return info.exitStates;
             } else {
@@ -192,6 +190,13 @@
         throw new GraalInternalError("too many iterations at %s", loop);
     }
 
+    private boolean assertExitStatesNonEmpty(Loop<Block> loop, LoopInfo<BlockT> info) {
+        for (int i = 0; i < loop.getExits().size(); i++) {
+            assert info.exitStates.get(i) != null : "no loop exit state at " + loop.getExits().get(i) + " / " + loop.getHeader();
+        }
+        return true;
+    }
+
     protected abstract void processLoopExit(LoopExitNode exitNode, BlockT initialState, BlockT exitState, GraphEffectList effects);
 
     protected abstract MergeProcessor createMergeProcessor(Block merge);
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationClosure.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationClosure.java	Tue Jan 27 16:58:48 2015 +0100
@@ -190,7 +190,7 @@
                     }
                 }
                 if (phi) {
-                    PhiNode phiNode = getCachedPhi(entry, value.stamp().unrestricted());
+                    PhiNode phiNode = getPhi(entry, value.stamp().unrestricted());
                     mergeEffects.addFloatingNode(phiNode, "mergeReadCache");
                     for (int i = 0; i < states.size(); i++) {
                         afterMergeEffects.addPhiInput(phiNode, states.get(i).getReadCache(key.object, key.identity, PEReadEliminationClosure.this));
@@ -222,7 +222,7 @@
                 values[i] = value;
             }
 
-            PhiNode phiNode = getCachedPhi(new ReadCacheEntry(identity, phi), values[0].stamp().unrestricted());
+            PhiNode phiNode = getPhi(new ReadCacheEntry(identity, phi), values[0].stamp().unrestricted());
             mergeEffects.addFloatingNode(phiNode, "mergeReadCachePhi");
             for (int i = 0; i < values.length; i++) {
                 afterMergeEffects.addPhiInput(phiNode, values[i]);
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeBlockState.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeBlockState.java	Tue Jan 27 16:58:48 2015 +0100
@@ -102,7 +102,7 @@
                     commit.addLocks(monitorIds);
                 }
 
-                assert commit.usages().filter(AllocatedObjectNode.class).count() == commit.usages().count();
+                assert commit.usages().filter(AllocatedObjectNode.class).count() == commit.getUsageCount();
                 List<AllocatedObjectNode> materializedValues = commit.usages().filter(AllocatedObjectNode.class).snapshot();
                 for (int i = 0; i < commit.getValues().size(); i++) {
                     if (materializedValues.contains(commit.getValues().get(i))) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue Jan 27 16:58:48 2015 +0100
@@ -56,6 +56,38 @@
     private final NodeBitMap usages;
     private final VirtualizerToolImpl tool;
 
+    private final class CollectVirtualObjectsClosure extends NodeClosure<ValueNode> {
+        private final Set<ObjectState> virtual;
+        private final GraphEffectList effects;
+        private final BlockT state;
+
+        private CollectVirtualObjectsClosure(Set<ObjectState> virtual, GraphEffectList effects, BlockT state) {
+            this.virtual = virtual;
+            this.effects = effects;
+            this.state = state;
+        }
+
+        @Override
+        public void apply(Node usage, ValueNode value) {
+            ObjectState valueObj = getObjectState(state, value);
+            if (valueObj != null) {
+                virtual.add(valueObj);
+                effects.replaceFirstInput(usage, value, valueObj.virtual);
+            } else if (value instanceof VirtualObjectNode) {
+                ObjectState virtualObj = null;
+                for (ObjectState obj : state.getStates()) {
+                    if (value == obj.virtual) {
+                        virtualObj = obj;
+                        break;
+                    }
+                }
+                if (virtualObj != null) {
+                    virtual.add(virtualObj);
+                }
+            }
+        }
+    }
+
     /**
      * Final subclass of PartialEscapeClosure, for performance and to make everything behave nicely
      * with generics.
@@ -108,11 +140,13 @@
             return !(node instanceof CommitAllocationNode || node instanceof AllocatedObjectNode || node instanceof BoxNode);
         }
         if (isMarked) {
-            for (ValueNode input : node.inputs().filter(ValueNode.class)) {
-                ObjectState obj = getObjectState(state, input);
-                if (obj != null) {
-                    VirtualUtil.trace("replacing input %s at %s: %s", input, node, obj);
-                    replaceWithMaterialized(input, node, insertBefore, state, obj, effects, METRIC_MATERIALIZATIONS_UNHANDLED);
+            for (Node input : node.inputs()) {
+                if (input instanceof ValueNode) {
+                    ObjectState obj = getObjectState(state, (ValueNode) input);
+                    if (obj != null) {
+                        VirtualUtil.trace("replacing input %s at %s: %s", input, node, obj);
+                        replaceWithMaterialized(input, node, insertBefore, state, obj, effects, METRIC_MATERIALIZATIONS_UNHANDLED);
+                    }
                 }
             }
             if (node instanceof NodeWithState) {
@@ -124,75 +158,76 @@
 
     private void processNodeWithState(NodeWithState nodeWithState, final BlockT state, final GraphEffectList effects) {
         for (FrameState fs : nodeWithState.states()) {
-            FrameState frameState = fs;
-            if (frameState.usages().count() > 1) {
-                FrameState copy = (FrameState) frameState.copyWithInputs();
-                nodeWithState.asNode().replaceFirstInput(frameState, copy);
-                frameState = copy;
-            }
-            final Set<ObjectState> virtual = new ArraySet<>();
-            frameState.applyToNonVirtual(new NodeClosure<ValueNode>() {
+            FrameState frameState = getUniqueFramestate(nodeWithState, fs);
+            Set<ObjectState> virtual = new ArraySet<>();
+            frameState.applyToNonVirtual(new CollectVirtualObjectsClosure(virtual, effects, state));
+            collectLockedVirtualObjects(state, virtual);
+            collectReferencedVirtualObjects(state, virtual);
+            addVirtualMappings(state, effects, frameState, virtual);
+        }
+    }
+
+    private static FrameState getUniqueFramestate(NodeWithState nodeWithState, FrameState frameState) {
+        if (frameState.getUsageCount() > 1) {
+            // Can happen for example from inlined snippets with multiple state split nodes.
+            FrameState copy = (FrameState) frameState.copyWithInputs();
+            nodeWithState.asNode().replaceFirstInput(frameState, copy);
+            return copy;
+        }
+        return frameState;
+    }
 
-                @Override
-                public void apply(Node usage, ValueNode value) {
-                    ObjectState valueObj = getObjectState(state, value);
-                    if (valueObj != null) {
-                        virtual.add(valueObj);
-                        effects.replaceFirstInput(usage, value, valueObj.virtual);
-                    } else if (value instanceof VirtualObjectNode) {
-                        ObjectState virtualObj = null;
-                        for (ObjectState obj : state.getStates()) {
-                            if (value == obj.virtual) {
-                                virtualObj = obj;
-                                break;
-                            }
-                        }
-                        if (virtualObj != null) {
-                            virtual.add(virtualObj);
-                        }
-                    }
-                }
-            });
-            for (ObjectState obj : state.getStates()) {
-                if (obj.isVirtual() && obj.hasLocks()) {
-                    virtual.add(obj);
-                }
+    private void addVirtualMappings(final BlockT state, final GraphEffectList effects, FrameState frameState, Set<ObjectState> virtual) {
+        for (ObjectState obj : virtual) {
+            EscapeObjectState v;
+            if (obj.isVirtual()) {
+                v = createVirtualObjectState(state, obj);
+            } else {
+                v = new MaterializedObjectState(obj.virtual, obj.getMaterializedValue());
             }
+            effects.addVirtualMapping(frameState, v);
+        }
+    }
 
-            ArrayDeque<ObjectState> queue = new ArrayDeque<>(virtual);
-            while (!queue.isEmpty()) {
-                ObjectState obj = queue.removeLast();
-                if (obj.isVirtual()) {
-                    for (ValueNode field : obj.getEntries()) {
-                        if (field instanceof VirtualObjectNode) {
-                            ObjectState fieldObj = state.getObjectState((VirtualObjectNode) field);
-                            if (fieldObj.isVirtual() && !virtual.contains(fieldObj)) {
-                                virtual.add(fieldObj);
-                                queue.addLast(fieldObj);
-                            }
+    private void collectReferencedVirtualObjects(final BlockT state, final Set<ObjectState> virtual) {
+        ArrayDeque<ObjectState> queue = new ArrayDeque<>(virtual);
+        while (!queue.isEmpty()) {
+            ObjectState obj = queue.removeLast();
+            if (obj.isVirtual()) {
+                for (ValueNode entry : obj.getEntries()) {
+                    if (entry instanceof VirtualObjectNode) {
+                        ObjectState fieldObj = state.getObjectState((VirtualObjectNode) entry);
+                        if (fieldObj.isVirtual() && !virtual.contains(fieldObj)) {
+                            virtual.add(fieldObj);
+                            queue.addLast(fieldObj);
                         }
                     }
                 }
             }
-            for (ObjectState obj : virtual) {
-                EscapeObjectState v;
-                if (obj.isVirtual()) {
-                    ValueNode[] fieldState = obj.getEntries().clone();
-                    for (int i = 0; i < fieldState.length; i++) {
-                        ObjectState valueObj = getObjectState(state, fieldState[i]);
-                        if (valueObj != null) {
-                            if (valueObj.isVirtual()) {
-                                fieldState[i] = valueObj.virtual;
-                            } else {
-                                fieldState[i] = valueObj.getMaterializedValue();
-                            }
-                        }
-                    }
-                    v = new VirtualObjectState(obj.virtual, fieldState);
+        }
+    }
+
+    private EscapeObjectState createVirtualObjectState(final BlockT state, ObjectState obj) {
+        EscapeObjectState v;
+        ValueNode[] fieldState = obj.getEntries().clone();
+        for (int i = 0; i < fieldState.length; i++) {
+            ObjectState valueObj = getObjectState(state, fieldState[i]);
+            if (valueObj != null) {
+                if (valueObj.isVirtual()) {
+                    fieldState[i] = valueObj.virtual;
                 } else {
-                    v = new MaterializedObjectState(obj.virtual, obj.getMaterializedValue());
+                    fieldState[i] = valueObj.getMaterializedValue();
                 }
-                effects.addVirtualMapping(frameState, v);
+            }
+        }
+        v = new VirtualObjectState(obj.virtual, fieldState);
+        return v;
+    }
+
+    private void collectLockedVirtualObjects(final BlockT state, final Set<ObjectState> virtual) {
+        for (ObjectState obj : state.getStates()) {
+            if (obj.isVirtual() && obj.hasLocks()) {
+                virtual.add(obj);
             }
         }
     }
@@ -213,7 +248,7 @@
         }
     }
 
-    private boolean replaceWithMaterialized(ValueNode value, Node usage, FixedNode materializeBefore, BlockT state, ObjectState obj, GraphEffectList effects, DebugMetric metric) {
+    private boolean replaceWithMaterialized(Node value, Node usage, FixedNode materializeBefore, BlockT state, ObjectState obj, GraphEffectList effects, DebugMetric metric) {
         boolean materialized = ensureMaterialized(state, obj, materializeBefore, effects, metric);
         effects.replaceFirstInput(usage, value, obj.getMaterializedValue());
         return materialized;
@@ -221,44 +256,51 @@
 
     @Override
     protected void processLoopExit(LoopExitNode exitNode, BlockT initialState, BlockT exitState, GraphEffectList effects) {
-        Map<VirtualObjectNode, ProxyNode> proxies = Node.newMap();
-
-        for (ProxyNode proxy : exitNode.proxies()) {
-            ObjectState obj = getObjectState(exitState, proxy.value());
-            if (obj != null) {
-                proxies.put(obj.virtual, proxy);
+        if (exitNode.graph().hasValueProxies()) {
+            Map<VirtualObjectNode, ProxyNode> proxies = Node.newMap();
+            for (ProxyNode proxy : exitNode.proxies()) {
+                ObjectState obj = getObjectState(exitState, proxy.value());
+                if (obj != null) {
+                    proxies.put(obj.virtual, proxy);
+                }
             }
-        }
-        if (exitNode.graph().hasValueProxies()) {
             for (ObjectState obj : exitState.getStates()) {
                 ObjectState initialObj = initialState.getObjectStateOptional(obj.virtual);
                 if (obj.isVirtual()) {
-                    for (int i = 0; i < obj.getEntries().length; i++) {
-                        ValueNode value = obj.getEntry(i);
-                        if (!(value instanceof VirtualObjectNode || value.isConstant())) {
-                            if (exitNode.loopBegin().isPhiAtMerge(value) || initialObj == null || !initialObj.isVirtual() || initialObj.getEntry(i) != value) {
-                                ProxyNode proxy = new ValueProxyNode(value, exitNode);
-                                obj.setEntry(i, proxy);
-                                effects.addFloatingNode(proxy, "virtualProxy");
-                            }
-                        }
-                    }
+                    processVirtualAtLoopExit(exitNode, effects, obj, initialObj);
                 } else {
-                    if (initialObj == null || initialObj.isVirtual()) {
-                        ProxyNode proxy = proxies.get(obj.virtual);
-                        if (proxy == null) {
-                            proxy = new ValueProxyNode(obj.getMaterializedValue(), exitNode);
-                            effects.addFloatingNode(proxy, "proxy");
-                        } else {
-                            effects.replaceFirstInput(proxy, proxy.value(), obj.getMaterializedValue());
-                            // nothing to do - will be handled in processNode
-                        }
-                        obj.updateMaterializedValue(proxy);
-                    } else {
-                        if (initialObj.getMaterializedValue() == obj.getMaterializedValue()) {
-                            Debug.log("materialized value changes within loop: %s vs. %s at %s", initialObj.getMaterializedValue(), obj.getMaterializedValue(), exitNode);
-                        }
-                    }
+                    processMaterializedAtLoopExit(exitNode, effects, proxies, obj, initialObj);
+                }
+            }
+        }
+    }
+
+    private static void processMaterializedAtLoopExit(LoopExitNode exitNode, GraphEffectList effects, Map<VirtualObjectNode, ProxyNode> proxies, ObjectState obj, ObjectState initialObj) {
+        if (initialObj == null || initialObj.isVirtual()) {
+            ProxyNode proxy = proxies.get(obj.virtual);
+            if (proxy == null) {
+                proxy = new ValueProxyNode(obj.getMaterializedValue(), exitNode);
+                effects.addFloatingNode(proxy, "proxy");
+            } else {
+                effects.replaceFirstInput(proxy, proxy.value(), obj.getMaterializedValue());
+                // nothing to do - will be handled in processNode
+            }
+            obj.updateMaterializedValue(proxy);
+        } else {
+            if (initialObj.getMaterializedValue() == obj.getMaterializedValue()) {
+                Debug.log("materialized value changes within loop: %s vs. %s at %s", initialObj.getMaterializedValue(), obj.getMaterializedValue(), exitNode);
+            }
+        }
+    }
+
+    private static void processVirtualAtLoopExit(LoopExitNode exitNode, GraphEffectList effects, ObjectState obj, ObjectState initialObj) {
+        for (int i = 0; i < obj.getEntries().length; i++) {
+            ValueNode value = obj.getEntry(i);
+            if (!(value instanceof VirtualObjectNode || value.isConstant())) {
+                if (exitNode.loopBegin().isPhiAtMerge(value) || initialObj == null || !initialObj.isVirtual() || initialObj.getEntry(i) != value) {
+                    ProxyNode proxy = new ValueProxyNode(value, exitNode);
+                    obj.setEntry(i, proxy);
+                    effects.addFloatingNode(proxy, "virtualProxy");
                 }
             }
         }
@@ -271,15 +313,28 @@
 
     protected class MergeProcessor extends EffectsClosure<BlockT>.MergeProcessor {
 
-        private final HashMap<Object, ValuePhiNode> materializedPhis = CollectionsFactory.newMap();
-        private final Map<ValueNode, ValuePhiNode[]> valuePhis = Node.newIdentityMap();
-        private final Map<ValuePhiNode, VirtualObjectNode> valueObjectVirtuals = Node.newIdentityMap();
+        private HashMap<Object, ValuePhiNode> materializedPhis;
+        private Map<ValueNode, ValuePhiNode[]> valuePhis;
+        private Map<ValuePhiNode, VirtualObjectNode> valueObjectVirtuals;
+        private final boolean needsCaching;
 
         public MergeProcessor(Block mergeBlock) {
             super(mergeBlock);
+            needsCaching = mergeBlock.isLoopHeader();
         }
 
-        protected <T> PhiNode getCachedPhi(T virtual, Stamp stamp) {
+        protected <T> PhiNode getPhi(T virtual, Stamp stamp) {
+            if (needsCaching) {
+                return getPhiCached(virtual, stamp);
+            } else {
+                return new ValuePhiNode(stamp, merge);
+            }
+        }
+
+        private <T> PhiNode getPhiCached(T virtual, Stamp stamp) {
+            if (materializedPhis == null) {
+                materializedPhis = CollectionsFactory.newMap();
+            }
             ValuePhiNode result = materializedPhis.get(virtual);
             if (result == null) {
                 result = new ValuePhiNode(stamp, merge);
@@ -289,6 +344,17 @@
         }
 
         private PhiNode[] getValuePhis(ValueNode key, int entryCount) {
+            if (needsCaching) {
+                return getValuePhisCached(key, entryCount);
+            } else {
+                return new ValuePhiNode[entryCount];
+            }
+        }
+
+        private PhiNode[] getValuePhisCached(ValueNode key, int entryCount) {
+            if (valuePhis == null) {
+                valuePhis = Node.newIdentityMap();
+            }
             ValuePhiNode[] result = valuePhis.get(key);
             if (result == null) {
                 result = new ValuePhiNode[entryCount];
@@ -299,6 +365,17 @@
         }
 
         private VirtualObjectNode getValueObjectVirtual(ValuePhiNode phi, VirtualObjectNode virtual) {
+            if (needsCaching) {
+                return getValueObjectVirtualCached(phi, virtual);
+            } else {
+                return virtual.duplicate();
+            }
+        }
+
+        private VirtualObjectNode getValueObjectVirtualCached(ValuePhiNode phi, VirtualObjectNode virtual) {
+            if (valueObjectVirtuals == null) {
+                valueObjectVirtuals = Node.newIdentityMap();
+            }
             VirtualObjectNode result = valueObjectVirtuals.get(phi);
             if (result == null) {
                 result = virtual.duplicate();
@@ -321,10 +398,7 @@
             super.merge(states);
 
             // calculate the set of virtual objects that exist in all predecessors
-            Set<VirtualObjectNode> virtualObjTemp = Node.newSet(states.get(0).getVirtualObjects());
-            for (int i = 1; i < states.size(); i++) {
-                virtualObjTemp.retainAll(states.get(i).getVirtualObjects());
-            }
+            Set<VirtualObjectNode> virtualObjTemp = intersectVirtualObjects(states);
 
             ObjectState[] objStates = new ObjectState[states.size()];
             boolean materialized;
@@ -356,12 +430,14 @@
                         if (uniqueMaterializedValue != null) {
                             newState.addObject(object, new ObjectState(object, uniqueMaterializedValue, EscapeState.Materialized, null));
                         } else {
-                            PhiNode materializedValuePhi = getCachedPhi(object, StampFactory.forKind(Kind.Object));
+                            PhiNode materializedValuePhi = getPhi(object, StampFactory.forKind(Kind.Object));
                             mergeEffects.addFloatingNode(materializedValuePhi, "materializedPhi");
                             for (int i = 0; i < objStates.length; i++) {
                                 ObjectState obj = objStates[i];
-                                Block predecessor = mergeBlock.getPredecessors().get(i);
-                                materialized |= ensureMaterialized(states.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
+                                if (obj.isVirtual()) {
+                                    Block predecessor = mergeBlock.getPredecessors().get(i);
+                                    materialized |= ensureMaterialized(states.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
+                                }
                                 afterMergeEffects.addPhiInput(materializedValuePhi, obj.getMaterializedValue());
                             }
                             newState.addObject(object, new ObjectState(object, materializedValuePhi, EscapeState.Materialized, null));
@@ -382,6 +458,14 @@
             } while (materialized);
         }
 
+        private Set<VirtualObjectNode> intersectVirtualObjects(List<BlockT> states) {
+            Set<VirtualObjectNode> virtualObjTemp = Node.newSet(states.get(0).getVirtualObjects());
+            for (int i = 1; i < states.size(); i++) {
+                virtualObjTemp.retainAll(states.get(i).getVirtualObjects());
+            }
+            return virtualObjTemp;
+        }
+
         /**
          * Try to merge multiple virtual object states into a single object state. If the incoming
          * object states are compatible, then this method will create PhiNodes for the object's
@@ -489,7 +573,7 @@
                 return materialized;
             } else {
                 // not compatible: materialize in all predecessors
-                PhiNode materializedValuePhi = getCachedPhi(object, StampFactory.forKind(Kind.Object));
+                PhiNode materializedValuePhi = getPhi(object, StampFactory.forKind(Kind.Object));
                 for (int i = 0; i < blockStates.size(); i++) {
                     ObjectState obj = objStates[i];
                     Block predecessor = mergeBlock.getPredecessors().get(i);
--- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/nodes/WordCastNode.java	Tue Jan 27 15:10:32 2015 +0100
+++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/nodes/WordCastNode.java	Tue Jan 27 16:58:48 2015 +0100
@@ -61,7 +61,7 @@
 
     @Override
     public Node canonical(CanonicalizerTool tool) {
-        if (usages().count() == 0) {
+        if (getUsageCount() == 0) {
             /* If the cast is unused, it can be eliminated. */
             return input;
         }