changeset 8565:0d0b1154438f

Merge
author Matthias Grimmer <grimmer@ssw.jku.at>
date Thu, 28 Mar 2013 16:58:44 +0100
parents bbd8167f21f9 (current diff) a1a97de0dc9d (diff)
children df2e756f9f56
files graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/SafeAccessNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/SafeReadNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/SafeWriteNode.java graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java
diffstat 45 files changed, 1214 insertions(+), 416 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Thu Mar 28 16:58:23 2013 +0100
+++ b/.hgignore	Thu Mar 28 16:58:44 2013 +0100
@@ -67,5 +67,6 @@
 *.bgv
 core.*
 *.jar
+*.jar.*
 eclipse-build.xml
 rebuild-launch.out
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java	Thu Mar 28 16:58:44 2013 +0100
@@ -120,9 +120,9 @@
                 PhasePlan phasePlan = getDefaultPhasePlan();
                 phasePlan.addPhase(PhasePosition.AFTER_PARSING, identifyBoxingPhase);
                 identifyBoxingPhase.apply(graph);
-                Collection<Invoke> hints = new ArrayList<>();
+                Map<Invoke, Double> hints = new HashMap<>();
                 for (Invoke invoke : graph.getInvokes()) {
-                    hints.add(invoke);
+                    hints.put(invoke, 1000d);
                 }
 
                 Assumptions assumptions = new Assumptions(false);
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfBoxingEliminationTest.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfBoxingEliminationTest.java	Thu Mar 28 16:58:44 2013 +0100
@@ -83,9 +83,9 @@
                 phasePlan.addPhase(PhasePosition.AFTER_PARSING, identifyBoxingPhase);
                 phasePlan.addPhase(PhasePosition.AFTER_PARSING, new PhiStampPhase());
                 identifyBoxingPhase.apply(graph);
-                Collection<Invoke> hints = new ArrayList<>();
+                Map<Invoke, Double> hints = new HashMap<>();
                 for (Invoke invoke : graph.getInvokes()) {
-                    hints.add(invoke);
+                    hints.put(invoke, 1000d);
                 }
 
                 Assumptions assumptions = new Assumptions(false);
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeExceptionTest.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeExceptionTest.java	Thu Mar 28 16:58:44 2013 +0100
@@ -60,9 +60,9 @@
 
     private void test(String snippet) {
         StructuredGraph graph = parseProfiled(snippet);
-        Collection<Invoke> hints = new ArrayList<>();
+        Map<Invoke, Double> hints = new HashMap<>();
         for (Invoke invoke : graph.getInvokes()) {
-            hints.add(invoke);
+            hints.put(invoke, 1000d);
         }
         Assumptions assumptions = new Assumptions(false);
         new InliningPhase(runtime(), hints, assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph);
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeHintsTest.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InvokeHintsTest.java	Thu Mar 28 16:58:44 2013 +0100
@@ -70,9 +70,9 @@
 
     private void test(String snippet) {
         StructuredGraph graph = parse(snippet);
-        Collection<Invoke> hints = new ArrayList<>();
+        Map<Invoke, Double> hints = new HashMap<>();
         for (Invoke invoke : graph.getInvokes()) {
-            hints.add(invoke);
+            hints.put(invoke, 1000d);
         }
 
         Assumptions assumptions = new Assumptions(false);
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MonitorGraphTest.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MonitorGraphTest.java	Thu Mar 28 16:58:44 2013 +0100
@@ -88,9 +88,9 @@
         for (Node n : local.usages().filter(isNotA(FrameState.class)).snapshot()) {
             n.replaceFirstInput(local, constant);
         }
-        Collection<Invoke> hints = new ArrayList<>();
+        Map<Invoke, Double> hints = new HashMap<>();
         for (Invoke invoke : graph.getInvokes()) {
-            hints.add(invoke);
+            hints.put(invoke, 1000d);
         }
         Assumptions assumptions = new Assumptions(false);
         new InliningPhase(runtime(), hints, assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph);
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java	Thu Mar 28 16:58:44 2013 +0100
@@ -23,6 +23,7 @@
 package com.oracle.graal.compiler.test.ea;
 
 import junit.framework.*;
+import junit.framework.Assert;
 
 import org.junit.Test;
 
@@ -30,7 +31,6 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.test.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
@@ -183,12 +183,7 @@
 
     @Test
     public void testInstanceOf() {
-        ReturnNode returnNode = testEscapeAnalysis("testInstanceOfSnippet", null, false);
-        ValueNode result = returnNode.result();
-        Assert.assertTrue(result instanceof ConditionalNode);
-        ConditionalNode conditional = (ConditionalNode) result;
-        Assert.assertTrue(conditional.condition() instanceof LogicConstantNode);
-        Assert.assertEquals(true, ((LogicConstantNode) conditional.condition()).getValue());
+        testEscapeAnalysis("testInstanceOfSnippet", Constant.forInt(1), false);
     }
 
     public boolean testInstanceOfSnippet() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/IterativeInliningTest.java	Thu Mar 28 16:58:44 2013 +0100
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.test.ea;
+
+import static org.junit.Assert.*;
+
+import java.util.concurrent.*;
+
+import org.junit.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.compiler.test.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.virtual.phases.ea.*;
+
+public class IterativeInliningTest extends GraalCompilerTest {
+
+    private StructuredGraph graph;
+
+    public static class TestObject {
+
+        public Callable<Integer> callable;
+
+        public TestObject(Callable<Integer> callable) {
+            this.callable = callable;
+        }
+    }
+
+    public static class TestInt implements Callable<Integer> {
+
+        public int x;
+        public int y;
+
+        public TestInt(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        @Override
+        public Integer call() throws Exception {
+            return new Integer(x);
+        }
+    }
+
+    @SuppressWarnings("all")
+    public static int testSimpleSnippet(int b) throws Exception {
+        TestObject a = new TestObject(null);
+        a.callable = new TestInt(b, 9);
+        return a.callable.call();
+    }
+
+    @Test
+    public void testSimple() {
+        ValueNode result = getReturn("testSimpleSnippet").result();
+        assertTrue(graph.getNodes(LoadFieldNode.class).isEmpty());
+        assertEquals(graph.getLocal(0), result);
+    }
+
+    @SuppressWarnings("all")
+    public static int testSimpleReadSnippet(TestObject a, int b) throws Exception {
+        a.callable = new TestInt(b, 9);
+        return a.callable.call();
+    }
+
+    @Test
+    public void testSimpleRead() {
+        ValueNode result = getReturn("testSimpleReadSnippet").result();
+        assertTrue(graph.getNodes(LoadFieldNode.class).isEmpty());
+        assertEquals(graph.getLocal(1), result);
+    }
+
+    final ReturnNode getReturn(String snippet) {
+        processMethod(snippet);
+        assertEquals(1, graph.getNodes(ReturnNode.class).count());
+        return graph.getNodes(ReturnNode.class).first();
+    }
+
+    private void processMethod(final String snippet) {
+        graph = parse(snippet);
+        new ComputeProbabilityPhase().apply(graph);
+        GraalOptions.PEAReadCache = true;
+        new IterativeInliningPhase(runtime(), new Assumptions(false), null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java	Thu Mar 28 16:58:44 2013 +0100
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.test.ea;
+
+import static org.junit.Assert.*;
+
+import java.util.concurrent.*;
+
+import org.junit.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.compiler.test.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.virtual.phases.ea.*;
+
+public class PEAReadEliminationTest extends GraalCompilerTest {
+
+    private StructuredGraph graph;
+
+    public static Object staticField;
+
+    public static class TestObject implements Callable<Integer> {
+
+        public int x;
+        public int y;
+
+        public TestObject(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        @Override
+        public Integer call() throws Exception {
+            return x;
+        }
+    }
+
+    public static class TestObject2 {
+
+        public Object x;
+        public Object y;
+
+        public TestObject2(Object x, Object y) {
+            this.x = x;
+            this.y = y;
+        }
+    }
+
+    @SuppressWarnings("all")
+    public static int testSimpleSnippet(TestObject a) {
+        a.x = 2;
+        staticField = a;
+        return a.x;
+    }
+
+    @Test
+    public void testSimple() {
+        ValueNode result = getReturn("testSimpleSnippet").result();
+        assertTrue(graph.getNodes(LoadFieldNode.class).isEmpty());
+        assertTrue(result.isConstant());
+        assertEquals(2, result.asConstant().asInt());
+    }
+
+    @SuppressWarnings("all")
+    public static int testSimpleConflictSnippet(TestObject a, TestObject b) {
+        a.x = 2;
+        b.x = 3;
+        staticField = a;
+        return a.x;
+    }
+
+    @Test
+    public void testSimpleConflict() {
+        ValueNode result = getReturn("testSimpleConflictSnippet").result();
+        assertFalse(result.isConstant());
+        assertTrue(result instanceof LoadFieldNode);
+    }
+
+    @SuppressWarnings("all")
+    public static int testParamSnippet(TestObject a, int b) {
+        a.x = b;
+        return a.x;
+    }
+
+    @Test
+    public void testParam() {
+        ValueNode result = getReturn("testParamSnippet").result();
+        assertTrue(graph.getNodes(LoadFieldNode.class).isEmpty());
+        assertEquals(graph.getLocal(1), result);
+    }
+
+    @SuppressWarnings("all")
+    public static int testMaterializedSnippet(int a) {
+        TestObject obj = new TestObject(a, 0);
+        staticField = obj;
+        return obj.x;
+    }
+
+    @Test
+    public void testMaterialized() {
+        ValueNode result = getReturn("testMaterializedSnippet").result();
+        assertTrue(graph.getNodes(LoadFieldNode.class).isEmpty());
+        assertEquals(graph.getLocal(0), result);
+    }
+
+    @SuppressWarnings("all")
+    public static int testSimpleLoopSnippet(TestObject obj, int a, int b) {
+        obj.x = a;
+        for (int i = 0; i < 10; i++) {
+            staticField = obj;
+        }
+        return obj.x;
+    }
+
+    @Test
+    public void testSimpleLoop() {
+        ValueNode result = getReturn("testSimpleLoopSnippet").result();
+        assertTrue(graph.getNodes(LoadFieldNode.class).isEmpty());
+        assertEquals(graph.getLocal(1), result);
+    }
+
+    @SuppressWarnings("all")
+    public static int testBadLoopSnippet(TestObject obj, int a, int b) {
+        obj.x = a;
+        for (int i = 0; i < 10; i++) {
+            staticField = obj;
+            obj.x = 0;
+        }
+        return obj.x;
+    }
+
+    @Test
+    public void testBadLoop() {
+        ValueNode result = getReturn("testBadLoopSnippet").result();
+        assertEquals(1, graph.getNodes(LoadFieldNode.class).count());
+        assertTrue(result instanceof LoadFieldNode);
+    }
+
+    @SuppressWarnings("all")
+    public static int testPhiSnippet(TestObject a, int b) {
+        if (b < 0) {
+            a.x = 1;
+        } else {
+            a.x = 2;
+        }
+        return a.x;
+    }
+
+    @Test
+    public void testPhi() {
+        ValueNode result = getReturn("testPhiSnippet").result();
+        assertTrue(graph.getNodes(LoadFieldNode.class).isEmpty());
+        assertTrue(result instanceof PhiNode);
+        PhiNode phi = (PhiNode) result;
+        assertTrue(phi.valueAt(0).isConstant());
+        assertTrue(phi.valueAt(1).isConstant());
+        assertEquals(1, phi.valueAt(0).asConstant().asInt());
+        assertEquals(2, phi.valueAt(1).asConstant().asInt());
+    }
+
+    @SuppressWarnings("all")
+    public static void testSimpleStoreSnippet(TestObject a, int b) {
+        a.x = b;
+        a.x = b;
+    }
+
+    @Test
+    public void testSimpleStore() {
+        processMethod("testSimpleStoreSnippet");
+        assertEquals(1, graph.getNodes().filter(StoreFieldNode.class).count());
+    }
+
+    final ReturnNode getReturn(String snippet) {
+        processMethod(snippet);
+        assertEquals(1, graph.getNodes(ReturnNode.class).count());
+        return graph.getNodes(ReturnNode.class).first();
+    }
+
+    private void processMethod(final String snippet) {
+        graph = parse(snippet);
+        new ComputeProbabilityPhase().apply(graph);
+        Assumptions assumptions = new Assumptions(false);
+        new InliningPhase(runtime(), null, assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph);
+        GraalOptions.PEAReadCache = true;
+        new PartialEscapeAnalysisPhase(runtime(), assumptions, false).apply(graph);
+    }
+}
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Thu Mar 28 16:58:44 2013 +0100
@@ -121,12 +121,16 @@
         }
 
         if (GraalOptions.Inline && !plan.isPhaseDisabled(InliningPhase.class)) {
-            new InliningPhase(runtime, null, assumptions, cache, plan, optimisticOpts).apply(graph);
-            new DeadCodeEliminationPhase().apply(graph);
+            if (GraalOptions.IterativeInlining) {
+                new IterativeInliningPhase(runtime, assumptions, cache, plan, optimisticOpts).apply(graph);
+            } else {
+                new InliningPhase(runtime, null, assumptions, cache, plan, optimisticOpts).apply(graph);
+                new DeadCodeEliminationPhase().apply(graph);
 
-            if (GraalOptions.ConditionalElimination && GraalOptions.OptCanonicalizer) {
-                new CanonicalizerPhase(runtime, assumptions).apply(graph);
-                new IterativeConditionalEliminationPhase(runtime, assumptions).apply(graph);
+                if (GraalOptions.ConditionalElimination && GraalOptions.OptCanonicalizer) {
+                    new CanonicalizerPhase(runtime, assumptions).apply(graph);
+                    new IterativeConditionalEliminationPhase(runtime, assumptions).apply(graph);
+                }
             }
         }
 
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Thu Mar 28 16:58:44 2013 +0100
@@ -197,6 +197,7 @@
                 if (inputChanged != null) {
                     inputChanged.inputChanged(this);
                 }
+                assert newInput.usages != null : "not yet added? " + newInput;
                 newInput.usages.add(this);
             }
         }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Thu Mar 28 16:58:44 2013 +0100
@@ -44,6 +44,7 @@
 import com.oracle.graal.hotspot.phases.*;
 import com.oracle.graal.java.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.debug.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.PhasePlan.PhasePosition;
 import com.oracle.graal.printer.*;
@@ -76,6 +77,8 @@
 
     private boolean quietMeterAndTime;
 
+    private long compilerStartTime;
+
     public VMToCompilerImpl(HotSpotGraalRuntime compiler) {
         this.graalRuntime = compiler;
 
@@ -196,6 +199,95 @@
             t.setDaemon(true);
             t.start();
         }
+
+        if (GraalOptions.BenchmarkDynamicCounters) {
+            System.setErr(new PrintStream(new BenchmarkCountersOutputStream(System.err, " starting =====", " PASSED in ", "\n")));
+            System.setOut(new PrintStream(new BenchmarkCountersOutputStream(System.out, "Iteration ~ (~s) begins: ", "Iteration ~ (~s) ends:   ", "\n")));
+            DynamicCounterNode.excludedClassPrefix = "Lcom/oracle/graal/";
+            DynamicCounterNode.enabled = true;
+        }
+        if (GraalOptions.GenericDynamicCounters) {
+            DynamicCounterNode.enabled = true;
+        }
+        compilerStartTime = System.nanoTime();
+    }
+
+    private final class BenchmarkCountersOutputStream extends CallbackOutputStream {
+
+        private long startTime;
+        private boolean waitingForEnd;
+
+        private BenchmarkCountersOutputStream(PrintStream delegate, String... patterns) {
+            super(delegate, patterns);
+        }
+
+        @Override
+        protected void patternFound(int index) {
+            switch (index) {
+                case 0:
+                    startTime = System.nanoTime();
+                    DynamicCounterNode.clear();
+                    break;
+                case 1:
+                    waitingForEnd = true;
+                    break;
+                case 2:
+                    if (waitingForEnd) {
+                        waitingForEnd = false;
+                        DynamicCounterNode.dump(delegate, (System.nanoTime() - startTime) / 1000000000d);
+                    }
+                    break;
+            }
+        }
+    }
+
+    public abstract static class CallbackOutputStream extends OutputStream {
+
+        protected final PrintStream delegate;
+        private final byte[][] patterns;
+        private final int[] positions;
+
+        public CallbackOutputStream(PrintStream delegate, String... patterns) {
+            this.delegate = delegate;
+            this.positions = new int[patterns.length];
+            this.patterns = new byte[patterns.length][];
+            for (int i = 0; i < patterns.length; i++) {
+                this.patterns[i] = patterns[i].getBytes();
+            }
+        }
+
+        protected abstract void patternFound(int index);
+
+        @Override
+        public void write(int b) throws IOException {
+            try {
+                delegate.write(b);
+                for (int i = 0; i < patterns.length; i++) {
+                    int j = positions[i];
+                    byte[] cs = patterns[i];
+                    byte patternChar = cs[j];
+                    if (patternChar == '~' && Character.isDigit(b)) {
+                        // nothing to do...
+                    } else {
+                        if (patternChar == '~') {
+                            patternChar = cs[++positions[i]];
+                        }
+                        if (b == patternChar) {
+                            positions[i]++;
+                        } else {
+                            positions[i] = 0;
+                        }
+                    }
+                    if (positions[i] == patterns[i].length) {
+                        positions[i] = 0;
+                        patternFound(i);
+                    }
+                }
+            } catch (RuntimeException e) {
+                e.printStackTrace(delegate);
+                throw e;
+            }
+        }
     }
 
     /**
@@ -281,6 +373,7 @@
         }
         System.gc();
         phaseTransition("bootstrap2");
+
     }
 
     private MetricRateInPhase parsedBytecodesPerSecond;
@@ -353,6 +446,9 @@
         }
 
         SnippetCounter.printGroups(TTY.out().out());
+        if (GraalOptions.GenericDynamicCounters) {
+            DynamicCounterNode.dump(System.out, (System.nanoTime() - compilerStartTime) / 1000000000d);
+        }
     }
 
     private void flattenChildren(DebugValueMap map, DebugValueMap globalMap) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Thu Mar 28 16:58:44 2013 +0100
@@ -529,8 +529,10 @@
         Kind wordKind = graalRuntime.getTarget().wordKind;
         if (n instanceof ArrayLengthNode) {
             ArrayLengthNode arrayLengthNode = (ArrayLengthNode) n;
-            SafeReadNode safeReadArrayLength = safeReadArrayLength(arrayLengthNode.array());
-            graph.replaceFixedWithFixed(arrayLengthNode, safeReadArrayLength);
+            ValueNode array = arrayLengthNode.array();
+            ReadNode arrayLengthRead = graph.add(new ReadNode(array, LocationNode.create(LocationNode.FINAL_LOCATION, Kind.Int, config.arrayLengthOffset, graph), StampFactory.positiveInt()));
+            arrayLengthRead.dependencies().add(tool.createNullCheckGuard(array));
+            graph.replaceFixedWithFixed(arrayLengthNode, arrayLengthRead);
         } else if (n instanceof Invoke) {
             Invoke invoke = (Invoke) n;
             if (invoke.callTarget() instanceof MethodCallTargetNode) {
@@ -566,9 +568,6 @@
                             graph.addAfterFixed(metaspaceMethod, compiledEntry);
                         }
                     }
-                } else if (callTarget.invokeKind() == InvokeKind.Special || callTarget.invokeKind() == InvokeKind.Static) {
-                    loweredCallTarget = graph.add(new HotSpotDirectCallTargetNode(parameters, invoke.node().stamp(), signature, callTarget.targetMethod(), CallingConvention.Type.JavaCall,
-                                    callTarget.invokeKind()));
                 }
 
                 if (loweredCallTarget == null) {
@@ -773,10 +772,6 @@
         return IndexedLocationNode.create(LocationNode.getArrayLocation(elementKind), elementKind, getArrayBaseOffset(elementKind), index, graph, scale);
     }
 
-    private SafeReadNode safeReadArrayLength(ValueNode array) {
-        return safeRead(array.graph(), Kind.Int, array, config.arrayLengthOffset, StampFactory.positiveInt());
-    }
-
     private static ValueNode createBoundsCheck(AccessIndexedNode n, LoweringTool tool) {
         StructuredGraph graph = (StructuredGraph) n.graph();
         ArrayLengthNode arrayLength = graph.add(new ArrayLengthNode(n.array()));
@@ -786,10 +781,6 @@
         return guard;
     }
 
-    private static SafeReadNode safeRead(Graph graph, Kind kind, ValueNode value, int offset, Stamp stamp) {
-        return graph.add(new SafeReadNode(value, LocationNode.create(LocationNode.FINAL_LOCATION, kind, offset, graph), stamp));
-    }
-
     public ResolvedJavaType lookupJavaType(Class<?> clazz) {
         return HotSpotResolvedObjectType.fromClass(clazz);
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/WriteBarrierPost.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/WriteBarrierPost.java	Thu Mar 28 16:58:44 2013 +0100
@@ -93,5 +93,5 @@
     }
 
     @NodeIntrinsic
-    public static native void arrayCopyWriteBarrier(Object array, Object value, int index);
+    public static native void arrayCopyWriteBarrier(Object array, Object value, long index);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ArrayCopyNode.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ArrayCopyNode.java	Thu Mar 28 16:58:44 2013 +0100
@@ -24,7 +24,6 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.debug.*;
-import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.Node.IterableNodeType;
 import com.oracle.graal.loop.phases.*;
 import com.oracle.graal.nodes.*;
@@ -88,21 +87,6 @@
         new CanonicalizerPhase(tool.getRuntime(), tool.assumptions()).apply(snippetGraph);
     }
 
-    private static void replaceSnippetInvokes(StructuredGraph snippetGraph, ResolvedJavaMethod targetMethod, int bci) {
-        for (InvokeNode invoke : snippetGraph.getNodes(InvokeNode.class)) {
-            if (invoke.methodCallTarget().targetMethod() != targetMethod) {
-                throw new GraalInternalError("unexpected invoke in arraycopy snippet");
-            }
-            if (invoke.stateAfter().bci == FrameState.INVALID_FRAMESTATE_BCI) {
-                InvokeNode newInvoke = snippetGraph.add(new InvokeNode(invoke.methodCallTarget(), bci));
-                newInvoke.setStateAfter(snippetGraph.add(new FrameState(FrameState.AFTER_BCI)));
-                snippetGraph.replaceFixedWithFixed(invoke, newInvoke);
-            } else {
-                assert invoke.stateAfter().bci == FrameState.AFTER_BCI : invoke;
-            }
-        }
-    }
-
     @Override
     protected StructuredGraph getSnippetGraph(LoweringTool tool) {
         if (!GraalOptions.IntrinsifyArrayCopy) {
@@ -115,7 +99,7 @@
             snippetGraph = ((StructuredGraph) snippetMethod.getCompilerStorage().get(Snippet.class)).copy();
             assert snippetGraph != null : "ArrayCopySnippets should be installed";
 
-            replaceSnippetInvokes(snippetGraph, getTargetMethod(), getBci());
+            replaceSnippetInvokes(snippetGraph);
         } else {
             assert snippetGraph != null : "ArrayCopySnippets should be installed";
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ArrayCopySnippets.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ArrayCopySnippets.java	Thu Mar 28 16:58:44 2013 +0100
@@ -246,7 +246,7 @@
         int header = arrayBaseOffset(Kind.Object);
         if (src == dest && srcPos < destPos) { // bad aliased case
             long start = (long) (length - 1) * scale;
-            int j = length - 1;
+            long j = (long) (length) - 1;
             for (long i = start; i >= 0; i -= scale) {
                 Object a = UnsafeLoadNode.load(src, header, i + (long) srcPos * scale, Kind.Object);
                 DirectObjectStoreNode.storeObject(dest, header, i + (long) destPos * scale, a);
@@ -255,7 +255,7 @@
             }
         } else {
             long end = (long) length * scale;
-            int j = 0;
+            long j = srcPos;
             for (long i = 0; i < end; i += scale) {
                 Object a = UnsafeLoadNode.load(src, header, i + (long) srcPos * scale, Kind.Object);
                 DirectObjectStoreNode.storeObject(dest, header, i + (long) destPos * scale, a);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Thu Mar 28 16:58:44 2013 +0100
@@ -136,6 +136,7 @@
                     @ConstantParameter("alignment") int alignment,
                     @ConstantParameter("headerSize") int headerSize,
                     @ConstantParameter("log2ElementSize") int log2ElementSize,
+                    @ConstantParameter("fillContents") boolean fillContents,
                     @ConstantParameter("type") ResolvedJavaType type) {
         if (!belowThan(length, MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH)) {
             probability(DEOPT_PATH_PROBABILITY);
@@ -144,7 +145,7 @@
         }
         int allocationSize = computeArrayAllocationSize(length, alignment, headerSize, log2ElementSize);
         Word memory = TLABAllocateNode.allocateVariableSize(allocationSize);
-        return InitializeArrayNode.initialize(memory, length, allocationSize, type, true, false);
+        return InitializeArrayNode.initialize(memory, length, allocationSize, type, fillContents, false);
     }
 
     /**
@@ -240,7 +241,7 @@
             allocate = snippet("allocate", int.class);
             initializeObject = snippet("initializeObject", Word.class, Word.class, Word.class, int.class, boolean.class, boolean.class);
             initializeArray = snippet("initializeArray", Word.class, Word.class, int.class, int.class, Word.class, int.class, boolean.class, boolean.class);
-            allocateArrayAndInitialize = snippet("allocateArrayAndInitialize", int.class, int.class, int.class, int.class, ResolvedJavaType.class);
+            allocateArrayAndInitialize = snippet("allocateArrayAndInitialize", int.class, int.class, int.class, int.class, boolean.class, ResolvedJavaType.class);
             newmultiarray = snippet("newmultiarray", Word.class, int.class, int[].class);
         }
 
@@ -299,7 +300,8 @@
                 InitializeArrayNode initializeNode = graph.add(new InitializeArrayNode(tlabAllocateNode, lengthNode, sizeNode, arrayType, newArrayNode.fillContents(), newArrayNode.locked()));
                 graph.replaceFixedWithFixed(newArrayNode, initializeNode);
             } else {
-                Key key = new Key(allocateArrayAndInitialize).add("alignment", alignment).add("headerSize", headerSize).add("log2ElementSize", log2ElementSize).add("type", arrayType);
+                Key key = new Key(allocateArrayAndInitialize).add("alignment", alignment).add("headerSize", headerSize).add("log2ElementSize", log2ElementSize).add("fillContents",
+                                newArrayNode.fillContents()).add("type", arrayType);
                 Arguments arguments = new Arguments().add("length", lengthNode);
                 SnippetTemplate template = cache.get(key, assumptions);
                 Debug.log("Lowering allocateArrayAndInitialize in %s: node=%s, template=%s, arguments=%s", graph, newArrayNode, template, arguments);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneNode.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneNode.java	Thu Mar 28 16:58:44 2013 +0100
@@ -122,7 +122,7 @@
                     ValueNode[] state = new ValueNode[fields.length];
                     final LoadFieldNode[] loads = new LoadFieldNode[fields.length];
                     for (int i = 0; i < fields.length; i++) {
-                        state[i] = loads[i] = graph().add(new LoadFieldNode(obj, fields[i]));
+                        state[i] = loads[i] = new LoadFieldNode(obj, fields[i]);
                     }
 
                     final StructuredGraph structuredGraph = (StructuredGraph) graph();
@@ -130,7 +130,7 @@
 
                         public void run() {
                             for (LoadFieldNode load : loads) {
-                                structuredGraph.addBeforeFixed(ObjectCloneNode.this, load);
+                                structuredGraph.addBeforeFixed(ObjectCloneNode.this, structuredGraph.add(load));
                             }
                         }
                     });
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/FrameStateBuilder.java	Thu Mar 28 16:58:44 2013 +0100
@@ -33,7 +33,6 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.Node.Verbosity;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.nodes.util.*;
@@ -235,21 +234,21 @@
             ValueNode value = localAt(i);
             if (value != null && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
                 Debug.log(" inserting proxy for %s", value);
-                storeLocal(i, graph.unique(new ProxyNode(value, loopExit, PhiType.Value)));
+                storeLocal(i, ProxyNode.forValue(value, loopExit, graph));
             }
         }
         for (int i = 0; i < stackSize(); i++) {
             ValueNode value = stackAt(i);
             if (value != null && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
                 Debug.log(" inserting proxy for %s", value);
-                storeStack(i, graph.unique(new ProxyNode(value, loopExit, PhiType.Value)));
+                storeStack(i, ProxyNode.forValue(value, loopExit, graph));
             }
         }
         for (int i = 0; i < locks.length; i++) {
             ValueNode value = locks[i];
             if (value != null && (!loopEntryState.contains(value) || loopExit.loopBegin().isPhiAtMerge(value))) {
                 Debug.log(" inserting proxy for %s", value);
-                locks[i] = graph.unique(new ProxyNode(value, loopExit, PhiType.Value));
+                locks[i] = ProxyNode.forValue(value, loopExit, graph);
             }
         }
     }
@@ -259,21 +258,21 @@
             ValueNode value = localAt(i);
             if (value != null) {
                 Debug.log(" inserting proxy for %s", value);
-                storeLocal(i, graph.unique(new ProxyNode(value, begin, PhiType.Value)));
+                storeLocal(i, ProxyNode.forValue(value, begin, graph));
             }
         }
         for (int i = 0; i < stackSize(); i++) {
             ValueNode value = stackAt(i);
             if (value != null) {
                 Debug.log(" inserting proxy for %s", value);
-                storeStack(i, graph.unique(new ProxyNode(value, begin, PhiType.Value)));
+                storeStack(i, ProxyNode.forValue(value, begin, graph));
             }
         }
         for (int i = 0; i < locks.length; i++) {
             ValueNode value = locks[i];
             if (value != null) {
                 Debug.log(" inserting proxy for %s", value);
-                locks[i] = graph.unique(new ProxyNode(value, begin, PhiType.Value));
+                locks[i] = ProxyNode.forValue(value, begin, graph);
             }
         }
     }
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragment.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragment.java	Thu Mar 28 16:58:44 2013 +0100
@@ -32,7 +32,6 @@
 import com.oracle.graal.nodes.VirtualState.NodeClosure;
 import com.oracle.graal.nodes.VirtualState.VirtualClosure;
 import com.oracle.graal.nodes.cfg.*;
-import com.oracle.graal.nodes.type.*;
 
 public abstract class LoopFragment {
 
@@ -275,8 +274,7 @@
                 final ValueNode replaceWith;
                 ProxyNode newVpn = getDuplicatedNode(vpn);
                 if (newVpn != null) {
-                    PhiNode phi = graph.add(vpn.type() == PhiType.Value ? vpn.stamp() == StampFactory.virtual() ? new PhiNode(vpn.stamp(), merge) : new PhiNode(vpn.kind(), merge) : new PhiNode(
-                                    vpn.type(), merge));
+                    PhiNode phi = graph.add(vpn.type() == PhiType.Value ? new PhiNode(vpn.kind(), merge) : new PhiNode(vpn.type(), merge, vpn.getIdentity()));
                     phi.addInput(vpn);
                     phi.addInput(newVpn);
                     replaceWith = phi;
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragmentInside.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragmentInside.java	Thu Mar 28 16:58:44 2013 +0100
@@ -30,7 +30,6 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.VirtualState.NodeClosure;
-import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.nodes.util.*;
 
 public class LoopFragmentInside extends LoopFragment {
@@ -162,8 +161,7 @@
             }
             // create a new phi (we don't patch the old one since some usages of the old one may
             // still be valid)
-            PhiNode newPhi = graph.add(phi.type() == PhiType.Value ? phi.stamp() == StampFactory.virtual() ? new PhiNode(phi.stamp(), loopBegin) : new PhiNode(phi.kind(), loopBegin) : new PhiNode(
-                            phi.type(), loopBegin));
+            PhiNode newPhi = graph.add(phi.type() == PhiType.Value ? new PhiNode(phi.kind(), loopBegin) : new PhiNode(phi.type(), loopBegin, phi.getIdentity()));
             newPhi.addInput(first);
             for (LoopEndNode end : loopBegin.orderedLoopEnds()) {
                 newPhi.addInput(phi.valueAt(end));
@@ -253,8 +251,7 @@
             }
 
             for (final PhiNode phi : loopBegin.phis().snapshot()) {
-                final PhiNode firstPhi = graph.add(phi.type() == PhiType.Value ? phi.stamp() == StampFactory.virtual() ? new PhiNode(phi.stamp(), newExitMerge) : new PhiNode(phi.kind(), newExitMerge)
-                                : new PhiNode(phi.type(), newExitMerge));
+                final PhiNode firstPhi = graph.add(phi.type() == PhiType.Value ? new PhiNode(phi.kind(), newExitMerge) : new PhiNode(phi.type(), newExitMerge, phi.getIdentity()));
                 for (EndNode end : newExitMerge.forwardEnds()) {
                     LoopEndNode loopEnd = reverseEnds.get(end);
                     ValueNode prim = prim(phi.valueAt(loopEnd));
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopFullUnrollPhase.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/phases/LoopFullUnrollPhase.java	Thu Mar 28 16:58:44 2013 +0100
@@ -34,12 +34,17 @@
     private static final DebugMetric FULLY_UNROLLED_LOOPS = Debug.metric("FullUnrolls");
     private final GraalCodeCacheProvider runtime;
     private final Assumptions assumptions;
+    private int unrollCount;
 
     public LoopFullUnrollPhase(GraalCodeCacheProvider runtime, Assumptions assumptions) {
         this.runtime = runtime;
         this.assumptions = assumptions;
     }
 
+    public int getUnrollCount() {
+        return unrollCount;
+    }
+
     @Override
     protected void run(StructuredGraph graph) {
         if (graph.hasLoops()) {
@@ -55,11 +60,11 @@
                         FULLY_UNROLLED_LOOPS.increment();
                         Debug.dump(graph, "After fullUnroll %s", loop);
                         peeled = true;
+                        unrollCount++;
                         break;
                     }
                 }
             } while (peeled);
         }
     }
-
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PhiNode.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PhiNode.java	Thu Mar 28 16:58:44 2013 +0100
@@ -50,6 +50,7 @@
     @Input(notDataflow = true) private MergeNode merge;
     @Input private final NodeInputList<ValueNode> values = new NodeInputList<>(this);
     private final PhiType type;
+    private final Object identity;
 
     /**
      * Create a value phi ({@link PhiType#Value}) with the specified kind.
@@ -66,6 +67,7 @@
         assert stamp != StampFactory.forVoid();
         this.type = PhiType.Value;
         this.merge = merge;
+        this.identity = null;
     }
 
     /**
@@ -74,11 +76,12 @@
      * @param type the type of the new phi
      * @param merge the merge that the new phi belongs to
      */
-    public PhiNode(PhiType type, MergeNode merge) {
+    public PhiNode(PhiType type, MergeNode merge, Object identity) {
         super(type.stamp);
         assert type.stamp != null : merge + " " + type;
         this.type = type;
         this.merge = merge;
+        this.identity = identity;
     }
 
     public PhiType type() {
@@ -89,6 +92,11 @@
         return merge;
     }
 
+    public Object getIdentity() {
+        assert type != PhiType.Value;
+        return identity;
+    }
+
     public void setMerge(MergeNode x) {
         updateUsages(merge, x);
         merge = x;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ProxyNode.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ProxyNode.java	Thu Mar 28 16:58:44 2013 +0100
@@ -40,10 +40,12 @@
     @Input(notDataflow = true) private BeginNode proxyPoint;
     @Input private ValueNode value;
     private final PhiType type;
+    private final Object identity;
 
-    public ProxyNode(ValueNode value, BeginNode exit, PhiType type) {
+    public ProxyNode(ValueNode value, BeginNode exit, PhiType type, Object identity) {
         super(type == PhiType.Value ? value.stamp() : type.stamp);
         this.type = type;
+        this.identity = identity;
         assert exit != null;
         this.proxyPoint = exit;
         this.value = value;
@@ -71,6 +73,11 @@
         return type;
     }
 
+    public Object getIdentity() {
+        assert type != PhiType.Value;
+        return identity;
+    }
+
     @Override
     public boolean verify() {
         assert value != null;
@@ -100,4 +107,13 @@
             }
         }
     }
+
+    public static ProxyNode forValue(ValueNode value, BeginNode exit, StructuredGraph graph) {
+        return graph.unique(new ProxyNode(value, exit, PhiType.Value, null));
+    }
+
+    public static ProxyNode forMemory(ValueNode value, BeginNode exit, Object location, StructuredGraph graph) {
+        return graph.unique(new ProxyNode(value, exit, PhiType.Memory, location));
+    }
+
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java	Thu Mar 28 16:58:44 2013 +0100
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.nodes.debug;
 
+import java.io.*;
 import java.util.*;
 
 import com.oracle.graal.api.meta.*;
@@ -45,28 +46,39 @@
     private static final int MAX_COUNTERS = 10 * 1024;
     private static final long[] COUNTERS = new long[MAX_COUNTERS];
     private static final HashMap<String, Integer> INDEXES = new HashMap<>();
+    public static String excludedClassPrefix = null;
+    public static boolean enabled = false;
+
     private final String name;
+    private final long increment;
     private final boolean addContext;
 
-    public DynamicCounterNode(String name, boolean addContext) {
+    public DynamicCounterNode(String name, long increment, boolean addContext) {
         super(StampFactory.forVoid());
+        if (!enabled) {
+            throw new GraalInternalError("dynamic counters not enabled");
+        }
         this.name = name;
+        this.increment = increment;
         this.addContext = addContext;
     }
 
+    public String getName() {
+        return name;
+    }
+
+    public long getIncrement() {
+        return increment;
+    }
+
+    public boolean isAddContext() {
+        return addContext;
+    }
+
     private static synchronized int getIndex(String name) {
         Integer index = INDEXES.get(name);
         if (index == null) {
             index = INDEXES.size();
-            if (index == 0) {
-                Runtime.getRuntime().addShutdownHook(new Thread() {
-
-                    @Override
-                    public void run() {
-                        dump();
-                    }
-                });
-            }
             INDEXES.put(name, index);
             if (index >= MAX_COUNTERS) {
                 throw new GraalInternalError("too many dynamic counters");
@@ -77,44 +89,59 @@
         }
     }
 
-    private static synchronized void dump() {
+    public static synchronized void dump(PrintStream out, double seconds) {
         TreeMap<Long, String> sorted = new TreeMap<>();
 
         long sum = 0;
         for (int i = 0; i < MAX_COUNTERS; i++) {
             sum += COUNTERS[i];
         }
+        long cutoff = sum / 1000;
         int cnt = 0;
         for (Map.Entry<String, Integer> entry : INDEXES.entrySet()) {
-            sorted.put(COUNTERS[entry.getValue()] * MAX_COUNTERS + cnt++, entry.getKey());
+            if (COUNTERS[entry.getValue()] > cutoff) {
+                sorted.put(COUNTERS[entry.getValue()] * MAX_COUNTERS + cnt++, entry.getKey());
+            }
         }
 
+        out.println("=========== dynamic counters, time = " + seconds + " s");
         for (Map.Entry<Long, String> entry : sorted.entrySet()) {
-            System.out.println((entry.getKey() / MAX_COUNTERS) + ": " + entry.getValue());
+            long counter = entry.getKey() / MAX_COUNTERS;
+            out.println((int) (counter / seconds) + "/s \t" + (counter * 100 / sum) + "% \t" + entry.getValue());
         }
-        System.out.println(sum + ": total");
+        out.println((int) (sum / seconds) + "/s: total");
+        out.println("============================");
 
+        clear();
+    }
+
+    public static void clear() {
+        Arrays.fill(COUNTERS, 0);
     }
 
     @Override
     public void lower(LoweringTool tool) {
-        int index = addContext ? getIndex(name + " @ " + MetaUtil.format("%h.%n", ((StructuredGraph) graph()).method())) : getIndex(name);
+        StructuredGraph graph = (StructuredGraph) graph();
+        if (excludedClassPrefix == null || !graph.method().getDeclaringClass().getName().startsWith(excludedClassPrefix)) {
+            int index = addContext ? getIndex(name + " @ " + MetaUtil.format("%h.%n", ((StructuredGraph) graph()).method())) : getIndex(name);
 
-        StructuredGraph graph = (StructuredGraph) graph();
-        ConstantNode arrayConstant = ConstantNode.forObject(COUNTERS, tool.getRuntime(), graph);
-        ConstantNode indexConstant = ConstantNode.forInt(index, graph);
-        LoadIndexedNode load = graph.add(new LoadIndexedNode(arrayConstant, indexConstant, Kind.Long));
-        IntegerAddNode add = graph.add(new IntegerAddNode(Kind.Long, load, ConstantNode.forLong(1, graph)));
-        StoreIndexedNode store = graph.add(new StoreIndexedNode(arrayConstant, indexConstant, Kind.Long, add));
+            ConstantNode arrayConstant = ConstantNode.forObject(COUNTERS, tool.getRuntime(), graph);
+            ConstantNode indexConstant = ConstantNode.forInt(index, graph);
+            LoadIndexedNode load = graph.add(new LoadIndexedNode(arrayConstant, indexConstant, Kind.Long));
+            IntegerAddNode add = graph.add(new IntegerAddNode(Kind.Long, load, ConstantNode.forLong(increment, graph)));
+            StoreIndexedNode store = graph.add(new StoreIndexedNode(arrayConstant, indexConstant, Kind.Long, add));
 
-        graph.addBeforeFixed(this, load);
-        graph.addBeforeFixed(this, store);
+            graph.addBeforeFixed(this, load);
+            graph.addBeforeFixed(this, store);
+        }
         graph.removeFixed(this);
     }
 
-    public static void createCounter(String name, FixedNode before, boolean addContext) {
-        StructuredGraph graph = (StructuredGraph) before.graph();
-        DynamicCounterNode counter = graph.add(new DynamicCounterNode(name, addContext));
-        graph.addBeforeFixed(before, counter);
+    public static void addCounterBefore(String name, long increment, boolean addContext, FixedNode position) {
+        if (enabled) {
+            StructuredGraph graph = (StructuredGraph) position.graph();
+            DynamicCounterNode counter = graph.add(new DynamicCounterNode(name, increment, addContext));
+            graph.addBeforeFixed(position, counter);
+        }
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/SurvivingCounterNode.java	Thu Mar 28 16:58:44 2013 +0100
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.debug;
+
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.spi.*;
+
+/**
+ * This is a special version of the dynamic counter node that removes itself as soon as it's the
+ * only usage of the associated node. This way it only increments the counter if the node is
+ * actually executed.
+ */
+public class SurvivingCounterNode extends DynamicCounterNode implements Simplifiable, Virtualizable {
+
+    @Input private ValueNode checkedValue;
+
+    public SurvivingCounterNode(String name, long increment, boolean addContext, ValueNode checkedValue) {
+        super(name, increment, addContext);
+        this.checkedValue = checkedValue;
+    }
+
+    @Override
+    public void simplify(SimplifierTool tool) {
+        if (checkedValue instanceof FloatingNode && checkedValue.usages().count() == 1) {
+            tool.addToWorkList(checkedValue);
+            ((StructuredGraph) graph()).removeFixed(this);
+            // ((StructuredGraph) graph()).replaceFixedWithFixed(this, graph().add(new
+            // DynamicCounterNode("non-surviving " + getName(), getIncrement(), isAddContext())));
+        }
+    }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        State state = tool.getObjectState(checkedValue);
+        if (state != null && state.getState() == EscapeState.Virtual) {
+            tool.delete();
+        }
+    }
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/SafeAccessNode.java	Thu Mar 28 16:58:23 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,50 +0,0 @@
-/*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.nodes.extended;
-
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.type.*;
-
-/**
- * An analog to {@link AccessNode} with the additional semantics of null-checking the receiver
- * object before the access.
- */
-public abstract class SafeAccessNode extends FixedWithNextNode {
-
-    @Input private ValueNode object;
-    @Input private LocationNode location;
-
-    public SafeAccessNode(ValueNode object, LocationNode location, Stamp stamp) {
-        super(stamp);
-        this.object = object;
-        this.location = location;
-    }
-
-    public ValueNode object() {
-        return object;
-    }
-
-    public LocationNode location() {
-        return location;
-    }
-}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/SafeReadNode.java	Thu Mar 28 16:58:23 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,49 +0,0 @@
-/*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.nodes.extended;
-
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.type.*;
-
-/**
- * An analog to {@link ReadNode} with the additional semantics of null-checking the receiver object
- * before reading from it.
- */
-public class SafeReadNode extends SafeAccessNode implements Lowerable {
-
-    public SafeReadNode(ValueNode object, LocationNode location, Stamp stamp) {
-        super(object, location, stamp);
-        assert object != null && location != null;
-    }
-
-    @Override
-    public void lower(LoweringTool tool) {
-        StructuredGraph graph = (StructuredGraph) graph();
-        ValueNode guard = tool.createNullCheckGuard(object());
-        ReadNode read = graph.add(new ReadNode(object(), location(), stamp()));
-        read.dependencies().add(guard);
-
-        graph.replaceFixedWithFixed(this, read);
-    }
-}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/SafeWriteNode.java	Thu Mar 28 16:58:23 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.nodes.extended;
-
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.type.*;
-
-/**
- * An analog to {@link WriteNode} with the additional semantics of null-checking the receiver object
- * before writing to it.
- */
-public class SafeWriteNode extends SafeAccessNode implements StateSplit, Lowerable {
-
-    @Input private ValueNode value;
-    @Input(notDataflow = true) private FrameState stateAfter;
-
-    public FrameState stateAfter() {
-        return stateAfter;
-    }
-
-    public void setStateAfter(FrameState x) {
-        assert x == null || x.isAlive() : "frame state must be in a graph";
-        updateUsages(stateAfter, x);
-        stateAfter = x;
-    }
-
-    public boolean hasSideEffect() {
-        return true;
-    }
-
-    public SafeWriteNode(ValueNode object, ValueNode value, LocationNode location) {
-        super(object, location, StampFactory.forVoid());
-        this.value = value;
-    }
-
-    public ValueNode value() {
-        return value;
-    }
-
-    @Override
-    public void lower(LoweringTool tool) {
-        StructuredGraph graph = (StructuredGraph) graph();
-        ValueNode guard = tool.createNullCheckGuard(object());
-        WriteNode write = graph.add(new WriteNode(object(), value(), location()));
-        write.dependencies().add(guard);
-        graph.replaceFixedWithFixed(this, write);
-    }
-}
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/EliminatePartiallyRedundantGuardsPhase.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/EliminatePartiallyRedundantGuardsPhase.java	Thu Mar 28 16:58:44 2013 +0100
@@ -139,7 +139,7 @@
         }
         Graph graph = merge.graph();
         for (GuardNode guard : hits) {
-            PhiNode phi = graph.add(new PhiNode(PhiType.Guard, merge));
+            PhiNode phi = graph.add(new PhiNode(PhiType.Guard, merge, null));
             for (EndNode otherEnd : merge.forwardEnds()) {
                 phi.addInput(graph.unique(new GuardNode(guard.condition(), BeginNode.prevBegin(otherEnd), guard.reason(), guard.action(), guard.negated())));
             }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java	Thu Mar 28 16:58:44 2013 +0100
@@ -186,7 +186,7 @@
                         } else if (merged == null) {
                             merged = last;
                         } else {
-                            PhiNode phi = merge.graph().add(new PhiNode(PhiType.Memory, merge));
+                            PhiNode phi = merge.graph().add(new PhiNode(PhiType.Memory, merge, key));
                             for (int j = 0; j < mergedStatesCount; j++) {
                                 phi.addInput(merged);
                             }
@@ -232,7 +232,7 @@
 
             Map<Object, PhiNode> phis = new HashMap<>();
             for (Object location : modifiedLocations) {
-                PhiNode phi = loop.graph().add(new PhiNode(PhiType.Memory, loop));
+                PhiNode phi = loop.graph().add(new PhiNode(PhiType.Memory, loop, location));
                 phi.addInput(initialState.getLastLocationAccess(location));
                 phis.put(location, phi);
                 initialState.lastMemorySnapshot.put(location, phi);
@@ -254,7 +254,7 @@
                 for (Object location : modifiedLocations) {
                     ValueNode lastAccessAtExit = state.lastMemorySnapshot.get(location);
                     if (lastAccessAtExit != null) {
-                        state.lastMemorySnapshot.put(location, loop.graph().add(new ProxyNode(lastAccessAtExit, exit, PhiType.Memory)));
+                        state.lastMemorySnapshot.put(location, ProxyNode.forMemory(lastAccessAtExit, exit, location, (StructuredGraph) loop.graph()));
                     }
                 }
             }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Thu Mar 28 16:58:44 2013 +0100
@@ -57,13 +57,17 @@
     private final OptimisticOptimizations optimisticOpts;
     private CustomCanonicalizer customCanonicalizer;
 
+    private int inliningCount;
+
+    private int maxMethodPerInlining = Integer.MAX_VALUE;
+
     // Metrics
     private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed");
     private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered");
     private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize");
     private static final DebugMetric metricInliningRuns = Debug.metric("Runs");
 
-    public InliningPhase(GraalCodeCacheProvider runtime, Collection<Invoke> hints, Assumptions assumptions, GraphCache cache, PhasePlan plan, OptimisticOptimizations optimisticOpts) {
+    public InliningPhase(GraalCodeCacheProvider runtime, Map<Invoke, Double> hints, Assumptions assumptions, GraphCache cache, PhasePlan plan, OptimisticOptimizations optimisticOpts) {
         this(runtime, assumptions, cache, plan, createInliningPolicy(runtime, assumptions, optimisticOpts, hints), optimisticOpts);
     }
 
@@ -71,6 +75,10 @@
         this.customCanonicalizer = customCanonicalizer;
     }
 
+    public void setMaxMethodsPerInlining(int max) {
+        maxMethodPerInlining = max;
+    }
+
     public InliningPhase(GraalCodeCacheProvider runtime, Assumptions assumptions, GraphCache cache, PhasePlan plan, InliningPolicy inliningPolicy, OptimisticOptimizations optimisticOpts) {
         this.runtime = runtime;
         this.assumptions = assumptions;
@@ -80,6 +88,10 @@
         this.optimisticOpts = optimisticOpts;
     }
 
+    public int getInliningCount() {
+        return inliningCount;
+    }
+
     @Override
     protected void run(final StructuredGraph graph) {
         inliningPolicy.initialize(graph);
@@ -89,6 +101,7 @@
 
             if (candidate != null) {
                 boolean isWorthInlining = inliningPolicy.isWorthInlining(candidate);
+                isWorthInlining &= candidate.numberOfMethods() <= maxMethodPerInlining;
 
                 metricInliningConsidered.increment();
                 if (isWorthInlining) {
@@ -102,6 +115,7 @@
                         if (GraalOptions.OptCanonicalizer) {
                             new CanonicalizerPhase(runtime, assumptions, invokeUsages, mark, customCanonicalizer).apply(graph);
                         }
+                        inliningCount++;
                         metricInliningPerformed.increment();
                     } catch (BailoutException bailout) {
                         throw bailout;
@@ -164,9 +178,9 @@
     private static class GreedySizeBasedInliningDecision implements InliningDecision {
 
         private final GraalCodeCacheProvider runtime;
-        private final Collection<Invoke> hints;
+        private final Map<Invoke, Double> hints;
 
-        public GreedySizeBasedInliningDecision(GraalCodeCacheProvider runtime, Collection<Invoke> hints) {
+        public GreedySizeBasedInliningDecision(GraalCodeCacheProvider runtime, Map<Invoke, Double> hints) {
             this.runtime = runtime;
             this.hints = hints;
         }
@@ -185,9 +199,14 @@
                 return InliningUtil.logInlinedMethod(info, "intrinsic");
             }
 
-            int bytecodeSize = bytecodeCodeSize(info);
-            int complexity = compilationComplexity(info);
-            int compiledCodeSize = compiledCodeSize(info);
+            double bonus = 1;
+            if (hints != null && hints.containsKey(info.invoke())) {
+                bonus = hints.get(info.invoke());
+            }
+
+            int bytecodeSize = (int) (bytecodeCodeSize(info) / bonus);
+            int complexity = (int) (compilationComplexity(info) / bonus);
+            int compiledCodeSize = (int) (compiledCodeSize(info) / bonus);
             double relevance = info.invoke().inliningRelevance();
 
             /*
@@ -213,13 +232,12 @@
             int invokeUsages = countInvokeUsages(info);
             int moreSpecificArguments = countMoreSpecificArgumentInfo(info);
             int level = info.level();
-            boolean preferredInvoke = hints != null && hints.contains(info.invoke());
 
             // TODO (chaeubl): compute metric that is used to check if this method should be inlined
 
             return InliningUtil.logNotInlinedMethod(info,
-                            "(relevance=%f, bytecodes=%d, complexity=%d, codeSize=%d, probability=%f, transferredValues=%d, invokeUsages=%d, moreSpecificArguments=%d, level=%d, preferred=%b)",
-                            relevance, bytecodeSize, complexity, compiledCodeSize, probability, transferredValues, invokeUsages, moreSpecificArguments, level, preferredInvoke);
+                            "(relevance=%f, bytecodes=%d, complexity=%d, codeSize=%d, probability=%f, transferredValues=%d, invokeUsages=%d, moreSpecificArguments=%d, level=%d, bonus=%f)", relevance,
+                            bytecodeSize, complexity, compiledCodeSize, probability, transferredValues, invokeUsages, moreSpecificArguments, level, bonus);
         }
 
         private static boolean isTrivialInlining(int bytecodeSize, int complexity, int compiledCodeSize) {
@@ -329,16 +347,14 @@
     private static class CFInliningPolicy implements InliningPolicy {
 
         private final InliningDecision inliningDecision;
-        private final Collection<Invoke> hints;
         private final Assumptions assumptions;
         private final OptimisticOptimizations optimisticOpts;
         private final Deque<Invoke> sortedInvokes;
         private NodeBitMap visitedFixedNodes;
         private FixedNode invokePredecessor;
 
-        public CFInliningPolicy(InliningDecision inliningPolicy, Collection<Invoke> hints, Assumptions assumptions, OptimisticOptimizations optimisticOpts) {
+        public CFInliningPolicy(InliningDecision inliningPolicy, Assumptions assumptions, OptimisticOptimizations optimisticOpts) {
             this.inliningDecision = inliningPolicy;
-            this.hints = hints;
             this.assumptions = assumptions;
             this.optimisticOpts = optimisticOpts;
             this.sortedInvokes = new ArrayDeque<>();
@@ -371,9 +387,6 @@
         public void initialize(StructuredGraph graph) {
             visitedFixedNodes = graph.createNodeBitMap(true);
             scanGraphForInvokes(graph.start());
-            if (hints != null) {
-                sortedInvokes.retainAll(hints);
-            }
         }
 
         public void scanInvokes(Iterable<? extends Node> newNodes) {
@@ -500,8 +513,8 @@
         }
     }
 
-    private static InliningPolicy createInliningPolicy(GraalCodeCacheProvider runtime, Assumptions assumptions, OptimisticOptimizations optimisticOpts, Collection<Invoke> hints) {
+    private static InliningPolicy createInliningPolicy(GraalCodeCacheProvider runtime, Assumptions assumptions, OptimisticOptimizations optimisticOpts, Map<Invoke, Double> hints) {
         InliningDecision inliningDecision = new GreedySizeBasedInliningDecision(runtime, hints);
-        return new CFInliningPolicy(inliningDecision, hints, assumptions, optimisticOpts);
+        return new CFInliningPolicy(inliningDecision, assumptions, optimisticOpts);
     }
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ReadEliminationPhase.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ReadEliminationPhase.java	Thu Mar 28 16:58:44 2013 +0100
@@ -27,7 +27,6 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.phases.*;
 
@@ -86,7 +85,7 @@
         if (lastLocationAccess instanceof ProxyNode) {
             ProxyNode proxy = (ProxyNode) lastLocationAccess;
             ValueNode value = getValue(n, proxy.value(), nodeMap);
-            return lastLocationAccess.graph().add(new ProxyNode(value, proxy.proxyPoint(), PhiType.Value));
+            return ProxyNode.forValue(value, proxy.proxyPoint(), (StructuredGraph) lastLocationAccess.graph());
         }
         if (lastLocationAccess instanceof WriteNode) {
             return ((WriteNode) lastLocationAccess).value();
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java	Thu Mar 28 16:58:44 2013 +0100
@@ -128,13 +128,21 @@
         }
     };
 
+    private int tailDuplicationCount;
+
+    public int getTailDuplicationCount() {
+        return tailDuplicationCount;
+    }
+
     @Override
     protected void run(StructuredGraph graph) {
         // A snapshot is taken here, so that new MergeNode instances aren't considered for tail
         // duplication.
         for (MergeNode merge : graph.getNodes(MergeNode.class).snapshot()) {
             if (!(merge instanceof LoopBeginNode) && merge.probability() >= GraalOptions.TailDuplicationProbability) {
-                tailDuplicate(merge, DEFAULT_DECISION, null);
+                if (tailDuplicate(merge, DEFAULT_DECISION, null)) {
+                    tailDuplicationCount++;
+                }
             }
         }
     }
@@ -155,7 +163,7 @@
      *            {@link PiNode}, and is used to replace {@link PiNode#object()} with the
      *            {@link PiNode} in the duplicated branch that corresponds to the entry.
      */
-    public static void tailDuplicate(MergeNode merge, TailDuplicationDecision decision, List<PiNode> replacements) {
+    public static boolean tailDuplicate(MergeNode merge, TailDuplicationDecision decision, List<PiNode> replacements) {
         assert !(merge instanceof LoopBeginNode);
         assert replacements == null || replacements.size() == merge.forwardEndCount();
         FixedNode fixed = merge;
@@ -179,15 +187,18 @@
                 if (decision.doTransform(merge, fixedCount)) {
                     metricDuplicationEndPerformed.increment();
                     new DuplicationOperation(merge, replacements).duplicate();
+                    return true;
                 }
             } else if (merge.stateAfter() != null) {
                 metricDuplicationOther.increment();
                 if (decision.doTransform(merge, fixedCount)) {
                     metricDuplicationOtherPerformed.increment();
                     new DuplicationOperation(merge, replacements).duplicate();
+                    return true;
                 }
             }
         }
+        return false;
     }
 
     /**
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Thu Mar 28 16:58:44 2013 +0100
@@ -49,6 +49,7 @@
     public static boolean LimitInlinedRelevance              = true;
     public static float   BoostInliningForEscapeAnalysis     = 2f;
     public static float   RelevanceCapForInlining            = 1f;
+    public static boolean IterativeInlining                  = ____;
 
     public static int     TrivialBytecodeSize                = 10;
     public static int     NormalBytecodeSize                 = 150;
@@ -67,6 +68,8 @@
     public static int     EscapeAnalysisIterations           = 2;
     public static String  EscapeAnalyzeOnly                  = null;
     public static int     MaximumEscapeAnalysisArrayLength   = 32;
+    public static boolean PEAReadCache                       = ____;
+    public static boolean PEAInliningHints                   = ____;
 
     public static double  TailDuplicationProbability         = 0.5;
     public static int     TailDuplicationTrivialSize         = 1;
@@ -124,6 +127,8 @@
     public static String  LogFile                            = null;
     public static String  MethodFilter                       = null;
     public static boolean DumpOnError                        = ____;
+    public static boolean GenericDynamicCounters             = ____;
+    public static boolean BenchmarkDynamicCounters           = ____;
 
     // Ideal graph visualizer output settings
     public static boolean PrintBinaryGraphs                  = true;
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ReentrantBlockIterator.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ReentrantBlockIterator.java	Thu Mar 28 16:58:44 2013 +0100
@@ -41,7 +41,7 @@
 
         protected abstract StateT merge(MergeNode merge, List<StateT> states);
 
-        protected abstract StateT afterSplit(FixedNode node, StateT oldState);
+        protected abstract StateT cloneState(StateT oldState);
 
         protected abstract List<StateT> processLoop(Loop loop, StateT initialState);
     }
@@ -56,25 +56,31 @@
         LoopInfo<StateT> info = new LoopInfo<>();
         List<Block> predecessors = loop.header.getPredecessors();
         for (int i = 1; i < predecessors.size(); i++) {
-            info.endStates.add(blockEndStates.get(predecessors.get(i).getEndNode()));
+            StateT endState = blockEndStates.get(predecessors.get(i).getEndNode());
+            // make sure all end states are unique objects
+            info.endStates.add(closure.cloneState(endState));
         }
         for (Block loopExit : loop.exits) {
             assert loopExit.getPredecessorCount() == 1;
-            StateT exitState = blockEndStates.get(loopExit.getFirstPredecessor().getEndNode());
+            StateT exitState = blockEndStates.get(loopExit.getBeginNode());
             assert exitState != null;
-            info.exitStates.add(exitState);
+            // make sure all exit states are unique objects
+            info.exitStates.add(closure.cloneState(exitState));
         }
         return info;
     }
 
     public static <StateT> IdentityHashMap<FixedNode, StateT> apply(BlockIteratorClosure<StateT> closure, Block start, StateT initialState, Set<Block> boundary) {
         Deque<Block> blockQueue = new ArrayDeque<>();
-        IdentityHashMap<FixedNode, StateT> blockEndStates = new IdentityHashMap<>();
+        /*
+         * States are stored on EndNodes before merges, and on BeginNodes after ControlSplitNodes.
+         */
+        IdentityHashMap<FixedNode, StateT> states = new IdentityHashMap<>();
 
         StateT state = initialState;
         Block current = start;
 
-        do {
+        while (true) {
             if (boundary == null || boundary.contains(current)) {
                 closure.processBlock(current, state);
 
@@ -86,7 +92,7 @@
                         if (current.isLoopEnd()) {
                             // nothing to do... loop ends only lead to loop begins we've already
                             // visited
-                            blockEndStates.put(current.getEndNode(), state);
+                            states.put(current.getEndNode(), state);
                         } else {
                             // recurse into the loop
                             Loop loop = successor.getLoop();
@@ -98,14 +104,14 @@
                             int i = 0;
                             assert loop.exits.size() == exitStates.size();
                             for (Block exit : loop.exits) {
-                                blockEndStates.put(exit.getFirstPredecessor().getEndNode(), exitStates.get(i++));
+                                states.put(exit.getBeginNode(), exitStates.get(i++));
                                 blockQueue.addFirst(exit);
                             }
                         }
                     } else {
                         if (successor.getBeginNode() instanceof LoopExitNode) {
                             assert successor.getPredecessors().size() == 1;
-                            blockEndStates.put(current.getEndNode(), state);
+                            states.put(successor.getBeginNode(), state);
                             current = successor;
                             continue;
                         } else {
@@ -114,12 +120,12 @@
                                 EndNode end = (EndNode) current.getEndNode();
 
                                 // add the end node and see if the merge is ready for processing
-                                assert !blockEndStates.containsKey(end);
-                                blockEndStates.put(end, state);
+                                assert !states.containsKey(end);
+                                states.put(end, state);
                                 MergeNode merge = end.merge();
                                 boolean endsVisited = true;
                                 for (EndNode forwardEnd : merge.forwardEnds()) {
-                                    if (!blockEndStates.containsKey(forwardEnd)) {
+                                    if (!states.containsKey(forwardEnd)) {
                                         endsVisited = false;
                                         break;
                                     }
@@ -136,46 +142,34 @@
                     }
                 } else {
                     assert current.getSuccessors().size() > 1;
-                    blockEndStates.put(current.getEndNode(), state);
-                    for (Block block : current.getSuccessors()) {
-                        blockQueue.addFirst(block);
+                    for (int i = 0; i < current.getSuccessors().size(); i++) {
+                        Block successor = current.getSuccessors().get(i);
+                        blockQueue.addFirst(successor);
+                        states.put(successor.getBeginNode(), i == 0 ? state : closure.cloneState(state));
                     }
                 }
             }
 
             // get next queued block
             if (blockQueue.isEmpty()) {
-                current = null;
+                return states;
             } else {
-                int maxIterations = blockQueue.size();
-                while (maxIterations-- > 0) {
-                    current = blockQueue.removeFirst();
-                    if (current.getPredecessors().size() > 1) {
-                        MergeNode merge = (MergeNode) current.getBeginNode();
-                        ArrayList<StateT> states = new ArrayList<>(merge.forwardEndCount());
-                        for (int i = 0; i < merge.forwardEndCount(); i++) {
-                            StateT other = blockEndStates.get(merge.forwardEndAt(i));
-                            assert other != null;
-                            states.add(other);
-                        }
-                        state = closure.merge(merge, states);
-                        if (state != null) {
-                            break;
-                        } else {
-                            blockQueue.addLast(current);
-                            current = null;
-                        }
-                    } else {
-                        assert current.getPredecessors().size() == 1;
-                        assert current.getBeginNode().predecessor() != null;
-                        if (boundary == null || boundary.contains(current)) {
-                            state = closure.afterSplit(current.getBeginNode(), blockEndStates.get(current.getBeginNode().predecessor()));
-                            break;
-                        }
+                current = blockQueue.removeFirst();
+                if (current.getPredecessors().size() == 1) {
+                    state = states.get(current.getBeginNode());
+                } else {
+                    assert current.getPredecessors().size() > 1;
+                    MergeNode merge = (MergeNode) current.getBeginNode();
+                    ArrayList<StateT> mergedStates = new ArrayList<>(merge.forwardEndCount());
+                    for (int i = 0; i < merge.forwardEndCount(); i++) {
+                        StateT other = states.get(merge.forwardEndAt(i));
+                        assert other != null;
+                        mergedStates.add(other);
                     }
+                    state = closure.merge(merge, mergedStates);
                 }
+                assert state != null;
             }
-        } while (current != null);
-        return blockEndStates;
+        }
     }
 }
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Thu Mar 28 16:58:44 2013 +0100
@@ -100,7 +100,7 @@
         }
 
         @Override
-        protected HashSet<FloatingReadNode> afterSplit(FixedNode node, HashSet<FloatingReadNode> oldState) {
+        protected HashSet<FloatingReadNode> cloneState(HashSet<FloatingReadNode> oldState) {
             return new HashSet<>(oldState);
         }
 
@@ -580,41 +580,45 @@
     }
 
     private void addToEarliestSorting(Block b, ScheduledNode i, List<ScheduledNode> sortedInstructions, NodeBitMap visited) {
-        if (i == null || visited.isMarked(i) || cfg.getNodeToBlock().get(i) != b || i instanceof PhiNode || i instanceof LocalNode) {
-            return;
-        }
+        ScheduledNode instruction = i;
+        while (true) {
+            if (instruction == null || visited.isMarked(instruction) || cfg.getNodeToBlock().get(instruction) != b || instruction instanceof PhiNode || instruction instanceof LocalNode) {
+                return;
+            }
 
-        visited.mark(i);
-        for (Node usage : i.usages()) {
-            if (usage instanceof VirtualState) {
-                // only fixed nodes can have VirtualState -> no need to schedule them
-            } else {
-                if (i instanceof LoopExitNode && usage instanceof ProxyNode) {
-                    // value proxies should be scheduled before the loopexit, not after
+            visited.mark(instruction);
+            for (Node usage : instruction.usages()) {
+                if (usage instanceof VirtualState) {
+                    // only fixed nodes can have VirtualState -> no need to schedule them
                 } else {
-                    addToEarliestSorting(b, (ScheduledNode) usage, sortedInstructions, visited);
-                }
-            }
-        }
-
-        if (i instanceof BeginNode) {
-            ArrayList<ProxyNode> proxies = (i instanceof LoopExitNode) ? new ArrayList<ProxyNode>() : null;
-            for (ScheduledNode inBlock : blockToNodesMap.get(b)) {
-                if (!visited.isMarked(inBlock)) {
-                    if (inBlock instanceof ProxyNode) {
-                        proxies.add((ProxyNode) inBlock);
+                    if (instruction instanceof LoopExitNode && usage instanceof ProxyNode) {
+                        // value proxies should be scheduled before the loopexit, not after
                     } else {
-                        addToEarliestSorting(b, inBlock, sortedInstructions, visited);
+                        addToEarliestSorting(b, (ScheduledNode) usage, sortedInstructions, visited);
                     }
                 }
             }
-            sortedInstructions.add(i);
-            if (proxies != null) {
-                sortedInstructions.addAll(proxies);
+
+            if (instruction instanceof BeginNode) {
+                ArrayList<ProxyNode> proxies = (instruction instanceof LoopExitNode) ? new ArrayList<ProxyNode>() : null;
+                for (ScheduledNode inBlock : blockToNodesMap.get(b)) {
+                    if (!visited.isMarked(inBlock)) {
+                        if (inBlock instanceof ProxyNode) {
+                            proxies.add((ProxyNode) inBlock);
+                        } else {
+                            addToEarliestSorting(b, inBlock, sortedInstructions, visited);
+                        }
+                    }
+                }
+                sortedInstructions.add(instruction);
+                if (proxies != null) {
+                    sortedInstructions.addAll(proxies);
+                }
+                break;
+            } else {
+                sortedInstructions.add(instruction);
+                instruction = (ScheduledNode) instruction.predecessor();
             }
-        } else {
-            sortedInstructions.add(i);
-            addToEarliestSorting(b, (ScheduledNode) i.predecessor(), sortedInstructions, visited);
         }
     }
 }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java	Thu Mar 28 16:58:44 2013 +0100
@@ -56,6 +56,10 @@
         return targetMethod;
     }
 
+    public JavaType getReturnType() {
+        return returnType;
+    }
+
     @SuppressWarnings("unused")
     protected StructuredGraph getSnippetGraph(LoweringTool tool) {
         return null;
@@ -85,4 +89,19 @@
         invoke.setStateAfter(stateAfter());
         return invoke;
     }
+
+    protected void replaceSnippetInvokes(StructuredGraph snippetGraph) {
+        for (InvokeNode invoke : snippetGraph.getNodes(InvokeNode.class)) {
+            if (invoke.methodCallTarget().targetMethod() != getTargetMethod()) {
+                throw new GraalInternalError("unexpected invoke %s in snippet", getClass().getSimpleName());
+            }
+            if (invoke.stateAfter().bci == FrameState.INVALID_FRAMESTATE_BCI) {
+                InvokeNode newInvoke = snippetGraph.add(new InvokeNode(invoke.methodCallTarget(), getBci()));
+                newInvoke.setStateAfter(snippetGraph.add(new FrameState(FrameState.AFTER_BCI)));
+                snippetGraph.replaceFixedWithFixed(invoke, newInvoke);
+            } else {
+                assert invoke.stateAfter().bci == FrameState.AFTER_BCI : invoke;
+            }
+        }
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Thu Mar 28 16:58:44 2013 +0100
@@ -30,28 +30,104 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.virtual.nodes.*;
 
 class BlockState {
 
-    private final HashMap<VirtualObjectNode, ObjectState> objectStates = new HashMap<>();
-    private final HashMap<ValueNode, VirtualObjectNode> objectAliases = new HashMap<>();
-    private final HashMap<ValueNode, ValueNode> scalarAliases = new HashMap<>();
+    private final IdentityHashMap<VirtualObjectNode, ObjectState> objectStates = new IdentityHashMap<>();
+    private final IdentityHashMap<ValueNode, VirtualObjectNode> objectAliases;
+    private final IdentityHashMap<ValueNode, ValueNode> scalarAliases;
+    private final HashMap<ReadCacheEntry, ValueNode> readCache;
+
+    static class ReadCacheEntry {
+
+        public final Object identity;
+        public final ValueNode object;
+
+        public ReadCacheEntry(Object identity, ValueNode object) {
+            this.identity = identity;
+            this.object = object;
+        }
+
+        @Override
+        public int hashCode() {
+            int result = 31 + ((identity == null) ? 0 : identity.hashCode());
+            return 31 * result + ((object == null) ? 0 : object.hashCode());
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            ReadCacheEntry other = (ReadCacheEntry) obj;
+            return identity == other.identity && object == other.object;
+        }
+
+        @Override
+        public String toString() {
+            return object + ":" + identity;
+        }
+    }
 
     public BlockState() {
+        objectAliases = new IdentityHashMap<>();
+        scalarAliases = new IdentityHashMap<>();
+        readCache = new HashMap<>();
     }
 
     public BlockState(BlockState other) {
         for (Map.Entry<VirtualObjectNode, ObjectState> entry : other.objectStates.entrySet()) {
             objectStates.put(entry.getKey(), entry.getValue().cloneState());
         }
-        for (Map.Entry<ValueNode, VirtualObjectNode> entry : other.objectAliases.entrySet()) {
-            objectAliases.put(entry.getKey(), entry.getValue());
+        objectAliases = new IdentityHashMap<>(other.objectAliases);
+        scalarAliases = new IdentityHashMap<>(other.scalarAliases);
+        readCache = new HashMap<>(other.readCache);
+    }
+
+    public void addReadCache(ValueNode object, Object identity, ValueNode value) {
+        ValueNode cacheObject;
+        ObjectState obj = getObjectState(object);
+        if (obj != null) {
+            assert !obj.isVirtual();
+            cacheObject = obj.getMaterializedValue();
+        } else {
+            cacheObject = object;
         }
-        for (Map.Entry<ValueNode, ValueNode> entry : other.scalarAliases.entrySet()) {
-            scalarAliases.put(entry.getKey(), entry.getValue());
+        readCache.put(new ReadCacheEntry(identity, cacheObject), value);
+    }
+
+    public ValueNode getReadCache(ValueNode object, Object identity) {
+        ValueNode cacheObject;
+        ObjectState obj = getObjectState(object);
+        if (obj != null) {
+            assert !obj.isVirtual();
+            cacheObject = obj.getMaterializedValue();
+        } else {
+            cacheObject = object;
+        }
+        ValueNode cacheValue = readCache.get(new ReadCacheEntry(identity, cacheObject));
+        obj = getObjectState(cacheValue);
+        if (obj != null) {
+            assert !obj.isVirtual();
+            cacheValue = obj.getMaterializedValue();
+        } else {
+            cacheValue = getScalarAlias(cacheValue);
+        }
+        return cacheValue;
+    }
+
+    public void killReadCache(Object identity) {
+        if (identity == LocationNode.ANY_LOCATION) {
+            readCache.clear();
+        } else {
+            Iterator<Map.Entry<ReadCacheEntry, ValueNode>> iter = readCache.entrySet().iterator();
+            while (iter.hasNext()) {
+                Map.Entry<ReadCacheEntry, ValueNode> entry = iter.next();
+                if (entry.getKey().identity == identity) {
+                    iter.remove();
+                }
+            }
         }
     }
 
@@ -126,6 +202,13 @@
         }
         deferred.remove(virtual);
 
+        if (virtual instanceof VirtualInstanceNode) {
+            VirtualInstanceNode instance = (VirtualInstanceNode) virtual;
+            for (int i = 0; i < fieldState.length; i++) {
+                readCache.put(new ReadCacheEntry(instance.field(i), materialize), fieldState[i]);
+            }
+        }
+
         materializeEffects.addMaterialization(materialize, fixed, values);
     }
 
@@ -175,6 +258,63 @@
         return objectStates.toString();
     }
 
+    public void mergeReadCache(List<BlockState> states, MergeNode merge, GraphEffectList effects) {
+        for (Map.Entry<ReadCacheEntry, ValueNode> entry : states.get(0).readCache.entrySet()) {
+            ReadCacheEntry key = entry.getKey();
+            ValueNode value = entry.getValue();
+            boolean phi = false;
+            for (int i = 1; i < states.size(); i++) {
+                ValueNode otherValue = states.get(i).readCache.get(key);
+                if (otherValue == null) {
+                    value = null;
+                    phi = false;
+                    break;
+                }
+                if (!phi && otherValue != value) {
+                    phi = true;
+                }
+            }
+            if (phi) {
+                PhiNode phiNode = new PhiNode(value.kind(), merge);
+                effects.addFloatingNode(phiNode);
+                for (int i = 0; i < states.size(); i++) {
+                    effects.addPhiInput(phiNode, states.get(i).getReadCache(key.object, key.identity));
+                }
+                readCache.put(key, phiNode);
+            } else if (value != null) {
+                readCache.put(key, value);
+            }
+        }
+        for (PhiNode phi : merge.phis()) {
+            if (phi.kind() == Kind.Object) {
+                for (Map.Entry<ReadCacheEntry, ValueNode> entry : states.get(0).readCache.entrySet()) {
+                    if (entry.getKey().object == phi.valueAt(0)) {
+                        mergeReadCachePhi(phi, entry.getKey().identity, states, merge, effects);
+                    }
+                }
+
+            }
+        }
+    }
+
+    private void mergeReadCachePhi(PhiNode phi, Object identity, List<BlockState> states, MergeNode merge, GraphEffectList effects) {
+        ValueNode[] values = new ValueNode[phi.valueCount()];
+        for (int i = 0; i < phi.valueCount(); i++) {
+            ValueNode value = states.get(i).getReadCache(phi.valueAt(i), identity);
+            if (value == null) {
+                return;
+            }
+            values[i] = value;
+        }
+
+        PhiNode phiNode = new PhiNode(values[0].kind(), merge);
+        effects.addFloatingNode(phiNode);
+        for (int i = 0; i < values.length; i++) {
+            effects.addPhiInput(phiNode, values[i]);
+        }
+        readCache.put(new ReadCacheEntry(identity, phi), phiNode);
+    }
+
     public static BlockState meetAliases(List<BlockState> states) {
         BlockState newState = new BlockState();
 
@@ -201,6 +341,12 @@
                 }
             }
         }
+
         return newState;
     }
+
+    public Map<ReadCacheEntry, ValueNode> getReadCache() {
+        return readCache;
+    }
+
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectList.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectList.java	Thu Mar 28 16:58:44 2013 +0100
@@ -193,7 +193,7 @@
                 for (int i2 = 0; i2 < levelAt(i); i2++) {
                     str.append("    ");
                 }
-                str.append(effect).toString();
+                str.append(effect).append('\n');
             }
         }
         return str.toString();
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Thu Mar 28 16:58:44 2013 +0100
@@ -26,12 +26,55 @@
 
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.debug.*;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.virtual.nodes.*;
 
 public class GraphEffectList extends EffectList {
 
+    public void addCounterBefore(final String name, final int increment, final boolean addContext, final FixedNode position) {
+        if (!DynamicCounterNode.enabled) {
+            return;
+        }
+        add(new Effect() {
+
+            @Override
+            public String name() {
+                return "addCounterBefore";
+            }
+
+            @Override
+            public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
+                assert position.isAlive();
+                DynamicCounterNode node = graph.add(new DynamicCounterNode(name, increment, addContext));
+                graph.addBeforeFixed(position, node);
+                node.setProbability(position.probability());
+            }
+        });
+    }
+
+    public void addSurvivingCounterBefore(final String name, final int increment, final boolean addContext, final ValueNode checkedValue, final FixedNode position) {
+        if (!DynamicCounterNode.enabled) {
+            return;
+        }
+        add(new Effect() {
+
+            @Override
+            public String name() {
+                return "addSurvivingCounterBefore";
+            }
+
+            @Override
+            public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
+                assert position.isAlive();
+                DynamicCounterNode node = graph.add(new SurvivingCounterNode(name, increment, addContext, checkedValue));
+                graph.addBeforeFixed(position, node);
+                node.setProbability(position.probability());
+            }
+        });
+    }
+
     /**
      * Adds the given fixed node to the graph's control flow, before position (so that the original
      * predecessor of position will then be node's predecessor).
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/IterativeInliningPhase.java	Thu Mar 28 16:58:44 2013 +0100
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.virtual.phases.ea;
+
+import java.util.*;
+import java.util.concurrent.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+
+public class IterativeInliningPhase extends Phase {
+
+    private final PhasePlan plan;
+
+    private final GraalCodeCacheProvider runtime;
+    private final Assumptions assumptions;
+    private final GraphCache cache;
+    private final OptimisticOptimizations optimisticOpts;
+
+    public IterativeInliningPhase(GraalCodeCacheProvider runtime, Assumptions assumptions, GraphCache cache, PhasePlan plan, OptimisticOptimizations optimisticOpts) {
+        this.runtime = runtime;
+        this.assumptions = assumptions;
+        this.cache = cache;
+        this.plan = plan;
+        this.optimisticOpts = optimisticOpts;
+    }
+
+    public static final void trace(String format, Object... obj) {
+        if (GraalOptions.TraceEscapeAnalysis) {
+            Debug.log(format, obj);
+        }
+    }
+
+    @Override
+    protected void run(final StructuredGraph graph) {
+        runIterations(graph, true);
+        runIterations(graph, false);
+    }
+
+    private void runIterations(final StructuredGraph graph, final boolean simple) {
+        Boolean continueIteration = true;
+        for (int iteration = 0; iteration < GraalOptions.EscapeAnalysisIterations && continueIteration; iteration++) {
+            continueIteration = Debug.scope("iteration " + iteration, new Callable<Boolean>() {
+
+                @Override
+                public Boolean call() {
+                    boolean progress = false;
+                    PartialEscapeAnalysisPhase ea = new PartialEscapeAnalysisPhase(runtime, assumptions, false);
+                    ea.apply(graph);
+                    progress |= ea.hasChanged();
+
+                    Map<Invoke, Double> hints = GraalOptions.PEAInliningHints ? PartialEscapeAnalysisPhase.getHints(graph) : null;
+
+                    InliningPhase inlining = new InliningPhase(runtime, hints, assumptions, cache, plan, optimisticOpts);
+                    inlining.setMaxMethodsPerInlining(simple ? 1 : Integer.MAX_VALUE);
+                    inlining.apply(graph);
+                    progress |= inlining.getInliningCount() > 0;
+
+                    new DeadCodeEliminationPhase().apply(graph);
+
+                    if (GraalOptions.ConditionalElimination && GraalOptions.OptCanonicalizer) {
+                        new CanonicalizerPhase(runtime, assumptions).apply(graph);
+                        new IterativeConditionalEliminationPhase(runtime, assumptions).apply(graph);
+                    }
+
+                    return progress;
+                }
+            });
+        }
+    }
+}
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Thu Mar 28 16:58:44 2013 +0100
@@ -30,12 +30,15 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.debug.*;
+import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.common.CanonicalizerPhase.CustomCanonicalizer;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.schedule.*;
+import com.oracle.graal.virtual.nodes.*;
 import com.oracle.graal.virtual.phases.ea.EffectList.Effect;
 
 public class PartialEscapeAnalysisPhase extends Phase {
@@ -45,12 +48,18 @@
     private CustomCanonicalizer customCanonicalizer;
     private final boolean iterative;
 
+    private boolean changed;
+
     public PartialEscapeAnalysisPhase(MetaAccessProvider runtime, Assumptions assumptions, boolean iterative) {
         this.runtime = runtime;
         this.assumptions = assumptions;
         this.iterative = iterative;
     }
 
+    public boolean hasChanged() {
+        return changed;
+    }
+
     public void setCustomCanonicalizer(CustomCanonicalizer customCanonicalizer) {
         this.customCanonicalizer = customCanonicalizer;
     }
@@ -61,25 +70,23 @@
         }
     }
 
-    public static final void error(String format, Object... obj) {
-        System.out.print(String.format(format, obj));
-    }
-
     @Override
     protected void run(final StructuredGraph graph) {
         if (!matches(graph, GraalOptions.EscapeAnalyzeOnly)) {
             return;
         }
 
-        boolean analyzableNodes = false;
-        for (Node node : graph.getNodes()) {
-            if (node instanceof VirtualizableAllocation) {
-                analyzableNodes = true;
-                break;
+        if (!GraalOptions.PEAReadCache) {
+            boolean analyzableNodes = false;
+            for (Node node : graph.getNodes()) {
+                if (node instanceof VirtualizableAllocation) {
+                    analyzableNodes = true;
+                    break;
+                }
             }
-        }
-        if (!analyzableNodes) {
-            return;
+            if (!analyzableNodes) {
+                return;
+            }
         }
 
         Boolean continueIteration = true;
@@ -88,14 +95,16 @@
 
                 @Override
                 public Boolean call() {
+
                     SchedulePhase schedule = new SchedulePhase();
                     schedule.apply(graph, false);
                     PartialEscapeClosure closure = new PartialEscapeClosure(graph.createNodeBitMap(), schedule, runtime, assumptions);
                     ReentrantBlockIterator.apply(closure, schedule.getCFG().getStartBlock(), new BlockState(), null);
 
-                    if (closure.getNewVirtualObjectCount() == 0) {
+                    if (!closure.hasChanged()) {
                         return false;
                     }
+                    changed = true;
 
                     // apply the effects collected during the escape analysis iteration
                     ArrayList<Node> obsoleteNodes = new ArrayList<>();
@@ -104,20 +113,25 @@
                     }
                     trace("%s\n", closure.getEffects());
 
-                    Debug.dump(graph, "after PartialEscapeAnalysis");
+                    Debug.dump(graph, "after PartialEscapeAnalysis iteration");
                     assert noObsoleteNodes(graph, obsoleteNodes);
 
                     new DeadCodeEliminationPhase().apply(graph);
-                    if (!iterative) {
-                        return false;
-                    }
+
                     if (GraalOptions.OptCanonicalizer) {
                         new CanonicalizerPhase(runtime, assumptions, null, customCanonicalizer).apply(graph);
                     }
-                    return true;
+
+                    return iterative;
                 }
             });
         }
+
+        for (Node node : graph.getNodes()) {
+            if (node instanceof LoadFieldNode) {
+                DynamicCounterNode.addCounterBefore("load non-elim", 1, false, (FixedNode) node);
+            }
+        }
     }
 
     private static boolean matches(StructuredGraph graph, String filter) {
@@ -128,7 +142,7 @@
         return true;
     }
 
-    private static boolean noObsoleteNodes(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
+    static boolean noObsoleteNodes(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
         // helper code that determines the paths that keep obsolete nodes alive:
 
         NodeFlood flood = graph.createNodeFlood();
@@ -153,7 +167,7 @@
 
         for (Node node : obsoleteNodes) {
             if (node instanceof FixedNode) {
-                assert !flood.isMarked(node);
+                assert !flood.isMarked(node) : node;
             }
         }
 
@@ -182,10 +196,10 @@
         boolean success = true;
         for (Node node : obsoleteNodes) {
             if (flood.isMarked(node)) {
-                error("offending node path:");
+                System.out.print("offending node path:");
                 Node current = node;
                 while (current != null) {
-                    error(current.toString());
+                    System.out.println(current.toString());
                     current = path.get(current);
                     if (current != null && current instanceof FixedNode && !obsoleteNodes.contains(current)) {
                         break;
@@ -196,4 +210,39 @@
         }
         return success;
     }
+
+    public static Map<Invoke, Double> getHints(StructuredGraph graph) {
+        Map<Invoke, Double> hints = null;
+        for (MaterializeObjectNode materialize : graph.getNodes(MaterializeObjectNode.class)) {
+            double sum = 0;
+            double invokeSum = 0;
+            for (Node usage : materialize.usages()) {
+                if (usage instanceof FixedNode) {
+                    sum += ((FixedNode) usage).probability();
+                } else {
+                    if (usage instanceof MethodCallTargetNode) {
+                        invokeSum += ((MethodCallTargetNode) usage).invoke().probability();
+                    }
+                    for (Node secondLevelUage : materialize.usages()) {
+                        if (secondLevelUage instanceof FixedNode) {
+                            sum += ((FixedNode) secondLevelUage).probability();
+                        }
+                    }
+                }
+            }
+            // TODO(lstadler) get rid of this magic number
+            if (sum > 100 && invokeSum > 0) {
+                for (Node usage : materialize.usages()) {
+                    if (usage instanceof MethodCallTargetNode) {
+                        if (hints == null) {
+                            hints = new HashMap<>();
+                        }
+                        Invoke invoke = ((MethodCallTargetNode) usage).invoke();
+                        hints.put(invoke, sum / invokeSum);
+                    }
+                }
+            }
+        }
+        return hints;
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Thu Mar 28 16:58:23 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Thu Mar 28 16:58:44 2013 +0100
@@ -34,14 +34,18 @@
 import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.VirtualState.NodeClosure;
 import com.oracle.graal.nodes.cfg.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
 import com.oracle.graal.nodes.virtual.*;
+import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.LoopInfo;
 import com.oracle.graal.phases.schedule.*;
 import com.oracle.graal.virtual.nodes.*;
+import com.oracle.graal.virtual.phases.ea.BlockState.ReadCacheEntry;
 
 class PartialEscapeClosure extends BlockIteratorClosure<BlockState> {
 
@@ -53,6 +57,11 @@
     public static final DebugMetric METRIC_MATERIALIZATIONS_LOOP_END = Debug.metric("MaterializationsLoopEnd");
     public static final DebugMetric METRIC_ALLOCATION_REMOVED = Debug.metric("AllocationsRemoved");
 
+    public static final DebugMetric METRIC_STOREFIELD_RECORDED = Debug.metric("StoreFieldRecorded");
+    public static final DebugMetric METRIC_LOADFIELD_ELIMINATED = Debug.metric("LoadFieldEliminated");
+    public static final DebugMetric METRIC_LOADFIELD_NOT_ELIMINATED = Debug.metric("LoadFieldNotEliminated");
+    public static final DebugMetric METRIC_MEMORYCHECKOINT = Debug.metric("MemoryCheckpoint");
+
     private final NodeBitMap usages;
     private final SchedulePhase schedule;
 
@@ -60,12 +69,20 @@
 
     private final VirtualizerToolImpl tool;
 
+    private final Map<Invoke, Double> hints = new IdentityHashMap<>();
+
+    private boolean changed;
+
     public PartialEscapeClosure(NodeBitMap usages, SchedulePhase schedule, MetaAccessProvider metaAccess, Assumptions assumptions) {
         this.usages = usages;
         this.schedule = schedule;
         tool = new VirtualizerToolImpl(effects, usages, metaAccess, assumptions);
     }
 
+    public boolean hasChanged() {
+        return changed;
+    }
+
     public GraphEffectList getEffects() {
         return effects;
     }
@@ -74,6 +91,10 @@
         return tool.getNewVirtualObjectCount();
     }
 
+    public Map<Invoke, Double> getHints() {
+        return hints;
+    }
+
     @Override
     protected void processBlock(Block block, BlockState state) {
         trace("\nBlock: %s (", block);
@@ -81,27 +102,68 @@
 
         FixedWithNextNode lastFixedNode = null;
         for (Node node : nodeList) {
+            boolean deleted;
             if (usages.isMarked(node) || node instanceof VirtualizableAllocation) {
                 trace("[[%s]] ", node);
-                processNode((ValueNode) node, lastFixedNode == null ? null : lastFixedNode.next(), state);
+                deleted = processNode((ValueNode) node, lastFixedNode == null ? null : lastFixedNode.next(), state);
             } else {
                 trace("%s ", node);
+                deleted = false;
             }
+            if (GraalOptions.PEAReadCache) {
+                if (!deleted) {
+                    if (node instanceof StoreFieldNode) {
+                        METRIC_STOREFIELD_RECORDED.increment();
+                        StoreFieldNode store = (StoreFieldNode) node;
+                        ValueNode cachedValue = state.getReadCache(store.object(), store.field());
+                        state.killReadCache(store.field());
 
-            if (node instanceof FixedWithNextNode && node.isAlive()) {
+                        if (cachedValue == store.value()) {
+                            effects.addCounterBefore("store elim", 1, false, lastFixedNode.next());
+                            effects.deleteFixedNode(store);
+                            changed = true;
+                        } else {
+                            state.addReadCache(store.object(), store.field(), store.value());
+                        }
+                    } else if (node instanceof LoadFieldNode) {
+                        LoadFieldNode load = (LoadFieldNode) node;
+                        ValueNode cachedValue = state.getReadCache(load.object(), load.field());
+                        if (cachedValue != null) {
+                            METRIC_LOADFIELD_ELIMINATED.increment();
+                            effects.addCounterBefore("load elim", 1, false, lastFixedNode.next());
+                            effects.replaceAtUsages(load, cachedValue);
+                            state.addScalarAlias(load, cachedValue);
+                            changed = true;
+                        } else {
+                            METRIC_LOADFIELD_NOT_ELIMINATED.increment();
+                            state.addReadCache(load.object(), load.field(), load);
+                        }
+                    } else if (node instanceof MemoryCheckpoint) {
+                        METRIC_MEMORYCHECKOINT.increment();
+                        MemoryCheckpoint checkpoint = (MemoryCheckpoint) node;
+                        for (Object identity : checkpoint.getLocationIdentities()) {
+                            state.killReadCache(identity);
+                        }
+                    }
+                }
+            }
+            if (node instanceof FixedWithNextNode) {
                 lastFixedNode = (FixedWithNextNode) node;
             }
         }
         trace(")\n    end state: %s\n", state);
     }
 
-    private void processNode(final ValueNode node, FixedNode insertBefore, final BlockState state) {
+    private boolean processNode(final ValueNode node, FixedNode insertBefore, final BlockState state) {
         tool.reset(state, node);
         if (node instanceof Virtualizable) {
             ((Virtualizable) node).virtualize(tool);
         }
         if (tool.isDeleted()) {
-            return;
+            if (tool.isCustomAction() || !(node instanceof VirtualizableAllocation || node instanceof CyclicMaterializeStoreNode)) {
+                changed = true;
+            }
+            return true;
         }
         if (node instanceof StateSplit) {
             StateSplit split = (StateSplit) node;
@@ -178,15 +240,20 @@
             }
         }
         if (tool.isCustomAction()) {
-            return;
+            return false;
         }
         for (ValueNode input : node.inputs().filter(ValueNode.class)) {
             ObjectState obj = state.getObjectState(input);
             if (obj != null) {
+                if (obj.isVirtual() && node instanceof MethodCallTargetNode) {
+                    Invoke invoke = ((MethodCallTargetNode) node).invoke();
+                    hints.put(invoke, 5d);
+                }
                 trace("replacing input %s at %s: %s", input, node, obj);
                 replaceWithMaterialized(input, node, insertBefore, state, obj, METRIC_MATERIALIZATIONS_UNHANDLED);
             }
         }
+        return false;
     }
 
     private void ensureMaterialized(BlockState state, ObjectState obj, FixedNode materializeBefore, DebugMetric metric) {
@@ -207,7 +274,6 @@
 
     @Override
     protected BlockState merge(MergeNode merge, List<BlockState> states) {
-
         BlockState newState = BlockState.meetAliases(states);
 
         // Iterative processing:
@@ -300,13 +366,15 @@
                 }
             }
 
-            for (PhiNode phi : merge.phis().snapshot()) {
+            for (PhiNode phi : merge.phis()) {
                 if (usages.isMarked(phi) && phi.type() == PhiType.Value) {
                     materialized |= processPhi(newState, merge, phi, states);
                 }
             }
         } while (materialized);
 
+        newState.mergeReadCache(states, merge, effects);
+
         return newState;
     }
 
@@ -373,7 +441,7 @@
     }
 
     @Override
-    protected BlockState afterSplit(FixedNode node, BlockState oldState) {
+    protected BlockState cloneState(BlockState oldState) {
         return oldState.cloneState();
     }
 
@@ -421,11 +489,13 @@
             List<BlockState> loopEndStates = info.endStates;
             List<Block> predecessors = loop.header.getPredecessors();
             HashSet<VirtualObjectNode> additionalMaterializations = new HashSet<>();
+            HashSet<ReadCacheEntry> additionalKilledReads = new HashSet<>();
             int oldPhiCount = phis.size();
             for (int i = 1; i < predecessors.size(); i++) {
-                processLoopEnd(loop.loopBegin(), (LoopEndNode) predecessors.get(i).getEndNode(), state, loopEndStates.get(i - 1), successEffects, additionalMaterializations, phis);
+                processLoopEnd(loop.loopBegin(), (LoopEndNode) predecessors.get(i).getEndNode(), state, loopEndStates.get(i - 1), successEffects, additionalMaterializations, additionalKilledReads,
+                                phis);
             }
-            if (additionalMaterializations.isEmpty() && oldPhiCount == phis.size()) {
+            if (additionalMaterializations.isEmpty() && additionalKilledReads.isEmpty() && oldPhiCount == phis.size()) {
                 effects.addAll(successEffects);
 
                 assert info.exitStates.size() == loop.exits.size();
@@ -450,6 +520,9 @@
                         assert obj.getState() == EscapeState.Global;
                     }
                 }
+                for (ReadCacheEntry entry : additionalKilledReads) {
+                    initialState.getReadCache().remove(entry);
+                }
             }
         }
 
@@ -473,7 +546,7 @@
                     ObjectState valueObj = exitState.getObjectState(value);
                     if (valueObj == null) {
                         if ((value instanceof PhiNode && ((PhiNode) value).merge() == exitNode.loopBegin()) || initialObj == null || !initialObj.isVirtual() || initialObj.getEntry(i) != value) {
-                            ProxyNode proxy = new ProxyNode(value, exitNode, PhiType.Value);
+                            ProxyNode proxy = new ProxyNode(value, exitNode, PhiType.Value, null);
                             obj.setEntry(i, proxy);
                             effects.addFloatingNode(proxy);
                         }
@@ -483,7 +556,7 @@
                 if (initialObj == null || initialObj.isVirtual()) {
                     ProxyNode proxy = proxies.get(obj.virtual);
                     if (proxy == null) {
-                        proxy = new ProxyNode(obj.getMaterializedValue(), exitNode, PhiType.Value);
+                        proxy = new ProxyNode(obj.getMaterializedValue(), exitNode, PhiType.Value, null);
                         effects.addFloatingNode(proxy);
                     } else {
                         effects.replaceFirstInput(proxy, proxy.value(), obj.getMaterializedValue());
@@ -492,10 +565,18 @@
                     obj.updateMaterializedValue(proxy);
                 } else {
                     assert initialObj.getMaterializedValue() == obj.getMaterializedValue() : "materialized value is not allowed to change within loops: " + initialObj.getMaterializedValue() +
-                                    " vs. " + obj.getMaterializedValue();
+                                    " vs. " + obj.getMaterializedValue() + " at " + exitNode;
                 }
             }
         }
+
+        for (Map.Entry<ReadCacheEntry, ValueNode> entry : exitState.getReadCache().entrySet()) {
+            if (initialState.getReadCache().get(entry.getKey()) != entry.getValue()) {
+                ProxyNode proxy = new ProxyNode(exitState.getReadCache(entry.getKey().object, entry.getKey().identity), exitNode, PhiType.Value, null);
+                effects.addFloatingNode(proxy);
+                entry.setValue(proxy);
+            }
+        }
     }
 
     private final class PhiDesc {
@@ -530,7 +611,7 @@
     }
 
     private void processLoopEnd(LoopBeginNode loopBegin, LoopEndNode loopEnd, BlockState initialState, BlockState loopEndState, GraphEffectList successEffects,
-                    Set<VirtualObjectNode> additionalMaterializations, HashSet<PhiDesc> phis) {
+                    Set<VirtualObjectNode> additionalMaterializations, HashSet<ReadCacheEntry> additionalKilledReads, HashSet<PhiDesc> phis) {
         assert loopEnd.loopBegin() == loopBegin;
         boolean materialized;
         do {
@@ -573,7 +654,7 @@
                     }
                 }
             }
-            for (PhiNode phi : loopBegin.phis().snapshot()) {
+            for (PhiNode phi : loopBegin.phis()) {
                 if (usages.isMarked(phi) && phi.type() == PhiType.Value) {
                     ObjectState initialObj = initialState.getObjectState(phi.valueAt(0));
                     boolean initialMaterialized = initialObj == null || !initialObj.isVirtual();
@@ -662,5 +743,11 @@
                 }
             }
         }
+
+        for (Map.Entry<ReadCacheEntry, ValueNode> entry : initialState.getReadCache().entrySet()) {
+            if (loopEndState.getReadCache().get(entry.getKey()) != entry.getValue()) {
+                additionalKilledReads.add(entry.getKey());
+            }
+        }
     }
 }
--- a/mx/commands.py	Thu Mar 28 16:58:23 2013 +0100
+++ b/mx/commands.py	Thu Mar 28 16:58:44 2013 +0100
@@ -1025,7 +1025,7 @@
     vm = _vm
     if len(args) is 0:
         args = ['all']
-        
+    
     vmArgs = [arg for arg in args if arg.startswith('-')]
 
     def benchmarks_in_group(group):