changeset 12389:9d1a5d61cc11

Merge.
author Christian Humer <christian.humer@gmail.com>
date Wed, 02 Oct 2013 15:33:25 +0200
parents 96c1d057a5ed (current diff) cdff87c89c5f (diff)
children 5151a7588384
files
diffstat 27 files changed, 576 insertions(+), 474 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Wed Oct 02 15:33:25 2013 +0200
@@ -246,6 +246,16 @@
     public final int osThreadOffset = getUninitializedInt();
 
     /**
+     * The value of JavaThread::graal_counters_offset().
+     */
+    public final int graalCountersThreadOffset = getUninitializedInt();
+
+    /**
+     * The length of the JavaThread::_graal_counters array.
+     */
+    public final int graalCountersSize = getUninitializedInt();
+
+    /**
      * The value of OSThread::interrupted_offset().
      */
     public final int osThreadInterruptedOffset = getUninitializedInt();
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java	Wed Oct 02 15:33:25 2013 +0200
@@ -228,4 +228,9 @@
     void invalidateInstalledCode(HotSpotInstalledCode hotspotInstalledCode);
 
     boolean isTypeLinked(HotSpotResolvedObjectType hotSpotResolvedObjectType);
+
+    /**
+     * Collects the current values of all Graal benchmark counters, summed up over all threads.
+     */
+    long[] collectCounters();
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java	Wed Oct 02 15:33:25 2013 +0200
@@ -190,4 +190,6 @@
      * verified entry point of the given native method.
      */
     public static native Object executeCompiledMethodIntrinsic(Object arg1, Object arg2, Object arg3, HotSpotInstalledCode hotspotInstalledCode) throws InvalidInstalledCodeException;
+
+    public native long[] collectCounters();
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Wed Oct 02 15:33:25 2013 +0200
@@ -47,7 +47,6 @@
 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.nodes.spi.*;
 import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
@@ -79,11 +78,6 @@
         }
     };
 
-    @Option(help = "")
-    private static final OptionValue<Boolean> GenericDynamicCounters = new OptionValue<>(false);
-
-    @Option(help = "")
-    private static final OptionValue<String> BenchmarkDynamicCounters = new OptionValue<>(null);
     //@formatter:on
 
     private final HotSpotGraalRuntime graalRuntime;
@@ -234,28 +228,8 @@
             t.start();
         }
 
-        if (BenchmarkDynamicCounters.getValue() != null) {
-            String[] arguments = BenchmarkDynamicCounters.getValue().split(",");
-            if (arguments.length == 0 || (arguments.length % 3) != 0) {
-                throw new GraalInternalError("invalid arguments to BenchmarkDynamicCounters: (err|out),start,end,(err|out),start,end,... (~ matches multiple digits)");
-            }
-            for (int i = 0; i < arguments.length; i += 3) {
-                if (arguments[i].equals("err")) {
-                    System.setErr(new PrintStream(new BenchmarkCountersOutputStream(System.err, arguments[i + 1], arguments[i + 2])));
-                } else if (arguments[i].equals("out")) {
-                    System.setOut(new PrintStream(new BenchmarkCountersOutputStream(System.out, arguments[i + 1], arguments[i + 2])));
-                } else {
-                    throw new GraalInternalError("invalid arguments to BenchmarkDynamicCounters: err|out");
-                }
-                // dacapo: "err, starting =====, PASSED in "
-                // specjvm2008: "out,Iteration ~ (~s) begins: ,Iteration ~ (~s) ends:   "
-            }
-            DynamicCounterNode.excludedClassPrefix = "Lcom/oracle/graal/";
-            DynamicCounterNode.enabled = true;
-        }
-        if (GenericDynamicCounters.getValue()) {
-            DynamicCounterNode.enabled = true;
-        }
+        BenchmarkCounters.initialize(graalRuntime.getCompilerToVM());
+
         compilerStartTime = System.nanoTime();
     }
 
@@ -284,84 +258,6 @@
         }
     }
 
-    private final class BenchmarkCountersOutputStream extends CallbackOutputStream {
-
-        private long startTime;
-        private boolean waitingForEnd;
-
-        private BenchmarkCountersOutputStream(PrintStream delegate, String start, String end) {
-            super(delegate, new String[]{start, end, "\n"});
-        }
-
-        @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;
-            }
-        }
-    }
-
     /**
      * Take action related to entering a new execution phase.
      * 
@@ -528,9 +424,7 @@
         }
 
         SnippetCounter.printGroups(TTY.out().out());
-        if (GenericDynamicCounters.getValue()) {
-            DynamicCounterNode.dump(System.out, (System.nanoTime() - compilerStartTime) / 1000000000d);
-        }
+        BenchmarkCounters.shutdown(graalRuntime.getCompilerToVM(), compilerStartTime);
     }
 
     private void flattenChildren(DebugValueMap map, DebugValueMap globalMap) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/debug/BenchmarkCounters.java	Wed Oct 02 15:33:25 2013 +0200
@@ -0,0 +1,313 @@
+/*
+ * 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.hotspot.debug;
+
+import java.io.*;
+import java.text.*;
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+
+import sun.misc.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.bridge.*;
+import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.HeapAccess.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.debug.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.options.*;
+import com.oracle.graal.replacements.nodes.*;
+
+public class BenchmarkCounters {
+
+    static class Options {
+
+        //@formatter:off
+        @Option(help = "Turn on the benchmark counters, and displays the results on VM shutdown")
+        private static final OptionValue<Boolean> GenericDynamicCounters = new OptionValue<>(false);
+
+        @Option(help = "Turn on the benchmark counters, and listen for specific patterns on System.out/System.err:%n" +
+                       "Format: (err|out),start pattern,end pattern (~ matches multiple digits)%n" +
+                       "Examples:%n" +
+                       "  dacapo = 'err, starting =====, PASSED in'%n" +
+                       "  specjvm2008 = 'out,Iteration ~ (~s) begins:,Iteration ~ (~s) ends:'")
+        private static final OptionValue<String> BenchmarkDynamicCounters = new OptionValue<>(null);
+        //@formatter:on
+    }
+
+    private static final boolean DUMP_STATIC = false;
+
+    public static String excludedClassPrefix = null;
+    public static boolean enabled = false;
+
+    public static final ConcurrentHashMap<String, Integer> indexes = new ConcurrentHashMap<>();
+    public static final ArrayList<String> groups = new ArrayList<>();
+    public static long[] delta;
+    public static final ArrayList<AtomicLong> staticCounters = new ArrayList<>();
+
+    public static int getIndex(DynamicCounterNode counter) {
+        if (!enabled) {
+            throw new GraalInternalError("counter nodes shouldn't exist when counters are not enabled");
+        }
+        String name = counter.getName();
+        String group = counter.getGroup();
+        name = counter.isWithContext() ? name + " @ " + counter.graph().graphId() + ":" + MetaUtil.format("%h.%n", counter.graph().method()) + "#" + group : name + "#" + group;
+        Integer index = indexes.get(name);
+        if (index == null) {
+            synchronized (BenchmarkCounters.class) {
+                index = indexes.get(name);
+                if (index == null) {
+                    index = indexes.size();
+                    indexes.put(name, index);
+                    groups.add(group);
+                    staticCounters.add(new AtomicLong());
+                }
+            }
+        }
+        assert groups.get(index).equals(group) : "mismatching groups: " + groups.get(index) + " vs. " + group;
+        if (counter.getIncrement().isConstant()) {
+            staticCounters.get(index).addAndGet(counter.getIncrement().asConstant().asLong());
+        }
+        return index;
+    }
+
+    public static synchronized void dump(PrintStream out, double seconds, long[] counters) {
+        if (!staticCounters.isEmpty()) {
+            out.println("====== dynamic counters (" + staticCounters.size() + " in total) ======");
+            for (String group : new TreeSet<>(groups)) {
+                if (group != null) {
+                    if (DUMP_STATIC) {
+                        dumpCounters(out, seconds, counters, true, group);
+                    }
+                    dumpCounters(out, seconds, counters, false, group);
+                }
+            }
+            out.println("============================");
+
+            clear(counters);
+        }
+    }
+
+    public static synchronized void clear(long[] counters) {
+        delta = counters;
+    }
+
+    private static synchronized void dumpCounters(PrintStream out, double seconds, long[] counters, boolean staticCounter, String group) {
+        TreeMap<Long, String> sorted = new TreeMap<>();
+
+        long[] array;
+        if (staticCounter) {
+            array = new long[indexes.size()];
+            for (int i = 0; i < array.length; i++) {
+                array[i] = staticCounters.get(i).get();
+            }
+        } else {
+            array = counters.clone();
+            for (int i = 0; i < array.length; i++) {
+                array[i] -= delta[i];
+            }
+        }
+        long sum = 0;
+        for (Map.Entry<String, Integer> entry : indexes.entrySet()) {
+            int index = entry.getValue();
+            if (groups.get(index).equals(group)) {
+                sum += array[index];
+                sorted.put(array[index] * array.length + index, entry.getKey().substring(0, entry.getKey().length() - group.length() - 1));
+            }
+        }
+
+        if (sum > 0) {
+            NumberFormat format = NumberFormat.getInstance(Locale.US);
+            long cutoff = sorted.size() < 10 ? 1 : Math.max(1, sum / 100);
+            if (staticCounter) {
+                out.println("=========== " + group + " static counters: ");
+                for (Map.Entry<Long, String> entry : sorted.entrySet()) {
+                    long counter = entry.getKey() / array.length;
+                    if (counter >= cutoff) {
+                        out.println(format.format(counter) + " \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue());
+                    }
+                }
+                out.println(sum + ": total");
+            } else {
+                if (group.startsWith("~")) {
+                    out.println("=========== " + group + " dynamic counters");
+                    for (Map.Entry<Long, String> entry : sorted.entrySet()) {
+                        long counter = entry.getKey() / array.length;
+                        if (counter >= cutoff) {
+                            out.println(format.format(counter) + " \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue());
+                        }
+                    }
+                    out.println(format.format(sum) + ": total");
+                } else {
+                    out.println("=========== " + group + " dynamic counters, time = " + seconds + " s");
+                    for (Map.Entry<Long, String> entry : sorted.entrySet()) {
+                        long counter = entry.getKey() / array.length;
+                        if (counter >= cutoff) {
+                            out.println(format.format((long) (counter / seconds)) + "/s \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue());
+                        }
+                    }
+                    out.println(format.format((long) (sum / seconds)) + "/s: total");
+                }
+            }
+        }
+    }
+
+    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;
+            }
+        }
+    }
+
+    public static void initialize(final CompilerToVM compilerToVM) {
+        final class BenchmarkCountersOutputStream extends CallbackOutputStream {
+
+            private long startTime;
+            private boolean waitingForEnd;
+
+            private BenchmarkCountersOutputStream(PrintStream delegate, String start, String end) {
+                super(delegate, new String[]{start, end, "\n"});
+            }
+
+            @Override
+            protected void patternFound(int index) {
+                switch (index) {
+                    case 0:
+                        startTime = System.nanoTime();
+                        BenchmarkCounters.clear(compilerToVM.collectCounters());
+                        break;
+                    case 1:
+                        waitingForEnd = true;
+                        break;
+                    case 2:
+                        if (waitingForEnd) {
+                            waitingForEnd = false;
+                            BenchmarkCounters.dump(delegate, (System.nanoTime() - startTime) / 1000000000d, compilerToVM.collectCounters());
+                        }
+                        break;
+                }
+            }
+        }
+
+        if (Options.BenchmarkDynamicCounters.getValue() != null) {
+            String[] arguments = Options.BenchmarkDynamicCounters.getValue().split(",");
+            if (arguments.length == 0 || (arguments.length % 3) != 0) {
+                throw new GraalInternalError("invalid arguments to BenchmarkDynamicCounters: (err|out),start,end,(err|out),start,end,... (~ matches multiple digits)");
+            }
+            for (int i = 0; i < arguments.length; i += 3) {
+                if (arguments[i].equals("err")) {
+                    System.setErr(new PrintStream(new BenchmarkCountersOutputStream(System.err, arguments[i + 1], arguments[i + 2])));
+                } else if (arguments[i].equals("out")) {
+                    System.setOut(new PrintStream(new BenchmarkCountersOutputStream(System.out, arguments[i + 1], arguments[i + 2])));
+                } else {
+                    throw new GraalInternalError("invalid arguments to BenchmarkDynamicCounters: err|out");
+                }
+            }
+            excludedClassPrefix = "Lcom/oracle/graal/";
+            enabled = true;
+        }
+        if (Options.GenericDynamicCounters.getValue()) {
+            enabled = true;
+        }
+        if (Options.GenericDynamicCounters.getValue() || Options.BenchmarkDynamicCounters.getValue() != null) {
+            clear(compilerToVM.collectCounters());
+        }
+    }
+
+    public static void shutdown(CompilerToVM compilerToVM, long compilerStartTime) {
+        if (Options.GenericDynamicCounters.getValue()) {
+            dump(System.out, (System.nanoTime() - compilerStartTime) / 1000000000d, compilerToVM.collectCounters());
+        }
+    }
+
+    public static void lower(DynamicCounterNode counter, HotSpotRuntime runtime) {
+        StructuredGraph graph = counter.graph();
+        if (excludedClassPrefix == null || !counter.graph().method().getDeclaringClass().getName().startsWith(excludedClassPrefix)) {
+            HotSpotVMConfig config = runtime.config;
+
+            ReadRegisterNode thread = graph.add(new ReadRegisterNode(runtime.threadRegister(), runtime.getTarget().wordKind, true, false));
+
+            int index = BenchmarkCounters.getIndex(counter);
+            if (index >= config.graalCountersSize) {
+                throw new GraalInternalError("too many counters, reduce number of counters or increase GRAAL_COUNTERS_SIZE (current value: " + config.graalCountersSize + ")");
+            }
+            ConstantLocationNode location = ConstantLocationNode.create(LocationIdentity.ANY_LOCATION, Kind.Long, config.graalCountersThreadOffset + Unsafe.ARRAY_LONG_INDEX_SCALE * index, graph);
+            ReadNode read = graph.add(new ReadNode(thread, location, StampFactory.forKind(Kind.Long), BarrierType.NONE, false));
+            IntegerAddNode add = graph.unique(new IntegerAddNode(Kind.Long, read, counter.getIncrement()));
+            WriteNode write = graph.add(new WriteNode(thread, add, location, BarrierType.NONE, false));
+
+            graph.addBeforeFixed(counter, thread);
+            graph.addBeforeFixed(counter, read);
+            graph.addBeforeFixed(counter, write);
+        }
+        graph.removeFixed(counter);
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Wed Oct 02 15:33:25 2013 +0200
@@ -74,6 +74,7 @@
 import com.oracle.graal.hotspot.HotSpotForeignCallLinkage.Transition;
 import com.oracle.graal.hotspot.bridge.*;
 import com.oracle.graal.hotspot.bridge.CompilerToVM.CodeInstallResult;
+import com.oracle.graal.hotspot.debug.*;
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.hotspot.phases.*;
 import com.oracle.graal.hotspot.replacements.*;
@@ -82,6 +83,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.HeapAccess.BarrierType;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.debug.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
@@ -788,6 +790,10 @@
                 osrStart.replaceAtUsages(newStart);
                 osrStart.safeDelete();
             }
+        } else if (n instanceof DynamicCounterNode) {
+            if (graph.getGuardsStage() == StructuredGraph.GuardsStage.AFTER_FSA) {
+                BenchmarkCounters.lower((DynamicCounterNode) n, this);
+            }
         } else if (n instanceof CheckCastDynamicNode) {
             checkcastDynamicSnippets.lower((CheckCastDynamicNode) n);
         } else if (n instanceof InstanceOfNode) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/DynamicCounterNode.java	Wed Oct 02 15:33:25 2013 +0200
@@ -22,200 +22,61 @@
  */
 package com.oracle.graal.nodes.debug;
 
-import java.io.*;
-import java.util.*;
-
-import sun.misc.*;
-
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.HeapAccess.BarrierType;
-import com.oracle.graal.nodes.calc.*;
-import com.oracle.graal.nodes.extended.*;
-import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 
 /**
  * This node can be used to add a counter to the code that will estimate the dynamic number of calls
  * by adding an increment to the compiled code. This should of course only be used for
- * debugging/testing purposes, and is not 100% accurate (because of concurrency issues while
- * accessing the counters).
+ * debugging/testing purposes.
  * 
- * A unique counter will be created for each unique String passed to the constructor.
+ * A unique counter will be created for each unique name passed to the constructor. Depending on the
+ * value of withContext, the name of the root method is added to the counter's name.
  */
 public class DynamicCounterNode extends FixedWithNextNode implements Lowerable {
 
-    private static final int MAX_COUNTERS = 10 * 1024;
-    public static final long[] COUNTERS = new long[MAX_COUNTERS];
-    private static final long[] STATIC_COUNTERS = new long[MAX_COUNTERS];
-    private static final String[] GROUPS = new String[MAX_COUNTERS];
-    private static final HashMap<String, Integer> INDEXES = new HashMap<>();
-    public static String excludedClassPrefix = null;
-    public static boolean enabled = false;
+    @Input private ValueNode increment;
 
     private final String name;
     private final String group;
-    private final long increment;
-    private final boolean addContext;
+    private final boolean withContext;
 
-    public DynamicCounterNode(String name, String group, long increment, boolean addContext) {
+    public DynamicCounterNode(String name, String group, ValueNode increment, boolean withContext) {
         super(StampFactory.forVoid());
-        if (!enabled) {
-            throw new GraalInternalError("dynamic counters not enabled");
-        }
         this.name = name;
         this.group = group;
         this.increment = increment;
-        this.addContext = addContext;
+        this.withContext = withContext;
+    }
+
+    public ValueNode getIncrement() {
+        return increment;
     }
 
     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();
-            INDEXES.put(name, index);
-            if (index >= MAX_COUNTERS) {
-                throw new GraalInternalError("too many dynamic counters");
-            }
-            return index;
-        } else {
-            return index;
-        }
-    }
-
-    public static synchronized void dump(PrintStream out, double seconds) {
-        for (String group : new HashSet<>(Arrays.asList(GROUPS))) {
-            if (group != null) {
-                dumpCounters(out, seconds, true, group);
-                dumpCounters(out, seconds, false, group);
-            }
-        }
-        out.println("============================");
-
-        clear();
+    public String getGroup() {
+        return group;
     }
 
-    private static void dumpCounters(PrintStream out, double seconds, boolean staticCounter, String group) {
-        TreeMap<Long, String> sorted = new TreeMap<>();
-
-        long[] array = staticCounter ? STATIC_COUNTERS : COUNTERS;
-        long sum = 0;
-        for (Map.Entry<String, Integer> entry : INDEXES.entrySet()) {
-            int index = entry.getValue();
-            if (GROUPS[index].equals(group)) {
-                sum += array[index];
-                sorted.put(array[index] * MAX_COUNTERS + index, entry.getKey());
-            }
-        }
-
-        if (sum > 0) {
-            long cutoff = sum / 1000;
-            long sum2 = 0;
-            if (staticCounter) {
-                out.println("=========== " + group + " static counters: ");
-                for (Map.Entry<Long, String> entry : sorted.entrySet()) {
-                    long counter = entry.getKey() / MAX_COUNTERS;
-                    sum2 += counter;
-                    if (sum2 >= cutoff) {
-                        out.println(counter + " \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue());
-                    }
-                }
-                out.println(sum + ": total");
-            } else {
-                if (group.startsWith("~")) {
-                    out.println("=========== " + group + " dynamic counters");
-                    for (Map.Entry<Long, String> entry : sorted.entrySet()) {
-                        long counter = entry.getKey() / MAX_COUNTERS;
-                        sum2 += counter;
-                        if (sum2 >= cutoff) {
-                            out.println(counter + " \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue());
-                        }
-                    }
-                    out.println(sum + "/s: total");
-                } else {
-                    out.println("=========== " + group + " dynamic counters, time = " + seconds + " s");
-                    for (Map.Entry<Long, String> entry : sorted.entrySet()) {
-                        long counter = entry.getKey() / MAX_COUNTERS;
-                        sum2 += counter;
-                        if (sum2 >= cutoff) {
-                            out.println((long) (counter / seconds) + "/s \t" + ((counter * 200 + 1) / sum / 2) + "% \t" + entry.getValue());
-                        }
-                    }
-                    out.println((long) (sum / seconds) + "/s: total");
-                }
-            }
-        }
-    }
-
-    public static void clear() {
-        Arrays.fill(COUNTERS, 0);
+    public boolean isWithContext() {
+        return withContext;
     }
 
     @Override
     public void lower(LoweringTool tool) {
-        if (!enabled) {
-            throw new GraalInternalError("counter nodes shouldn't exist when not enabled");
-        }
-        if (excludedClassPrefix == null || !graph().method().getDeclaringClass().getName().startsWith(excludedClassPrefix)) {
-            int index = addContext ? getIndex(name + " @ " + MetaUtil.format("%h.%n", graph().method())) : getIndex(name);
-            STATIC_COUNTERS[index] += increment;
-            GROUPS[index] = group;
-
-            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);
-            load.lower(tool);
-            store.lower(tool);
-        }
-        graph().removeFixed(this);
+        tool.getRuntime().lower(this, tool);
     }
 
-    public static void addLowLevel(String group, String name, long increment, boolean addContext, FixedNode position, MetaAccessProvider runtime) {
-        if (!enabled) {
-            throw new GraalInternalError("counter nodes shouldn't exist when not enabled");
-        }
+    public static void addCounterBefore(String group, String name, long increment, boolean withContext, FixedNode position) {
         StructuredGraph graph = position.graph();
-        if (excludedClassPrefix == null || !graph.method().getDeclaringClass().getName().startsWith(excludedClassPrefix)) {
-            int index = addContext ? getIndex(name + " @ " + MetaUtil.format("%h.%n", graph.method())) : getIndex(name);
-            STATIC_COUNTERS[index] += increment;
-            GROUPS[index] = group;
-
-            ConstantNode arrayConstant = ConstantNode.forObject(COUNTERS, runtime, graph);
-            ConstantLocationNode location = ConstantLocationNode.create(NamedLocationIdentity.getArrayLocation(Kind.Long), Kind.Long, Unsafe.ARRAY_LONG_BASE_OFFSET + Unsafe.ARRAY_LONG_INDEX_SCALE *
-                            index, graph);
-            ReadNode read = graph.add(new ReadNode(arrayConstant, location, StampFactory.forKind(Kind.Long), BarrierType.NONE, false));
-            IntegerAddNode add = graph.add(new IntegerAddNode(Kind.Long, read, ConstantNode.forLong(increment, graph)));
-            WriteNode write = graph.add(new WriteNode(arrayConstant, add, location, BarrierType.NONE, false));
-
-            graph.addBeforeFixed(position, read);
-            graph.addBeforeFixed(position, write);
-        }
+        graph.addBeforeFixed(position, position.graph().add(new DynamicCounterNode(name, group, ConstantNode.forLong(increment, position.graph()), withContext)));
     }
 
-    public static void addCounterBefore(String group, String name, long increment, boolean addContext, FixedNode position) {
-        if (enabled) {
-            StructuredGraph graph = position.graph();
-            DynamicCounterNode counter = graph.add(new DynamicCounterNode(name, group, increment, addContext));
-            graph.addBeforeFixed(position, counter);
-        }
-    }
+    @NodeIntrinsic
+    public static native void counter(@ConstantNodeParameter String name, @ConstantNodeParameter String group, long increment, @ConstantNodeParameter boolean addContext);
+
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/SurvivingCounterNode.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/SurvivingCounterNode.java	Wed Oct 02 15:33:25 2013 +0200
@@ -35,7 +35,7 @@
 
     @Input private ValueNode checkedValue;
 
-    public SurvivingCounterNode(String group, String name, long increment, boolean addContext, ValueNode checkedValue) {
+    public SurvivingCounterNode(String group, String name, ValueNode increment, boolean addContext, ValueNode checkedValue) {
         super(group, name, increment, addContext);
         this.checkedValue = checkedValue;
     }
@@ -55,4 +55,10 @@
             tool.delete();
         }
     }
+
+    public static void addCounterBefore(String group, String name, long increment, boolean addContext, ValueNode checkedValue, FixedNode position) {
+        StructuredGraph graph = position.graph();
+        SurvivingCounterNode counter = graph.add(new SurvivingCounterNode(name, group, ConstantNode.forLong(increment, graph), addContext, checkedValue));
+        graph.addBeforeFixed(position, counter);
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Virtualizable.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Virtualizable.java	Wed Oct 02 15:33:25 2013 +0200
@@ -34,7 +34,7 @@
 public interface Virtualizable {
 
     public static enum EscapeState {
-        Virtual, ThreadLocal, Global
+        Virtual, Materialized
     }
 
     public abstract static class State {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Wed Oct 02 15:33:25 2013 +0200
@@ -87,15 +87,6 @@
      */
     void setVirtualEntry(State state, int index, ValueNode value);
 
-    /**
-     * Queries the current state of the given value: if it was materialized or not.
-     * 
-     * @param value the value whose state should be queried.
-     * @return the materialized value (usually a MaterializeObjectNode or a {@link PhiNode}) if it
-     *         was materialized, null otherwise.
-     */
-    ValueNode getMaterializedValue(ValueNode value);
-
     // scalar replacement
 
     /**
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectList.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectList.java	Wed Oct 02 15:33:25 2013 +0200
@@ -30,8 +30,7 @@
 
 /**
  * An {@link EffectList} can be used to maintain a list of {@link Effect}s and backtrack to a
- * previous state by truncating the list. It can also maintain a level for each effect, which helps
- * in creating a string representation for the list.
+ * previous state by truncating the list.
  */
 public class EffectList implements Iterable<EffectList.Effect> {
 
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsBlockState.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsBlockState.java	Wed Oct 02 15:33:25 2013 +0200
@@ -24,48 +24,14 @@
 
 import java.util.*;
 
-import com.oracle.graal.nodes.*;
-
 public abstract class EffectsBlockState<T extends EffectsBlockState<T>> {
 
-    protected final IdentityHashMap<ValueNode, ValueNode> scalarAliases;
-
-    protected EffectsBlockState() {
-        scalarAliases = new IdentityHashMap<>();
-    }
-
-    protected EffectsBlockState(EffectsBlockState<T> other) {
-        scalarAliases = new IdentityHashMap<>(other.scalarAliases);
-    }
-
-    public void addScalarAlias(ValueNode alias, ValueNode value) {
-        scalarAliases.put(alias, value);
-    }
-
-    public ValueNode getScalarAlias(ValueNode alias) {
-        ValueNode result = scalarAliases.get(alias);
-        return result == null ? alias : result;
-    }
-
     @Override
     public String toString() {
-        return "Scalar Aliases: " + scalarAliases.toString();
+        return "";
     }
 
-    public void meetAliases(List<T> states) {
-        scalarAliases.putAll(states.get(0).scalarAliases);
-        for (int i = 1; i < states.size(); i++) {
-            EffectsBlockState<T> state = states.get(i);
-            meetMaps(scalarAliases, state.scalarAliases);
-        }
-    }
-
-    public boolean equivalentTo(T other) {
-        if (this == other) {
-            return true;
-        }
-        return scalarAliases.equals(other.scalarAliases);
-    }
+    protected abstract boolean equivalentTo(T other);
 
     protected static <K, V> boolean compareMaps(Map<K, V> left, Map<K, V> right) {
         if (left.size() != right.size()) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java	Wed Oct 02 15:33:25 2013 +0200
@@ -30,6 +30,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
+import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.LoopInfo;
@@ -40,6 +41,7 @@
 
     private final SchedulePhase schedule;
 
+    protected final NodeMap<ValueNode> aliases;
     protected final BlockMap<GraphEffectList> blockEffects;
     private final IdentityHashMap<Loop, GraphEffectList> loopMergeEffects = new IdentityHashMap<>();
 
@@ -47,6 +49,7 @@
 
     public EffectsClosure(SchedulePhase schedule) {
         this.schedule = schedule;
+        this.aliases = schedule.getCFG().graph.createNodeMap();
         this.blockEffects = new BlockMap<>(schedule.getCFG());
         for (Block block : schedule.getCFG().getBlocks()) {
             blockEffects.put(block, new GraphEffectList());
@@ -118,6 +121,7 @@
         GraphEffectList effects = blockEffects.get(block);
         FixedWithNextNode lastFixedNode = null;
         for (Node node : schedule.getBlockToNodesMap().get(block)) {
+            aliases.set(node, null);
             changed |= processNode(node, state, effects, lastFixedNode);
             if (node instanceof FixedWithNextNode) {
                 lastFixedNode = (FixedWithNextNode) node;
@@ -202,9 +206,11 @@
             this.afterMergeEffects = new GraphEffectList();
         }
 
+        /**
+         * @param states the states that should be merged.
+         */
         protected void merge(List<BlockT> states) {
             newState = getInitialState();
-            newState.meetAliases(states);
             mergeEffects.clear();
             afterMergeEffects.clear();
         }
@@ -213,4 +219,18 @@
         protected void commitEnds(List<BlockT> states) {
         }
     }
+
+    public void addScalarAlias(ValueNode node, ValueNode alias) {
+        assert !(alias instanceof VirtualObjectNode);
+        aliases.set(node, alias);
+    }
+
+    public ValueNode getScalarAlias(ValueNode node) {
+        assert !(node instanceof VirtualObjectNode);
+        if (node == null || !node.isAlive() || aliases.isNew(node)) {
+            return node;
+        }
+        ValueNode result = aliases.get(node);
+        return (result == null || result instanceof VirtualObjectNode) ? node : result;
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Wed Oct 02 15:33:25 2013 +0200
@@ -24,7 +24,6 @@
 
 import java.util.*;
 
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
@@ -35,9 +34,6 @@
 public class GraphEffectList extends EffectList {
 
     public void addCounterBefore(final String group, final String name, final int increment, final boolean addContext, final FixedNode position) {
-        if (!DynamicCounterNode.enabled) {
-            return;
-        }
         add(new Effect() {
 
             @Override
@@ -48,16 +44,12 @@
             @Override
             public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
                 assert position.isAlive();
-                DynamicCounterNode node = graph.add(new DynamicCounterNode(group, name, increment, addContext));
-                graph.addBeforeFixed(position, node);
+                DynamicCounterNode.addCounterBefore(group, name, increment, addContext, position);
             }
         });
     }
 
     public void addSurvivingCounterBefore(final String group, final String name, final int increment, final boolean addContext, final ValueNode checkedValue, final FixedNode position) {
-        if (!DynamicCounterNode.enabled) {
-            return;
-        }
         add(new Effect() {
 
             @Override
@@ -68,8 +60,7 @@
             @Override
             public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
                 assert position.isAlive();
-                DynamicCounterNode node = graph.add(new SurvivingCounterNode(group, name, increment, addContext, checkedValue));
-                graph.addBeforeFixed(position, node);
+                SurvivingCounterNode.addCounterBefore(group, name, increment, addContext, checkedValue, position);
             }
         });
     }
@@ -368,24 +359,4 @@
             }
         });
     }
-
-    public void addLowLevelCounterBefore(final String group, final String name, final int increment, final boolean addContext, final FixedNode position, final MetaAccessProvider runtime) {
-        add(new Effect() {
-
-            @Override
-            public String name() {
-                return "addLowLevelCounterBefore";
-            }
-
-            @Override
-            public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
-                DynamicCounterNode.addLowLevel(group, name, increment, addContext, position, runtime);
-            }
-
-            @Override
-            public boolean isVisible() {
-                return true;
-            }
-        });
-    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Wed Oct 02 15:33:25 2013 +0200
@@ -137,7 +137,7 @@
     }
 
     public void escape(ValueNode materialized, EscapeState newState) {
-        assert state == EscapeState.Virtual || (state == EscapeState.ThreadLocal && newState == EscapeState.Global);
+        assert state == EscapeState.Virtual && newState == EscapeState.Materialized;
         state = newState;
         materializedValue = materialized;
         entries = null;
@@ -146,7 +146,7 @@
 
     @Override
     public ValueNode getMaterializedValue() {
-        assert state == EscapeState.ThreadLocal || state == EscapeState.Global;
+        assert state == EscapeState.Materialized;
         return materializedValue;
     }
 
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationBlockState.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationBlockState.java	Wed Oct 02 15:33:25 2013 +0200
@@ -92,9 +92,9 @@
         return super.equivalentTo(other);
     }
 
-    public void addReadCache(ValueNode object, ResolvedJavaField identity, ValueNode value) {
+    public void addReadCache(ValueNode object, ResolvedJavaField identity, ValueNode value, PartialEscapeClosure<?> closure) {
         ValueNode cacheObject;
-        ObjectState obj = getObjectState(object);
+        ObjectState obj = closure.getObjectState(this, object);
         if (obj != null) {
             assert !obj.isVirtual();
             cacheObject = obj.getMaterializedValue();
@@ -104,9 +104,9 @@
         readCache.put(new ReadCacheEntry(identity, cacheObject), value);
     }
 
-    public ValueNode getReadCache(ValueNode object, ResolvedJavaField identity) {
+    public ValueNode getReadCache(ValueNode object, ResolvedJavaField identity, PartialEscapeClosure<?> closure) {
         ValueNode cacheObject;
-        ObjectState obj = getObjectState(object);
+        ObjectState obj = closure.getObjectState(this, object);
         if (obj != null) {
             assert !obj.isVirtual();
             cacheObject = obj.getMaterializedValue();
@@ -114,13 +114,13 @@
             cacheObject = object;
         }
         ValueNode cacheValue = readCache.get(new ReadCacheEntry(identity, cacheObject));
-        obj = getObjectState(cacheValue);
+        obj = closure.getObjectState(this, cacheValue);
         if (obj != null) {
             assert !obj.isVirtual();
             cacheValue = obj.getMaterializedValue();
         } else {
             // assert !scalarAliases.containsKey(cacheValue);
-            cacheValue = getScalarAlias(cacheValue);
+            cacheValue = closure.getScalarAlias(cacheValue);
         }
         return cacheValue;
     }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationClosure.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationClosure.java	Wed Oct 02 15:33:25 2013 +0200
@@ -57,13 +57,13 @@
                 LoadFieldNode load = (LoadFieldNode) node;
                 if (!load.isVolatile()) {
                     ValueNode object = GraphUtil.unproxify(load.object());
-                    ValueNode cachedValue = state.getReadCache(object, load.field());
+                    ValueNode cachedValue = state.getReadCache(object, load.field(), this);
                     if (cachedValue != null) {
                         effects.replaceAtUsages(load, cachedValue);
-                        state.addScalarAlias(load, cachedValue);
+                        addScalarAlias(load, cachedValue);
                         deleted = true;
                     } else {
-                        state.addReadCache(object, load.field(), load);
+                        state.addReadCache(object, load.field(), load, this);
                     }
                 } else {
                     processIdentity(state, ANY_LOCATION);
@@ -72,15 +72,15 @@
                 StoreFieldNode store = (StoreFieldNode) node;
                 if (!store.isVolatile()) {
                     ValueNode object = GraphUtil.unproxify(store.object());
-                    ValueNode cachedValue = state.getReadCache(object, store.field());
+                    ValueNode cachedValue = state.getReadCache(object, store.field(), this);
 
-                    ValueNode value = state.getScalarAlias(store.value());
+                    ValueNode value = getScalarAlias(store.value());
                     if (GraphUtil.unproxify(value) == GraphUtil.unproxify(cachedValue)) {
                         effects.deleteFixedNode(store);
                         deleted = true;
                     }
                     state.killReadCache(store.field());
-                    state.addReadCache(object, store.field(), value);
+                    state.addReadCache(object, store.field(), value, this);
                 } else {
                     processIdentity(state, ANY_LOCATION);
                 }
@@ -112,7 +112,7 @@
 
         for (Map.Entry<ReadCacheEntry, ValueNode> entry : exitState.getReadCache().entrySet()) {
             if (initialState.getReadCache().get(entry.getKey()) != entry.getValue()) {
-                ValueNode value = exitState.getReadCache(entry.getKey().object, entry.getKey().identity);
+                ValueNode value = exitState.getReadCache(entry.getKey().object, entry.getKey().identity, this);
                 if (!(value instanceof ProxyNode) || ((ProxyNode) value).proxyPoint() != exitNode) {
                     ProxyNode proxy = new ProxyNode(value, exitNode, PhiType.Value, null);
                     effects.addFloatingNode(proxy, "readCacheProxy");
@@ -165,7 +165,7 @@
                     PhiNode phiNode = getCachedPhi(entry, value.kind());
                     mergeEffects.addFloatingNode(phiNode, "mergeReadCache");
                     for (int i = 0; i < states.size(); i++) {
-                        afterMergeEffects.addPhiInput(phiNode, states.get(i).getReadCache(key.object, key.identity));
+                        afterMergeEffects.addPhiInput(phiNode, states.get(i).getReadCache(key.object, key.identity, PEReadEliminationClosure.this));
                     }
                     newState.readCache.put(key, phiNode);
                 } else if (value != null) {
@@ -187,7 +187,7 @@
         private void mergeReadCachePhi(PhiNode phi, ResolvedJavaField identity, List<PEReadEliminationBlockState> states) {
             ValueNode[] values = new ValueNode[phi.valueCount()];
             for (int i = 0; i < phi.valueCount(); i++) {
-                ValueNode value = states.get(i).getReadCache(phi.valueAt(i), identity);
+                ValueNode value = states.get(i).getReadCache(phi.valueAt(i), identity, PEReadEliminationClosure.this);
                 if (value == null) {
                     return;
                 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeBlockState.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeBlockState.java	Wed Oct 02 15:33:25 2013 +0200
@@ -24,7 +24,6 @@
 
 import java.util.*;
 
-import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
 import com.oracle.graal.nodes.virtual.*;
@@ -32,7 +31,6 @@
 public abstract class PartialEscapeBlockState<T extends PartialEscapeBlockState<T>> extends EffectsBlockState<T> {
 
     protected final IdentityHashMap<VirtualObjectNode, ObjectState> objectStates = new IdentityHashMap<>();
-    protected final IdentityHashMap<ValueNode, VirtualObjectNode> objectAliases;
 
     /**
      * Final subclass of PartialEscapeBlockState, for performance and to make everything behave
@@ -49,15 +47,12 @@
     }
 
     protected PartialEscapeBlockState() {
-        objectAliases = new IdentityHashMap<>();
     }
 
     protected PartialEscapeBlockState(PartialEscapeBlockState<T> other) {
-        super(other);
         for (Map.Entry<VirtualObjectNode, ObjectState> entry : other.objectStates.entrySet()) {
             objectStates.put(entry.getKey(), entry.getValue().cloneState());
         }
-        objectAliases = new IdentityHashMap<>(other.objectAliases);
     }
 
     public ObjectState getObjectState(VirtualObjectNode object) {
@@ -69,11 +64,6 @@
         return objectStates.get(object);
     }
 
-    public ObjectState getObjectState(ValueNode value) {
-        VirtualObjectNode object = objectAliases.get(value);
-        return object == null ? null : getObjectState(object);
-    }
-
     public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, EscapeState state, GraphEffectList materializeEffects) {
         PartialEscapeClosure.METRIC_MATERIALIZATIONS.increment();
         List<AllocatedObjectNode> objects = new ArrayList<>(2);
@@ -100,8 +90,8 @@
                 values.add(null);
             }
             for (int i = 0; i < entries.length; i++) {
-                ObjectState entryObj = getObjectState(entries[i]);
-                if (entryObj != null) {
+                if (entries[i] instanceof VirtualObjectNode) {
+                    ObjectState entryObj = getObjectState((VirtualObjectNode) entries[i]);
                     if (entryObj.isVirtual()) {
                         materializeWithCommit(fixed, entryObj.getVirtualObject(), objects, locks, values, otherAllocations, state);
                     }
@@ -122,26 +112,6 @@
         VirtualUtil.trace("materialized %s as %s with values %s", virtual, representation, values);
     }
 
-    void addAndMarkAlias(VirtualObjectNode virtual, ValueNode node, NodeBitMap usages) {
-        objectAliases.put(node, virtual);
-        if (node.isAlive()) {
-            for (Node usage : node.usages()) {
-                markVirtualUsages(usage, usages);
-            }
-        }
-    }
-
-    private void markVirtualUsages(Node node, NodeBitMap usages) {
-        if (!usages.isNew(node)) {
-            usages.mark(node);
-        }
-        if (node instanceof VirtualState) {
-            for (Node usage : node.usages()) {
-                markVirtualUsages(usage, usages);
-            }
-        }
-    }
-
     public void addObject(VirtualObjectNode virtual, ObjectState state) {
         objectStates.put(virtual, state);
     }
@@ -150,30 +120,18 @@
         return objectStates.values();
     }
 
-    public Collection<VirtualObjectNode> getVirtualObjects() {
-        return objectAliases.values();
+    public Set<VirtualObjectNode> getVirtualObjects() {
+        return objectStates.keySet();
     }
 
     @Override
     public String toString() {
-        return super.toString() + ", Object Aliases: " + objectAliases + ", Object States: " + objectStates;
-    }
-
-    @Override
-    public void meetAliases(List<T> states) {
-        super.meetAliases(states);
-        objectAliases.putAll(states.get(0).objectAliases);
-        for (int i = 1; i < states.size(); i++) {
-            meetMaps(objectAliases, states.get(i).objectAliases);
-        }
+        return super.toString() + ", Object States: " + objectStates;
     }
 
     @Override
     public boolean equivalentTo(T other) {
-        if (!compareMaps(objectAliases, other.objectAliases) || !compareMaps(objectStates, other.objectStates)) {
-            return false;
-        }
-        return super.equivalentTo(other);
+        return compareMaps(objectStates, other.objectStates);
     }
 
     protected static <K, V> boolean compareMaps(Map<K, V> left, Map<K, V> right) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Wed Oct 02 15:33:25 2013 +0200
@@ -79,7 +79,7 @@
     public PartialEscapeClosure(SchedulePhase schedule, MetaAccessProvider metaAccess, Assumptions assumptions) {
         super(schedule);
         this.usages = schedule.getCFG().graph.createNodeBitMap();
-        this.tool = new VirtualizerToolImpl(usages, metaAccess, assumptions);
+        this.tool = new VirtualizerToolImpl(metaAccess, assumptions, this);
     }
 
     public Map<Invoke, Double> getHints() {
@@ -129,7 +129,7 @@
 
                         @Override
                         public void apply(Node usage, ValueNode value) {
-                            ObjectState valueObj = state.getObjectState(value);
+                            ObjectState valueObj = getObjectState(state, value);
                             if (valueObj != null) {
                                 virtual.add(valueObj);
                                 effects.replaceFirstInput(usage, value, valueObj.virtual);
@@ -158,8 +158,8 @@
                         ObjectState obj = queue.removeLast();
                         if (obj.isVirtual()) {
                             for (ValueNode field : obj.getEntries()) {
-                                ObjectState fieldObj = state.getObjectState(field);
-                                if (fieldObj != null) {
+                                if (field instanceof VirtualObjectNode) {
+                                    ObjectState fieldObj = state.getObjectState((VirtualObjectNode) field);
                                     if (fieldObj.isVirtual() && !virtual.contains(fieldObj)) {
                                         virtual.add(fieldObj);
                                         queue.addLast(fieldObj);
@@ -173,7 +173,7 @@
                         if (obj.isVirtual()) {
                             ValueNode[] fieldState = obj.getEntries().clone();
                             for (int i = 0; i < fieldState.length; i++) {
-                                ObjectState valueObj = state.getObjectState(fieldState[i]);
+                                ObjectState valueObj = getObjectState(state, fieldState[i]);
                                 if (valueObj != null) {
                                     if (valueObj.isVirtual()) {
                                         fieldState[i] = valueObj.virtual;
@@ -191,7 +191,7 @@
                 }
             }
             for (ValueNode input : node.inputs().filter(ValueNode.class)) {
-                ObjectState obj = state.getObjectState(input);
+                ObjectState obj = getObjectState(state, input);
                 if (obj != null) {
                     if (obj.isVirtual() && node instanceof MethodCallTargetNode) {
                         Invoke invoke = ((MethodCallTargetNode) node).invoke();
@@ -205,18 +205,18 @@
         return false;
     }
 
-    private static void ensureMaterialized(PartialEscapeBlockState state, ObjectState obj, FixedNode materializeBefore, GraphEffectList effects, DebugMetric metric) {
+    private void ensureMaterialized(BlockT state, ObjectState obj, FixedNode materializeBefore, GraphEffectList effects, DebugMetric metric) {
         assert obj != null;
         if (obj.getState() == EscapeState.Virtual) {
             metric.increment();
-            state.materializeBefore(materializeBefore, obj.virtual, EscapeState.Global, effects);
+            state.materializeBefore(materializeBefore, obj.virtual, EscapeState.Materialized, effects);
         } else {
-            assert obj.getState() == EscapeState.Global;
+            assert obj.getState() == EscapeState.Materialized;
         }
         assert !obj.isVirtual();
     }
 
-    private static void replaceWithMaterialized(ValueNode value, Node usage, FixedNode materializeBefore, PartialEscapeBlockState state, ObjectState obj, GraphEffectList effects, DebugMetric metric) {
+    private void replaceWithMaterialized(ValueNode value, Node usage, FixedNode materializeBefore, BlockT state, ObjectState obj, GraphEffectList effects, DebugMetric metric) {
         ensureMaterialized(state, obj, materializeBefore, effects, metric);
         effects.replaceFirstInput(usage, value, obj.getMaterializedValue());
     }
@@ -226,7 +226,7 @@
         HashMap<VirtualObjectNode, ProxyNode> proxies = new HashMap<>();
 
         for (ProxyNode proxy : exitNode.proxies()) {
-            ObjectState obj = exitState.getObjectState(proxy.value());
+            ObjectState obj = getObjectState(exitState, proxy.value());
             if (obj != null) {
                 proxies.put(obj.virtual, proxy);
             }
@@ -236,8 +236,7 @@
             if (obj.isVirtual()) {
                 for (int i = 0; i < obj.getEntries().length; i++) {
                     ValueNode value = obj.getEntry(i);
-                    ObjectState valueObj = exitState.getObjectState(value);
-                    if (valueObj == null) {
+                    if (!(value instanceof VirtualObjectNode)) {
                         if (exitNode.loopBegin().isPhiAtMerge(value) || initialObj == null || !initialObj.isVirtual() || initialObj.getEntry(i) != value) {
                             ProxyNode proxy = new ProxyNode(value, exitNode, PhiType.Value, null);
                             obj.setEntry(i, proxy);
@@ -327,16 +326,21 @@
              * and so on.
              */
 
-            HashSet<VirtualObjectNode> virtualObjects = new HashSet<>(newState.getVirtualObjects());
+            HashSet<VirtualObjectNode> virtualObjTemp = new HashSet<>(states.get(0).getVirtualObjects());
+            for (int i = 1; i < states.size(); i++) {
+                virtualObjTemp.retainAll(states.get(i).getVirtualObjects());
+            }
+
             boolean materialized;
             do {
                 mergeEffects.clear();
                 afterMergeEffects.clear();
                 materialized = false;
-                for (VirtualObjectNode object : virtualObjects) {
+                for (VirtualObjectNode object : virtualObjTemp) {
                     ObjectState[] objStates = new ObjectState[states.size()];
                     for (int i = 0; i < states.size(); i++) {
-                        objStates[i] = states.get(i).getObjectState(object);
+                        objStates[i] = states.get(i).getObjectStateOptional(object);
+                        assert objStates[i] != null;
                     }
                     int virtual = 0;
                     ObjectState startObj = objStates[0];
@@ -359,16 +363,16 @@
                             PhiNode materializedValuePhi = getCachedPhi(object, Kind.Object);
                             mergeEffects.addFloatingNode(materializedValuePhi, "materializedPhi");
                             for (int i = 0; i < states.size(); i++) {
-                                PartialEscapeBlockState state = states.get(i);
+                                BlockT state = states.get(i);
                                 ObjectState obj = objStates[i];
                                 materialized |= obj.isVirtual();
                                 Block predecessor = mergeBlock.getPredecessors().get(i);
                                 ensureMaterialized(state, obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
                                 afterMergeEffects.addPhiInput(materializedValuePhi, obj.getMaterializedValue());
                             }
-                            newState.addObject(object, new ObjectState(object, materializedValuePhi, EscapeState.Global, null));
+                            newState.addObject(object, new ObjectState(object, materializedValuePhi, EscapeState.Materialized, null));
                         } else {
-                            newState.addObject(object, new ObjectState(object, singleValue, EscapeState.Global, null));
+                            newState.addObject(object, new ObjectState(object, singleValue, EscapeState.Materialized, null));
                         }
                     } else {
                         assert virtual == states.size();
@@ -396,8 +400,8 @@
                                         break outer;
                                     }
                                     ValueNode[] fields = objStates[i].getEntries();
-                                    ObjectState obj = states.get(i).getObjectState(fields[index]);
-                                    if (obj != null) {
+                                    if (fields[index] instanceof VirtualObjectNode) {
+                                        ObjectState obj = states.get(i).getObjectState((VirtualObjectNode) fields[index]);
                                         materialized |= obj.isVirtual();
                                         Block predecessor = mergeBlock.getPredecessors().get(i);
                                         ensureMaterialized(states.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
@@ -421,6 +425,7 @@
         }
 
         private boolean processPhi(PhiNode phi, List<BlockT> states) {
+            aliases.set(phi, null);
             assert states.size() == phi.valueCount();
             int virtualInputs = 0;
             boolean materialized = false;
@@ -430,7 +435,7 @@
             boolean hasIdentity = false;
             for (int i = 0; i < phi.valueCount(); i++) {
                 ValueNode value = phi.valueAt(i);
-                ObjectState obj = states.get(i).getObjectState(value);
+                ObjectState obj = getObjectState(states.get(i), value);
                 if (obj != null) {
                     if (obj.isVirtual()) {
                         virtualInputs++;
@@ -460,10 +465,10 @@
                 // nothing to do...
             } else if (virtualInputs == phi.valueCount()) {
                 if (sameObject != null) {
-                    newState.addAndMarkAlias(sameObject, phi, usages);
+                    addAndMarkAlias(sameObject, phi);
                 } else if (sameType != null && sameEntryCount != -1) {
                     if (!hasIdentity) {
-                        VirtualObjectNode virtual = getValueObjectVirtual(phi, states.get(0).getObjectState(phi.valueAt(0)).virtual);
+                        VirtualObjectNode virtual = getValueObjectVirtual(phi, getObjectState(states.get(0), phi.valueAt(0)).virtual);
 
                         PhiNode[] phis = getValueObjectMergePhis(phi, virtual.entryCount());
                         for (int i = 0; i < virtual.entryCount(); i++) {
@@ -473,13 +478,13 @@
                             }
                             mergeEffects.addFloatingNode(phis[i], "valueObjectPhi");
                             for (int i2 = 0; i2 < phi.valueCount(); i2++) {
-                                afterMergeEffects.addPhiInput(phis[i], states.get(i2).getObjectState(phi.valueAt(i2)).getEntry(i));
+                                afterMergeEffects.addPhiInput(phis[i], getObjectState(states.get(i2), phi.valueAt(i2)).getEntry(i));
                             }
                         }
                         mergeEffects.addFloatingNode(virtual, "valueObjectNode");
                         newState.addObject(virtual, new ObjectState(virtual, Arrays.copyOf(phis, phis.length, ValueNode[].class), EscapeState.Virtual, null));
-                        newState.addAndMarkAlias(virtual, virtual, usages);
-                        newState.addAndMarkAlias(virtual, phi, usages);
+                        addAndMarkAlias(virtual, virtual);
+                        addAndMarkAlias(virtual, phi);
                     } else {
                         materialize = true;
                     }
@@ -493,7 +498,7 @@
             if (materialize) {
                 for (int i = 0; i < phi.valueCount(); i++) {
                     ValueNode value = phi.valueAt(i);
-                    ObjectState obj = states.get(i).getObjectState(value);
+                    ObjectState obj = getObjectState(states.get(i), value);
                     if (obj != null) {
                         materialized |= obj.isVirtual();
                         Block predecessor = mergeBlock.getPredecessors().get(i);
@@ -504,4 +509,39 @@
             return materialized;
         }
     }
+
+    public ObjectState getObjectState(PartialEscapeBlockState<?> state, ValueNode value) {
+        if (value == null) {
+            return null;
+        }
+        if (value.isAlive() && !aliases.isNew(value)) {
+            ValueNode object = aliases.get(value);
+            return object instanceof VirtualObjectNode ? state.getObjectStateOptional((VirtualObjectNode) object) : null;
+        } else {
+            if (value instanceof VirtualObjectNode) {
+                return state.getObjectStateOptional((VirtualObjectNode) value);
+            }
+            return null;
+        }
+    }
+
+    void addAndMarkAlias(VirtualObjectNode virtual, ValueNode node) {
+        if (node.isAlive()) {
+            aliases.set(node, virtual);
+            for (Node usage : node.usages()) {
+                markVirtualUsages(usage);
+            }
+        }
+    }
+
+    private void markVirtualUsages(Node node) {
+        if (!usages.isNew(node)) {
+            usages.mark(node);
+        }
+        if (node instanceof VirtualState) {
+            for (Node usage : node.usages()) {
+                markVirtualUsages(usage);
+            }
+        }
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ReadEliminationBlockState.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ReadEliminationBlockState.java	Wed Oct 02 15:33:25 2013 +0200
@@ -103,7 +103,6 @@
     }
 
     public ReadEliminationBlockState(ReadEliminationBlockState other) {
-        super(other);
         readCache = new HashMap<>(other.readCache);
     }
 
@@ -114,10 +113,7 @@
 
     @Override
     public boolean equivalentTo(ReadEliminationBlockState other) {
-        if (!compareMapsNoSize(readCache, other.readCache)) {
-            return false;
-        }
-        return super.equivalentTo(other);
+        return compareMapsNoSize(readCache, other.readCache);
     }
 
     public void addCacheEntry(CacheEntry<?> identifier, ValueNode value) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ReadEliminationClosure.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ReadEliminationClosure.java	Wed Oct 02 15:33:25 2013 +0200
@@ -61,7 +61,7 @@
                 ValueNode cachedValue = state.getCacheEntry(identifier);
                 if (cachedValue != null) {
                     effects.replaceAtUsages(load, cachedValue);
-                    state.addScalarAlias(load, cachedValue);
+                    addScalarAlias(load, cachedValue);
                     deleted = true;
                 } else {
                     state.addCacheEntry(identifier, load);
@@ -76,7 +76,7 @@
                 LoadCacheEntry identifier = new LoadCacheEntry(object, store.field());
                 ValueNode cachedValue = state.getCacheEntry(identifier);
 
-                ValueNode value = state.getScalarAlias(store.value());
+                ValueNode value = getScalarAlias(store.value());
                 if (GraphUtil.unproxify(value) == GraphUtil.unproxify(cachedValue)) {
                     effects.deleteFixedNode(store);
                     deleted = true;
@@ -94,7 +94,7 @@
                 ValueNode cachedValue = state.getCacheEntry(identifier);
                 if (cachedValue != null) {
                     effects.replaceAtUsages(read, cachedValue);
-                    state.addScalarAlias(read, cachedValue);
+                    addScalarAlias(read, cachedValue);
                     deleted = true;
                 } else {
                     state.addCacheEntry(identifier, read);
@@ -107,7 +107,7 @@
                 ReadCacheEntry identifier = new ReadCacheEntry(object, write.location());
                 ValueNode cachedValue = state.getCacheEntry(identifier);
 
-                ValueNode value = state.getScalarAlias(write.value());
+                ValueNode value = getScalarAlias(write.value());
                 if (GraphUtil.unproxify(value) == GraphUtil.unproxify(cachedValue)) {
                     effects.deleteFixedNode(write);
                     deleted = true;
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Wed Oct 02 15:33:08 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Wed Oct 02 15:33:25 2013 +0200
@@ -36,18 +36,18 @@
 
 class VirtualizerToolImpl implements VirtualizerTool {
 
-    private final NodeBitMap usages;
     private final MetaAccessProvider metaAccess;
     private final Assumptions assumptions;
+    private final PartialEscapeClosure<?> closure;
 
-    VirtualizerToolImpl(NodeBitMap usages, MetaAccessProvider metaAccess, Assumptions assumptions) {
-        this.usages = usages;
+    VirtualizerToolImpl(MetaAccessProvider metaAccess, Assumptions assumptions, PartialEscapeClosure<?> closure) {
         this.metaAccess = metaAccess;
         this.assumptions = assumptions;
+        this.closure = closure;
     }
 
     private boolean deleted;
-    private PartialEscapeBlockState state;
+    private PartialEscapeBlockState<?> state;
     private ValueNode current;
     private FixedNode position;
     private GraphEffectList effects;
@@ -76,15 +76,15 @@
 
     @Override
     public State getObjectState(ValueNode value) {
-        return state.getObjectState(value);
+        return closure.getObjectState(state, value);
     }
 
     @Override
     public void setVirtualEntry(State objectState, int index, ValueNode value) {
         ObjectState obj = (ObjectState) objectState;
         assert obj != null && obj.isVirtual() : "not virtual: " + obj;
-        ObjectState valueState = state.getObjectState(value);
-        ValueNode newValue = value;
+        ObjectState valueState = closure.getObjectState(state, value);
+        ValueNode newValue;
         if (valueState == null) {
             newValue = getReplacedValue(value);
             assert obj.getEntry(index) == null || obj.getEntry(index).kind() == newValue.kind() || (isObjectEntry(obj.getEntry(index)) && isObjectEntry(newValue));
@@ -105,19 +105,13 @@
     }
 
     @Override
-    public ValueNode getMaterializedValue(ValueNode value) {
-        ObjectState obj = state.getObjectState(value);
-        return obj != null && !obj.isVirtual() ? obj.getMaterializedValue() : null;
-    }
-
-    @Override
     public ValueNode getReplacedValue(ValueNode original) {
-        return state.getScalarAlias(original);
+        return closure.getScalarAlias(original);
     }
 
     @Override
     public void replaceWithVirtual(VirtualObjectNode virtual) {
-        state.addAndMarkAlias(virtual, current, usages);
+        closure.addAndMarkAlias(virtual, current);
         if (current instanceof FixedWithNextNode) {
             effects.deleteFixedNode((FixedWithNextNode) current);
         }
@@ -126,8 +120,8 @@
 
     @Override
     public void replaceWithValue(ValueNode replacement) {
-        effects.replaceAtUsages(current, state.getScalarAlias(replacement));
-        state.addScalarAlias(current, replacement);
+        effects.replaceAtUsages(current, closure.getScalarAlias(replacement));
+        closure.addScalarAlias(current, replacement);
         deleted = true;
     }
 
@@ -154,16 +148,21 @@
     @Override
     public void createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, int[] locks) {
         VirtualUtil.trace("{{%s}} ", current);
-        if (virtualObject.isAlive()) {
-            state.addAndMarkAlias(virtualObject, virtualObject, usages);
-        } else {
+        if (!virtualObject.isAlive()) {
             effects.addFloatingNode(virtualObject, "newVirtualObject");
         }
         for (int i = 0; i < entryState.length; i++) {
-            entryState[i] = state.getScalarAlias(entryState[i]);
+            if (!(entryState[i] instanceof VirtualObjectNode)) {
+                ObjectState v = closure.getObjectState(state, entryState[i]);
+                if (v != null) {
+                    entryState[i] = v.isVirtual() ? v.getVirtualObject() : v.getMaterializedValue();
+                } else {
+                    entryState[i] = closure.getScalarAlias(entryState[i]);
+                }
+            }
         }
         state.addObject(virtualObject, new ObjectState(virtualObject, entryState, EscapeState.Virtual, locks));
-        state.addAndMarkAlias(virtualObject, virtualObject, usages);
+        closure.addAndMarkAlias(virtualObject, virtualObject);
         PartialEscapeClosure.METRIC_ALLOCATION_REMOVED.increment();
     }
 
--- a/src/share/vm/classfile/systemDictionary.hpp	Wed Oct 02 15:33:08 2013 +0200
+++ b/src/share/vm/classfile/systemDictionary.hpp	Wed Oct 02 15:33:25 2013 +0200
@@ -202,6 +202,7 @@
   do_klass(HotSpotResolvedObjectType_klass,       com_oracle_graal_hotspot_meta_HotSpotResolvedObjectType,      Opt) \
   do_klass(HotSpotMonitorValue_klass,             com_oracle_graal_hotspot_meta_HotSpotMonitorValue,            Opt) \
   do_klass(LocalImpl_klass,                       com_oracle_graal_hotspot_debug_LocalImpl,                     Opt) \
+  do_klass(CompilerThread_klass,                  com_oracle_graal_hotspot_CompilerThread,                      Opt) \
   /* graal.api.code */                                                                                               \
   do_klass(Assumptions_klass,                     com_oracle_graal_api_code_Assumptions,                        Opt) \
   do_klass(Assumptions_ConcreteMethod_klass,      com_oracle_graal_api_code_Assumptions_ConcreteMethod,         Opt) \
--- a/src/share/vm/classfile/vmSymbols.hpp	Wed Oct 02 15:33:08 2013 +0200
+++ b/src/share/vm/classfile/vmSymbols.hpp	Wed Oct 02 15:33:25 2013 +0200
@@ -311,6 +311,7 @@
   template(com_oracle_graal_hotspot_meta_HotSpotResolvedObjectType,  "com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType")       \
   template(com_oracle_graal_hotspot_meta_HotSpotMonitorValue,        "com/oracle/graal/hotspot/meta/HotSpotMonitorValue")             \
   template(com_oracle_graal_hotspot_debug_LocalImpl,                 "com/oracle/graal/hotspot/debug/LocalImpl")                      \
+  template(com_oracle_graal_hotspot_CompilerThread,                  "com/oracle/graal/hotspot/CompilerThread")                       \
   template(com_oracle_graal_hotspot_ptx_PTXHotSpotGraalRuntime,      "com/oracle/graal/hotspot/ptx/PTXHotSpotGraalRuntime")\
   AMD64_ONLY(template(com_oracle_graal_hotspot_amd64_AMD64HotSpotGraalRuntime,"com/oracle/graal/hotspot/amd64/AMD64HotSpotGraalRuntime"))\
   SPARC_ONLY(template(com_oracle_graal_hotspot_sparc_SPARCHotSpotGraalRuntime,"com/oracle/graal/hotspot/sparc/SPARCHotSpotGraalRuntime"))\
--- a/src/share/vm/graal/graalCompilerToVM.cpp	Wed Oct 02 15:33:08 2013 +0200
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Wed Oct 02 15:33:25 2013 +0200
@@ -875,6 +875,9 @@
 
   set_int("arrayClassElementOffset", in_bytes(ObjArrayKlass::element_klass_offset()));
 
+  set_int("graalCountersThreadOffset", in_bytes(JavaThread::graal_counters_offset()));
+  set_int("graalCountersSize", (jint) GRAAL_COUNTERS_SIZE);
+
 #undef set_boolean
 #undef set_int
 #undef set_long
@@ -1145,6 +1148,12 @@
   return klass;
 C2V_END
 
+C2V_VMENTRY(jlongArray, collectCounters, (JNIEnv *env, jobject))
+  typeArrayOop arrayOop = oopFactory::new_longArray(GRAAL_COUNTERS_SIZE, CHECK_NULL);
+  JavaThread::collect_counters(arrayOop);
+  return (jlongArray) JNIHandles::make_local(arrayOop);
+C2V_END
+
 #define CC (char*)  /*cast a literal from (const char*)*/
 #define FN_PTR(f) CAST_FROM_FN_PTR(void*, &(c2v_ ## f))
 
@@ -1224,6 +1233,7 @@
   {CC"invalidateInstalledCode",       CC"("HS_INSTALLED_CODE")V",                                       FN_PTR(invalidateInstalledCode)},
   {CC"readUnsafeUncompressedPointer", CC"("OBJECT"J)"OBJECT,                                            FN_PTR(readUnsafeUncompressedPointer)},
   {CC"readUnsafeKlassPointer",        CC"("OBJECT")J",                                                  FN_PTR(readUnsafeKlassPointer)},
+  {CC"collectCounters",               CC"()[J",                                                         FN_PTR(collectCounters)},
 };
 
 int CompilerToVM_methods_count() {
--- a/src/share/vm/runtime/thread.cpp	Wed Oct 02 15:33:08 2013 +0200
+++ b/src/share/vm/runtime/thread.cpp	Wed Oct 02 15:33:25 2013 +0200
@@ -1411,6 +1411,36 @@
 
 // ======= JavaThread ========
 
+#ifdef GRAAL
+
+#if GRAAL_COUNTERS_SIZE > 0
+jlong JavaThread::_graal_old_counters[GRAAL_COUNTERS_SIZE];
+
+bool graal_counters_include(oop threadObj) {
+  return !GRAAL_COUNTERS_EXCLUDE_COMPILER_THREADS || threadObj == NULL || threadObj->klass() != SystemDictionary::CompilerThread_klass();
+}
+
+void JavaThread::collect_counters(typeArrayOop array) {
+  MutexLocker tl(Threads_lock);
+  for (int i = 0; i < array->length(); i++) {
+    array->long_at_put(i, _graal_old_counters[i]);
+  }
+  for (JavaThread* tp = Threads::first(); tp != NULL; tp = tp->next()) {
+    if (graal_counters_include(tp->threadObj())) {
+      for (int i = 0; i < array->length(); i++) {
+        array->long_at_put(i, array->long_at(i) + tp->_graal_counters[i]);
+      }
+    }
+  }
+}
+#else
+void JavaThread::collect_counters(typeArrayOop array) {
+  // empty
+}
+#endif // GRAAL_COUNTERS_SIZE > 0
+
+#endif // GRAAL
+
 // A JavaThread is a normal Java thread
 
 void JavaThread::initialize() {
@@ -1449,6 +1479,9 @@
   _stack_guard_state = stack_guard_unused;
 #ifdef GRAAL
   _graal_alternate_call_target = NULL;
+  for (int i = 0; i < GRAAL_COUNTERS_SIZE; i++) {
+    _graal_counters[i] = 0;
+  }
 #endif
   _exception_oop = NULL;
   _exception_pc  = 0;
@@ -1638,6 +1671,14 @@
   ThreadSafepointState::destroy(this);
   if (_thread_profiler != NULL) delete _thread_profiler;
   if (_thread_stat != NULL) delete _thread_stat;
+
+#if defined(GRAAL) && (GRAAL_COUNTERS_SIZE > 0)
+  if (graal_counters_include(threadObj())) {
+    for (int i = 0; i < GRAAL_COUNTERS_SIZE; i++) {
+      _graal_old_counters[i] += _graal_counters[i];
+    }
+  }
+#endif
 }
 
 
--- a/src/share/vm/runtime/thread.hpp	Wed Oct 02 15:33:08 2013 +0200
+++ b/src/share/vm/runtime/thread.hpp	Wed Oct 02 15:33:25 2013 +0200
@@ -919,6 +919,17 @@
 #ifdef GRAAL
   address   _graal_alternate_call_target;
   address   _graal_implicit_exception_pc;  // pc at which the most recent implicit exception occurred
+
+  // number of counters, increase as needed. 0 == disabled
+#define GRAAL_COUNTERS_SIZE (0)
+#define GRAAL_COUNTERS_EXCLUDE_COMPILER_THREADS (true)
+
+  jlong     _graal_counters[GRAAL_COUNTERS_SIZE];
+  static jlong _graal_old_counters[GRAAL_COUNTERS_SIZE];
+
+ public:
+  static void collect_counters(typeArrayOop array);
+ private:
 #endif
   StackGuardState        _stack_guard_state;
 
@@ -1379,6 +1390,7 @@
 #ifdef GRAAL
   static ByteSize graal_alternate_call_target_offset() { return byte_offset_of(JavaThread, _graal_alternate_call_target); }
   static ByteSize graal_implicit_exception_pc_offset() { return byte_offset_of(JavaThread, _graal_implicit_exception_pc); }
+  static ByteSize graal_counters_offset()              { return byte_offset_of(JavaThread, _graal_counters             ); }
 #endif
   static ByteSize exception_oop_offset()         { return byte_offset_of(JavaThread, _exception_oop       ); }
   static ByteSize exception_pc_offset()          { return byte_offset_of(JavaThread, _exception_pc        ); }