changeset 10629:3c2a77f01e89

Merge.
author Christian Haeubl <haeubl@ssw.jku.at>
date Mon, 08 Jul 2013 09:58:00 +0200
parents 703d00fe2703 (current diff) cb2d97f002d4 (diff)
children add96a4e79f7 08e06d4a9e73
files graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java
diffstat 25 files changed, 550 insertions(+), 90 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java	Mon Jul 08 09:58:00 2013 +0200
@@ -372,4 +372,21 @@
             record(assumptions.list[i]);
         }
     }
+
+    public void print(PrintStream out) {
+        List<Assumption> nonNullList = new ArrayList<>();
+        if (list != null) {
+            for (int i = 0; i < list.length; ++i) {
+                Assumption a = list[i];
+                if (a != null) {
+                    nonNullList.add(a);
+                }
+            }
+        }
+
+        out.printf("%d assumptions:\n", nonNullList.size());
+        for (Assumption a : nonNullList) {
+            out.println(a.toString());
+        }
+    }
 }
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Mon Jul 08 09:58:00 2013 +0200
@@ -101,6 +101,23 @@
         }
     }
 
+    /**
+     * Creates a new debug scope that is unrelated to the current scope and runs a given task in the
+     * new scope.
+     * 
+     * @param name new scope name
+     * @param context the context objects of the new scope
+     * @param config the debug configuration to use for the new scope
+     * @param callable the task to run in the new scope
+     */
+    public static <T> T sandbox(String name, Object[] context, DebugConfig config, Callable<T> callable) {
+        if (ENABLED) {
+            return DebugScope.getInstance().scope(name, null, callable, true, config, context);
+        } else {
+            return DebugScope.call(callable);
+        }
+    }
+
     public static void scope(String name, Runnable runnable) {
         scope(name, new Object[0], runnable);
     }
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Mon Jul 08 09:58:00 2013 +0200
@@ -432,8 +432,12 @@
         }
         Variable input = load(inputVal);
         if (runtime().config.useCompressedOops && isCompressCandidate(access)) {
-            Variable scratch = newVariable(Kind.Long);
-            append(new StoreCompressedPointer(kind, storeAddress, input, scratch, state, runtime().config.narrowOopBase, runtime().config.narrowOopShift, runtime().config.logMinObjAlignment));
+            if (input.getKind() == Kind.Object) {
+                Variable scratch = newVariable(Kind.Long);
+                append(new StoreCompressedPointer(kind, storeAddress, input, scratch, state, runtime().config.narrowOopBase, runtime().config.narrowOopShift, runtime().config.logMinObjAlignment));
+            } else {
+                append(new StoreOp(input.getKind(), storeAddress, input, state));
+            }
         } else {
             append(new StoreOp(kind, storeAddress, input, state));
         }
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierVerificationTest.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierVerificationTest.java	Mon Jul 08 09:58:00 2013 +0200
@@ -32,6 +32,7 @@
 import com.oracle.graal.compiler.test.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.internal.*;
+import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.phases.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
@@ -630,10 +631,15 @@
                 new SafepointInsertionPhase().apply(graph);
                 new WriteBarrierAdditionPhase().apply(graph);
 
+                int barriers = 0;
                 // First, the total number of expected barriers is checked.
-                final int barriers = graph.getNodes(SerialWriteBarrier.class).count();
-                Assert.assertTrue(expectedBarriers == barriers);
-
+                if (((HotSpotRuntime) runtime()).config.useG1GC) {
+                    barriers = graph.getNodes(G1PreWriteBarrier.class).count() + graph.getNodes(G1PostWriteBarrier.class).count();
+                    Assert.assertTrue(expectedBarriers * 2 == barriers);
+                } else {
+                    barriers = graph.getNodes(SerialWriteBarrier.class).count();
+                    Assert.assertTrue(expectedBarriers == barriers);
+                }
                 // Iterate over all write nodes and remove barriers according to input indices.
                 NodeIteratorClosure<Boolean> closure = new NodeIteratorClosure<Boolean>() {
 
@@ -653,10 +659,10 @@
                                     }
                                 }
                             }
-                        } else if (node instanceof SerialWriteBarrier) {
+                        } else if (node instanceof SerialWriteBarrier || node instanceof G1PostWriteBarrier) {
                             // Remove flagged write barriers.
                             if (currentState) {
-                                graph.removeFixed(((SerialWriteBarrier) node));
+                                graph.removeFixed(((FixedWithNextNode) node));
                                 return false;
                             }
                         }
@@ -692,7 +698,7 @@
                 try {
                     ReentrantNodeIterator.apply(closure, graph.start(), false, null);
                     Debug.setConfig(Debug.fixedConfig(false, false, false, false, config.dumpHandlers(), config.output()));
-                    new WriteBarrierVerificationPhase().apply(graph);
+                    new WriteBarrierVerificationPhase(((HotSpotRuntime) runtime()).config.useG1GC).apply(graph);
                 } catch (AssertionError error) {
                     /*
                      * Catch assertion, test for expected one and re-throw in order to validate unit
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Mon Jul 08 09:58:00 2013 +0200
@@ -50,6 +50,7 @@
     public boolean usePopCountInstruction;
     public boolean useAESIntrinsics;
     public boolean useG1GC;
+    public long gcTotalCollectionsAddress;
 
     // Compressed Oops related values.
     public boolean useCompressedOops;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Mon Jul 08 09:58:00 2013 +0200
@@ -1214,7 +1214,7 @@
 
         ret.getMidTier().appendPhase(new WriteBarrierAdditionPhase());
         if (VerifyPhases.getValue()) {
-            ret.getMidTier().appendPhase(new WriteBarrierVerificationPhase());
+            ret.getMidTier().appendPhase(new WriteBarrierVerificationPhase(config.useG1GC));
         }
 
         return ret;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierVerificationPhase.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/phases/WriteBarrierVerificationPhase.java	Mon Jul 08 09:58:00 2013 +0200
@@ -41,12 +41,18 @@
  */
 public class WriteBarrierVerificationPhase extends Phase {
 
+    private final boolean useG1GC;
+
+    public WriteBarrierVerificationPhase(boolean useG1GC) {
+        this.useG1GC = useG1GC;
+    }
+
     @Override
     protected void run(StructuredGraph graph) {
         processWrites(graph);
     }
 
-    private static void processWrites(StructuredGraph graph) {
+    private void processWrites(StructuredGraph graph) {
         for (Node node : graph.getNodes()) {
             if (isObjectWrite(node)) {
                 validateWrite(node);
@@ -54,7 +60,7 @@
         }
     }
 
-    private static void validateWrite(Node write) {
+    private void validateWrite(Node write) {
         /*
          * The currently validated write is checked in order to discover if it has an appropriate
          * attached write barrier.
@@ -68,14 +74,25 @@
         while (iterator.hasNext()) {
             Node currentNode = iterator.next();
             assert !isSafepoint(currentNode) : "Write barrier must be present";
-            if (!(currentNode instanceof SerialWriteBarrier) || ((currentNode instanceof SerialWriteBarrier) && !validateBarrier(write, (SerialWriteBarrier) currentNode))) {
-                expandFrontier(frontier, currentNode);
+            if (useG1GC) {
+                if (!(currentNode instanceof G1PostWriteBarrier) || ((currentNode instanceof G1PostWriteBarrier) && !validateBarrier(write, (WriteBarrier) currentNode))) {
+                    expandFrontier(frontier, currentNode);
+                }
+            } else {
+                if (!(currentNode instanceof SerialWriteBarrier) || ((currentNode instanceof SerialWriteBarrier) && !validateBarrier(write, (WriteBarrier) currentNode))) {
+                    expandFrontier(frontier, currentNode);
+                }
             }
         }
     }
 
-    private static boolean hasAttachedBarrier(Node node) {
-        return (((FixedWithNextNode) node).next() instanceof SerialWriteBarrier) && validateBarrier(node, (SerialWriteBarrier) ((FixedWithNextNode) node).next());
+    private boolean hasAttachedBarrier(Node node) {
+        if (useG1GC) {
+            return ((FixedWithNextNode) node).next() instanceof G1PostWriteBarrier && ((FixedWithNextNode) node).predecessor() instanceof G1PreWriteBarrier &&
+                            validateBarrier(node, (G1PostWriteBarrier) ((FixedWithNextNode) node).next()) && validateBarrier(node, (G1PreWriteBarrier) ((FixedWithNextNode) node).predecessor());
+        } else {
+            return (((FixedWithNextNode) node).next() instanceof SerialWriteBarrier) && validateBarrier(node, (SerialWriteBarrier) ((FixedWithNextNode) node).next());
+        }
     }
 
     private static boolean isObjectWrite(Node node) {
@@ -103,7 +120,7 @@
         return ((node instanceof DeoptimizingNode) && ((DeoptimizingNode) node).canDeoptimize()) || (node instanceof LoopBeginNode);
     }
 
-    private static boolean validateBarrier(Node write, SerialWriteBarrier barrier) {
+    private static boolean validateBarrier(Node write, WriteBarrier barrier) {
         ValueNode writtenObject = null;
         LocationNode writtenLocation = null;
         if (write instanceof WriteNode) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Mon Jul 08 09:58:00 2013 +0200
@@ -692,4 +692,9 @@
     public static int verifiedEntryPointOffset() {
         return config().nmethodEntryOffset;
     }
+
+    @Fold
+    public static long gcTotalCollectionsAddress() {
+        return config().gcTotalCollectionsAddress;
+    }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Mon Jul 08 09:58:00 2013 +0200
@@ -259,7 +259,7 @@
             args.addConst("size", size);
             args.add("hub", hub);
             args.add("prototypeMarkWord", type.prototypeMarkWord());
-            args.addConst("fillContents", newInstanceNode.fillContents());
+            args.addConst("fillContents", useG1GC() ? true : newInstanceNode.fillContents());
 
             SnippetTemplate template = template(args);
             Debug.log("Lowering allocateInstance in %s: node=%s, template=%s, arguments=%s", graph, newInstanceNode, template, args);
@@ -284,7 +284,7 @@
             args.add("prototypeMarkWord", arrayType.prototypeMarkWord());
             args.addConst("headerSize", headerSize);
             args.addConst("log2ElementSize", log2ElementSize);
-            args.addConst("fillContents", newArrayNode.fillContents());
+            args.addConst("fillContents", useG1GC() ? true : newArrayNode.fillContents());
 
             SnippetTemplate template = template(args);
             Debug.log("Lowering allocateArray in %s: node=%s, template=%s, arguments=%s", graph, newArrayNode, template, args);
@@ -295,7 +295,7 @@
             Arguments args = new Arguments(allocateArrayDynamic);
             args.add("elementType", newArrayNode.getElementType());
             args.add("length", newArrayNode.length());
-            args.addConst("fillContents", newArrayNode.fillContents());
+            args.addConst("fillContents", useG1GC() ? true : newArrayNode.fillContents());
 
             SnippetTemplate template = template(args);
             template.instantiate(runtime, newArrayNode, DEFAULT_REPLACER, args);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneSnippets.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneSnippets.java	Mon Jul 08 09:58:00 2013 +0200
@@ -54,7 +54,7 @@
     private static Object instanceClone(Object src, Word hub, int layoutHelper) {
         int instanceSize = layoutHelper;
         Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION);
-        Object result = NewObjectSnippets.allocateInstance(instanceSize, hub, prototypeMarkWord, false);
+        Object result = NewObjectSnippets.allocateInstance(instanceSize, hub, prototypeMarkWord, useG1GC() ? true : false);
 
         Pointer memory = Word.fromObject(result);
         for (int offset = 2 * wordSize(); offset < instanceSize; offset += wordSize()) {
@@ -71,7 +71,7 @@
         int sizeInBytes = NewObjectSnippets.computeArrayAllocationSize(arrayLength, wordSize(), headerSize, log2ElementSize);
 
         Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION);
-        Object result = NewObjectSnippets.allocateArray(hub, arrayLength, prototypeMarkWord, headerSize, log2ElementSize, false);
+        Object result = NewObjectSnippets.allocateArray(hub, arrayLength, prototypeMarkWord, headerSize, log2ElementSize, useG1GC() ? true : false);
 
         Pointer memory = Word.fromObject(result);
         for (int offset = headerSize; offset < sizeInBytes; offset += wordSize()) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/WriteBarrierSnippets.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/WriteBarrierSnippets.java	Mon Jul 08 09:58:00 2013 +0200
@@ -28,11 +28,13 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.graph.Node.*;
+import com.oracle.graal.graph.Node.ConstantNodeParameter;
+import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.phases.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.replacements.Snippet.ConstantParameter;
 import com.oracle.graal.replacements.SnippetTemplate.AbstractTemplates;
@@ -40,12 +42,14 @@
 import com.oracle.graal.replacements.SnippetTemplate.SnippetInfo;
 import com.oracle.graal.replacements.nodes.*;
 import com.oracle.graal.word.*;
+import static com.oracle.graal.replacements.nodes.BranchProbabilityNode.*;
 
 public class WriteBarrierSnippets implements Snippets {
 
     private static final SnippetCounter.Group countersWriteBarriers = SnippetCounters.getValue() ? new SnippetCounter.Group("WriteBarriers") : null;
     private static final SnippetCounter serialFieldWriteBarrierCounter = new SnippetCounter(countersWriteBarriers, "serialFieldWriteBarrier", "Number of Serial Field Write Barriers");
     private static final SnippetCounter serialArrayWriteBarrierCounter = new SnippetCounter(countersWriteBarriers, "serialArrayWriteBarrier", "Number of Serial Array Write Barriers");
+    private static final int gcStartCycles = GraalOptions.GCDebugStartCycle.getValue();
 
     @Snippet
     public static void serialArrayWriteBarrier(Object obj, Object location, @ConstantParameter boolean usePrecise) {
@@ -85,19 +89,15 @@
         }
     }
 
-    /**
-     * Log method of debugging purposes.
-     */
-    static void log(boolean enabled, String format, WordBase value) {
-        if (enabled) {
-            Log.printf(format, value.rawValue());
+    @Snippet
+    public static void g1PreWriteBarrier(Object object, Object expectedObject, Object location, @ConstantParameter boolean doLoad, @ConstantParameter boolean nullCheck,
+                    @ConstantParameter boolean trace) {
+        if (nullCheck && object == null) {
+            DeoptimizeNode.deopt(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.NullCheckException);
         }
-    }
-
-    @Snippet
-    public static void g1PreWriteBarrier(Object object, Object expectedObject, Object location, @ConstantParameter boolean doLoad) {
         Word thread = thread();
         Object fixedObject = FixedValueAnchorNode.getObject(object);
+        verifyOop(fixedObject);
         Object fixedExpectedObject = FixedValueAnchorNode.getObject(expectedObject);
         Word field = (Word) Word.fromArray(fixedObject, location);
         Word previousOop = (Word) Word.fromObject(fixedExpectedObject);
@@ -105,19 +105,33 @@
         Word bufferAddress = thread.readWord(g1SATBQueueBufferOffset());
         Word indexAddress = thread.add(g1SATBQueueIndexOffset());
         Word indexValue = indexAddress.readWord(0);
-
+        int gcCycle = 0;
+        if (trace) {
+            gcCycle = (int) Word.unsigned(HotSpotReplacementsUtil.gcTotalCollectionsAddress()).readLong(0);
+            log(trace, "[%d] G1-Pre Thread %p Object %p\n", gcCycle, thread.rawValue(), Word.fromObject(fixedObject).rawValue());
+            log(trace, "[%d] G1-Pre Thread %p Expected Object %p\n", gcCycle, thread.rawValue(), Word.fromObject(fixedExpectedObject).rawValue());
+            log(trace, "[%d] G1-Pre Thread %p Field %p\n", gcCycle, thread.rawValue(), field.rawValue());
+            log(trace, "[%d] G1-Pre Thread %p Marking %d\n", gcCycle, thread.rawValue(), markingValue);
+            log(trace, "[%d] G1-Pre Thread %p DoLoad %d\n", gcCycle, thread.rawValue(), doLoad ? 1L : 0L);
+        }
         // If the concurrent marker is enabled, the barrier is issued.
-        if (markingValue != (byte) 0) {
+        if (probability(NOT_LIKELY_PROBABILITY, markingValue != (byte) 0)) {
             // If the previous value has to be loaded (before the write), the load is issued.
             // The load is always issued except the cases of CAS and referent field.
-            if (doLoad) {
+            if (probability(LIKELY_PROBABILITY, doLoad)) {
                 previousOop = (Word) Word.fromObject(field.readObjectCompressed(0));
+                if (trace) {
+                    if (previousOop.notEqual(Word.zero())) {
+                        verifyOop(previousOop.toObject());
+                    }
+                    log(trace, "[%d] G1-Pre Thread %p Previous Object %p\n ", gcCycle, thread.rawValue(), previousOop.rawValue());
+                }
             }
             // If the previous value is null the barrier should not be issued.
-            if (previousOop.notEqual(0)) {
+            if (probability(FREQUENT_PROBABILITY, previousOop.notEqual(0))) {
                 // If the thread-local SATB buffer is full issue a native call which will
                 // initialize a new one and add the entry.
-                if (indexValue.notEqual(0)) {
+                if (probability(FREQUENT_PROBABILITY, indexValue.notEqual(0))) {
                     Word nextIndex = indexValue.subtract(wordSize());
                     Word logAddress = bufferAddress.add(nextIndex);
                     // Log the object to be marked as well as update the SATB's buffer next index.
@@ -131,10 +145,12 @@
     }
 
     @Snippet
-    public static void g1PostWriteBarrier(Object object, Object value, Object location, @ConstantParameter boolean usePrecise) {
+    public static void g1PostWriteBarrier(Object object, Object value, Object location, @ConstantParameter boolean usePrecise, @ConstantParameter boolean trace) {
         Word thread = thread();
         Object fixedObject = FixedValueAnchorNode.getObject(object);
         Object fixedValue = FixedValueAnchorNode.getObject(value);
+        verifyOop(fixedObject);
+        verifyOop(fixedValue);
         Word oop = (Word) Word.fromObject(fixedObject);
         Word field;
         if (usePrecise) {
@@ -142,7 +158,12 @@
         } else {
             field = oop;
         }
-
+        int gcCycle = 0;
+        if (trace) {
+            gcCycle = (int) Word.unsigned(HotSpotReplacementsUtil.gcTotalCollectionsAddress()).readLong(0);
+            log(trace, "[%d] G1-Post Thread: %p Object: %p Field: %p\n", gcCycle, thread.rawValue(), Word.fromObject(fixedObject).rawValue());
+            log(trace, "[%d] G1-Post Thread: %p Field: %p\n", gcCycle, thread.rawValue(), field.rawValue());
+        }
         Word writtenValue = (Word) Word.fromObject(fixedValue);
         Word bufferAddress = thread.readWord(g1CardQueueBufferOffset());
         Word indexAddress = thread.add(g1CardQueueIndexOffset());
@@ -163,16 +184,17 @@
         }
         Word cardAddress = cardBase.add(displacement);
 
-        if (xorResult.notEqual(0)) {
+        if (probability(LIKELY_PROBABILITY, xorResult.notEqual(0))) {
             // If the written value is not null continue with the barrier addition.
-            if (writtenValue.notEqual(0)) {
+            if (probability(FREQUENT_PROBABILITY, writtenValue.notEqual(0))) {
                 byte cardByte = cardAddress.readByte(0);
                 // If the card is already dirty, (hence already enqueued) skip the insertion.
-                if (cardByte != (byte) 0) {
+                if (probability(LIKELY_PROBABILITY, cardByte != (byte) 0)) {
+                    log(trace, "[%d] G1-Post Thread: %p Card: %p \n", gcCycle, thread.rawValue(), Word.unsigned(cardByte).rawValue());
                     cardAddress.writeByte(0, (byte) 0);
                     // If the thread local card queue is full, issue a native call which will
                     // initialize a new one and add the card entry.
-                    if (indexValue.notEqual(0)) {
+                    if (probability(FREQUENT_PROBABILITY, indexValue.notEqual(0))) {
                         Word nextIndex = indexValue.subtract(wordSize());
                         Word logAddress = bufferAddress.add(nextIndex);
                         // Log the object to be scanned as well as update
@@ -305,6 +327,8 @@
             args.add("expectedObject", writeBarrierPre.getExpectedObject());
             args.add("location", writeBarrierPre.getLocation());
             args.addConst("doLoad", writeBarrierPre.doLoad());
+            args.addConst("nullCheck", !writeBarrierPre.getObject().stamp().nonNull());
+            args.addConst("trace", traceBarrier());
             template(args).instantiate(runtime, writeBarrierPre, DEFAULT_REPLACER, args);
         }
 
@@ -314,6 +338,7 @@
             args.add("value", writeBarrierPost.getValue());
             args.add("location", writeBarrierPost.getLocation());
             args.addConst("usePrecise", writeBarrierPost.usePrecise());
+            args.addConst("trace", traceBarrier());
             template(args).instantiate(runtime, writeBarrierPost, DEFAULT_REPLACER, args);
         }
 
@@ -333,4 +358,29 @@
             template(args).instantiate(runtime, arrayRangeWriteBarrier, DEFAULT_REPLACER, args);
         }
     }
+
+    /**
+     * Log method of debugging purposes.
+     */
+    private static void log(boolean enabled, String format, long value) {
+        if (enabled) {
+            Log.printf(format, value);
+        }
+    }
+
+    private static void log(boolean enabled, String format, long value1, long value2) {
+        if (enabled) {
+            Log.printf(format, value1, value2);
+        }
+    }
+
+    private static void log(boolean enabled, String format, long value1, long value2, long value3) {
+        if (enabled) {
+            Log.printf(format, value1, value2, value3);
+        }
+    }
+
+    private static boolean traceBarrier() {
+        return gcStartCycles > 0 && ((int) Word.unsigned(HotSpotReplacementsUtil.gcTotalCollectionsAddress()).readLong(0) > gcStartCycles);
+    }
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Mon Jul 08 09:58:00 2013 +0200
@@ -33,6 +33,7 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.nodes.util.*;
@@ -123,10 +124,10 @@
 
     @Override
     protected void run(final StructuredGraph graph) {
-        InliningData data = new InliningData(graph, compilationAssumptions);
+        final InliningData data = new InliningData(graph, compilationAssumptions);
 
         while (data.hasUnprocessedGraphs()) {
-            MethodInvocation currentInvocation = data.currentInvocation();
+            final MethodInvocation currentInvocation = data.currentInvocation();
             GraphInfo graphInfo = data.currentGraph();
             if (!currentInvocation.isRoot() && !inliningPolicy.isWorthInlining(currentInvocation.callee(), data.inliningDepth(), currentInvocation.probability(), currentInvocation.relevance(), false)) {
                 int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs();
@@ -142,8 +143,15 @@
                     currentInvocation.incrementProcessedGraphs();
                     if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) {
                         data.popInvocation();
-                        MethodInvocation parentInvoke = data.currentInvocation();
-                        tryToInline(data.currentGraph(), currentInvocation, parentInvoke, data.inliningDepth() + 1);
+                        final MethodInvocation parentInvoke = data.currentInvocation();
+                        Debug.scope("Inlining", data.inliningContext(), new Runnable() {
+
+                            @Override
+                            public void run() {
+                                tryToInline(data.currentGraph(), currentInvocation, parentInvoke, data.inliningDepth() + 1);
+                            }
+                        });
+
                     }
                 }
             }
@@ -595,6 +603,9 @@
 
         private static final GraphInfo DummyGraphInfo = new GraphInfo(null, new LinkedList<Invoke>(), 1.0, 1.0);
 
+        /**
+         * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee.
+         */
         private final ArrayDeque<GraphInfo> graphQueue;
         private final ArrayDeque<MethodInvocation> invocationQueue;
 
@@ -646,6 +657,18 @@
             }
         }
 
+        /**
+         * Gets the call hierarchy of this inling from outer most call to inner most callee.
+         */
+        public Object[] inliningContext() {
+            Object[] result = new Object[graphQueue.size()];
+            int i = 0;
+            for (GraphInfo g : graphQueue) {
+                result[i++] = g.graph.method();
+            }
+            return result;
+        }
+
         public MethodInvocation currentInvocation() {
             return invocationQueue.peek();
         }
@@ -684,10 +707,12 @@
             StringBuilder result = new StringBuilder("Invocations: ");
 
             for (MethodInvocation invocation : invocationQueue) {
-                result.append(invocation.callee().numberOfMethods());
-                result.append("x ");
-                result.append(invocation.callee().invoke());
-                result.append("; ");
+                if (invocation.callee() != null) {
+                    result.append(invocation.callee().numberOfMethods());
+                    result.append("x ");
+                    result.append(invocation.callee().invoke());
+                    result.append("; ");
+                }
             }
 
             result.append("\nGraphs: ");
@@ -768,8 +793,26 @@
         public boolean isRoot() {
             return callee == null;
         }
+
+        @Override
+        public String toString() {
+            if (isRoot()) {
+                return "<root>";
+            }
+            CallTargetNode callTarget = callee.invoke().callTarget();
+            if (callTarget instanceof MethodCallTargetNode) {
+                ResolvedJavaMethod calleeMethod = ((MethodCallTargetNode) callTarget).targetMethod();
+                return MetaUtil.format("Invoke#%H.%n(%p)", calleeMethod);
+            } else {
+                return "Invoke#" + callTarget.targetName();
+            }
+        }
     }
 
+    /**
+     * Information about a graph that will potentially be inlined. This includes tracking the
+     * invocations in graph that will subject to inlining themselves.
+     */
     private static class GraphInfo {
 
         private final StructuredGraph graph;
@@ -791,6 +834,10 @@
             }
         }
 
+        /**
+         * Gets the method associated with the {@linkplain #graph() graph} represented by this
+         * object.
+         */
         public ResolvedJavaMethod method() {
             return graph.method();
         }
@@ -799,6 +846,9 @@
             return !remainingInvokes.isEmpty();
         }
 
+        /**
+         * The graph about which this object contains inlining information.
+         */
         public StructuredGraph graph() {
             return graph;
         }
@@ -823,6 +873,11 @@
         public double invokeRelevance(Invoke invoke) {
             return Math.min(CapInheritedRelevance.getValue(), relevance) * nodeRelevance.get(invoke.asNode());
         }
+
+        @Override
+        public String toString() {
+            return MetaUtil.format("%H.%n(%p)", method()) + remainingInvokes;
+        }
     }
 
     private static class CompiledMethodInfo {
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Mon Jul 08 09:58:00 2013 +0200
@@ -143,7 +143,8 @@
     // Debug settings:
     @Option(help = "")
     public static final OptionValue<Boolean> BootstrapReplacements = new OptionValue<>(false);
-
+    @Option(help = "")
+    public static final OptionValue<Integer> GCDebugStartCycle = new OptionValue<>(-1);
     // Ideal graph visualizer output settings
     @Option(help = "")
     public static final OptionValue<Boolean> PrintBinaryGraphs = new OptionValue<>(true);
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Mon Jul 08 09:58:00 2013 +0200
@@ -23,6 +23,7 @@
 package com.oracle.graal.phases.schedule;
 
 import static com.oracle.graal.api.meta.LocationIdentity.*;
+import static com.oracle.graal.nodes.cfg.ControlFlowGraph.*;
 import static com.oracle.graal.phases.GraalOptions.*;
 
 import java.util.*;
@@ -335,7 +336,7 @@
 
         @Override
         public void apply(Block newBlock) {
-            this.block = getCommonDominator(this.block, newBlock);
+            this.block = commonDominator(this.block, newBlock);
         }
     }
 
@@ -487,16 +488,6 @@
         // now true usages are ready
     }
 
-    private static Block getCommonDominator(Block a, Block b) {
-        if (a == null) {
-            return b;
-        }
-        if (b == null) {
-            return a;
-        }
-        return ControlFlowGraph.commonDominator(a, b);
-    }
-
     private void sortNodesWithinBlocks(StructuredGraph graph, SchedulingStrategy strategy) {
         NodeBitMap visited = graph.createNodeBitMap();
         for (Block b : cfg.getBlocks()) {
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/PartialEvaluationTest.java	Mon Jul 08 09:58:00 2013 +0200
@@ -57,7 +57,8 @@
     public PartialEvaluationTest() {
         // Make sure Truffle runtime is initialized.
         Assert.assertTrue(Truffle.getRuntime() instanceof GraalTruffleRuntime);
-        this.partialEvaluator = new PartialEvaluator(runtime, ((GraalTruffleRuntime) Truffle.getRuntime()).getReplacements());
+        this.partialEvaluator = new PartialEvaluator(runtime, ((GraalTruffleRuntime) Truffle.getRuntime()).getReplacements(), new TruffleCache(runtime, GraphBuilderConfiguration.getDefault(),
+                        TruffleCompilerImpl.Optimizations, ((GraalTruffleRuntime) Truffle.getRuntime()).getReplacements()));
 
         DebugEnvironment.initialize(System.out);
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithoutBoxing.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/FrameWithoutBoxing.java	Mon Jul 08 09:58:00 2013 +0200
@@ -220,6 +220,7 @@
         }
         int slotIndex = slot.getIndex();
         if (slotIndex >= tags.length) {
+            CompilerDirectives.transferToInterpreter();
             resize();
         }
         tags[slotIndex] = (byte) accessKind.ordinal();
@@ -237,9 +238,11 @@
         }
         int slotIndex = slot.getIndex();
         if (slotIndex >= tags.length) {
+            CompilerDirectives.transferToInterpreter();
             resize();
         }
         if (tags[slotIndex] != accessKind.ordinal()) {
+            CompilerDirectives.transferToInterpreter();
             descriptor.getTypeConversion().updateFrameSlot(this, slot, getValue(slot));
             if (tags[slotIndex] != accessKind.ordinal()) {
                 throw new FrameSlotTypeException();
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Mon Jul 08 09:58:00 2013 +0200
@@ -70,14 +70,16 @@
     private final Replacements replacements;
     private Set<Constant> constantReceivers;
     private final HotSpotGraphCache cache;
+    private final TruffleCache truffleCache;
 
-    public PartialEvaluator(MetaAccessProvider metaAccessProvider, Replacements replacements) {
+    public PartialEvaluator(MetaAccessProvider metaAccessProvider, Replacements replacements, TruffleCache truffleCache) {
         this.metaAccessProvider = metaAccessProvider;
         this.nodeClass = metaAccessProvider.lookupJavaType(com.oracle.truffle.api.nodes.Node.class);
         this.customCanonicalizer = new PartialEvaluatorCanonicalizer(metaAccessProvider, nodeClass);
         this.skippedExceptionTypes = TruffleCompilerImpl.getSkippedExceptionTypes(metaAccessProvider);
         this.replacements = replacements;
         this.cache = HotSpotGraalRuntime.graalRuntime().getCache();
+        this.truffleCache = truffleCache;
 
         try {
             executeHelperMethod = metaAccessProvider.lookupJavaMethod(OptimizedCallTarget.class.getDeclaredMethod("executeHelper", PackedFrame.class, Arguments.class));
@@ -215,7 +217,7 @@
                         StructuredGraph inlineGraph = replacements.getMethodSubstitution(methodCallTargetNode.targetMethod());
                         NewFrameNode otherNewFrame = null;
                         if (inlineGraph == null) {
-                            inlineGraph = parseGraph(config, methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, !AOTCompilation.getValue());
+                            inlineGraph = parseGraph(methodCallTargetNode.targetMethod(), methodCallTargetNode.arguments(), assumptions, !AOTCompilation.getValue());
                             otherNewFrame = inlineGraph.getNodes(NewFrameNode.class).first();
                         }
 
@@ -225,7 +227,6 @@
                             int nodeCountAfter = graph.getNodeCount();
                             Debug.dump(graph, "After inlining %s %+d (%d)", methodCallTargetNode.targetMethod().toString(), nodeCountAfter - nodeCountBefore, nodeCountAfter);
                         }
-
                         changed = true;
 
                         if (otherNewFrame != null) {
@@ -240,23 +241,21 @@
         } while (changed && newFrameNode.isAlive() && newFrameNode.usages().isNotEmpty());
     }
 
-    private StructuredGraph parseGraph(final GraphBuilderConfiguration config, final ResolvedJavaMethod targetMethod, final NodeInputList<ValueNode> arguments, final Assumptions assumptions,
-                    final boolean canonicalizeReads) {
+    private StructuredGraph parseGraph(final ResolvedJavaMethod targetMethod, final NodeInputList<ValueNode> arguments, final Assumptions assumptions, final boolean canonicalizeReads) {
 
-        final StructuredGraph graph = new StructuredGraph(targetMethod);
+        final StructuredGraph graph = truffleCache.lookup(targetMethod, arguments).copy();
         Debug.scope("parseGraph", targetMethod, new Runnable() {
 
             @Override
             public void run() {
-                new GraphBuilderPhase(metaAccessProvider, config, TruffleCompilerImpl.Optimizations).apply(graph);
                 // Pass on constant arguments.
                 for (LocalNode local : graph.getNodes(LocalNode.class)) {
                     ValueNode arg = arguments.get(local.index());
-                    if (arg instanceof NewFrameNode) {
-                        local.setStamp(arg.stamp());
-                    } else if (arg.isConstant()) {
+                    if (arg.isConstant()) {
                         Constant constant = arg.asConstant();
                         local.replaceAndDelete(ConstantNode.forConstant(constant, metaAccessProvider, graph));
+                    } else {
+                        local.setStamp(arg.stamp());
                     }
                 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCache.java	Mon Jul 08 09:58:00 2013 +0200
@@ -0,0 +1,266 @@
+/*
+ * 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.truffle;
+
+import static com.oracle.graal.phases.GraalOptions.*;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.debug.internal.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Node;
+import com.oracle.graal.java.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.tiers.*;
+import com.oracle.graal.truffle.phases.*;
+import com.oracle.graal.virtual.phases.ea.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * Implementation of a cache for Truffle graphs for improving partial evaluation time.
+ */
+public final class TruffleCache {
+
+    private final MetaAccessProvider metaAccessProvider;
+    private final GraphBuilderConfiguration config;
+    private final OptimisticOptimizations optimisticOptimizations;
+    private final Replacements replacements;
+
+    private final HashMap<ResolvedJavaMethod, StructuredGraph> cache = new HashMap<>();
+
+    public TruffleCache(MetaAccessProvider metaAccessProvider, GraphBuilderConfiguration config, OptimisticOptimizations optimisticOptimizations, Replacements replacements) {
+        this.metaAccessProvider = metaAccessProvider;
+        this.config = config;
+        this.optimisticOptimizations = optimisticOptimizations;
+        this.replacements = replacements;
+    }
+
+    public StructuredGraph lookup(final ResolvedJavaMethod method, final NodeInputList<ValueNode> arguments) {
+
+        if (cache.containsKey(method)) {
+            StructuredGraph graph = cache.get(method);
+            if (checkArgumentStamps(graph, arguments)) {
+                return graph;
+            }
+        }
+
+        StructuredGraph resultGraph = Debug.sandbox("TruffleCache", new Object[]{metaAccessProvider, method}, DebugScope.getConfig(), new Callable<StructuredGraph>() {
+
+            public StructuredGraph call() {
+                StructuredGraph newGraph = parseGraph(method);
+
+                // Get stamps from actual arguments.
+                List<Stamp> stamps = new ArrayList<>();
+                for (ValueNode arg : arguments) {
+                    stamps.add(arg.stamp());
+                }
+
+                if (cache.containsKey(method)) {
+                    // Make sure stamps are generalized based on previous stamps.
+                    StructuredGraph graph = cache.get(method);
+                    for (LocalNode localNode : graph.getNodes(LocalNode.class)) {
+                        int index = localNode.index();
+                        Stamp stamp = stamps.get(index);
+                        stamps.set(index, stamp.meet(localNode.stamp()));
+                    }
+                }
+
+                // Set stamps into graph before optimizing.
+                for (LocalNode localNode : newGraph.getNodes(LocalNode.class)) {
+                    int index = localNode.index();
+                    Stamp stamp = stamps.get(index);
+                    localNode.setStamp(stamp);
+                }
+
+                optimizeGraph(newGraph);
+                cache.put(method, newGraph);
+                if (TruffleCompilerOptions.TraceTruffleCacheDetails.getValue()) {
+                    TTY.println(String.format("[truffle] added to graph cache method %s with %d nodes.", method, newGraph.getNodeCount()));
+                }
+                return newGraph;
+            }
+        });
+        return resultGraph;
+    }
+
+    private void optimizeGraph(StructuredGraph newGraph) {
+
+        ConditionalEliminationPhase eliminate = new ConditionalEliminationPhase(metaAccessProvider);
+        ConvertDeoptimizeToGuardPhase convertDeoptimizeToGuardPhase = new ConvertDeoptimizeToGuardPhase();
+
+        Assumptions assumptions = new Assumptions(false);
+        CanonicalizerPhase.Instance canonicalizerPhase = new CanonicalizerPhase.Instance(metaAccessProvider, assumptions, !AOTCompilation.getValue(), null, null);
+
+        Integer maxNodes = TruffleCompilerOptions.TruffleOperationCacheMaxNodes.getValue();
+
+        contractGraph(newGraph, eliminate, convertDeoptimizeToGuardPhase, canonicalizerPhase);
+
+        while (newGraph.getNodeCount() <= maxNodes) {
+
+            int mark = newGraph.getMark();
+
+            expandGraph(newGraph, maxNodes);
+
+            if (newGraph.getNewNodes(mark).count() == 0) {
+                // No progress => exit iterative optimization.
+                break;
+            }
+
+            contractGraph(newGraph, eliminate, convertDeoptimizeToGuardPhase, canonicalizerPhase);
+        }
+
+        HighTierContext context = new HighTierContext(metaAccessProvider, assumptions, replacements);
+        PartialEscapePhase partialEscapePhase = new PartialEscapePhase(false, new CanonicalizerPhase(true));
+        partialEscapePhase.apply(newGraph, context);
+
+        if (newGraph.getNodeCount() > maxNodes && (TruffleCompilerOptions.TraceTruffleCacheDetails.getValue() || TruffleCompilerOptions.TraceTrufflePerformanceWarnings.getValue())) {
+            TTY.println(String.format("[truffle] PERFORMANCE WARNING: method %s got too large with %d nodes.", newGraph.method(), newGraph.getNodeCount()));
+        }
+    }
+
+    private static void contractGraph(StructuredGraph newGraph, ConditionalEliminationPhase eliminate, ConvertDeoptimizeToGuardPhase convertDeoptimizeToGuardPhase,
+                    CanonicalizerPhase.Instance canonicalizerPhase) {
+        // Canonicalize / constant propagate.
+        canonicalizerPhase.apply(newGraph);
+
+        // Convert deopt to guards.
+        convertDeoptimizeToGuardPhase.apply(newGraph);
+
+        // Conditional elimination.
+        eliminate.apply(newGraph);
+    }
+
+    private void expandGraph(StructuredGraph newGraph, int maxNodes) {
+        NodeBitMap visitedNodes = newGraph.createNodeBitMap(true);
+        Queue<AbstractBeginNode> workQueue = new LinkedList<>();
+        workQueue.add(newGraph.start());
+
+        while (!workQueue.isEmpty() && newGraph.getNodeCount() <= maxNodes) {
+            AbstractBeginNode start = workQueue.poll();
+            expandPath(newGraph, maxNodes, visitedNodes, start, workQueue);
+        }
+    }
+
+    private void expandPath(StructuredGraph newGraph, int maxNodes, NodeBitMap visitedNodes, AbstractBeginNode start, Queue<AbstractBeginNode> workQueue) {
+        FixedNode next = start;
+        while (!visitedNodes.isMarked(next)) {
+            visitedNodes.mark(next);
+            if (next instanceof Invoke) {
+                Invoke invoke = (Invoke) next;
+                next = expandInvoke(invoke);
+                if (newGraph.getNodeCount() > maxNodes) {
+                    return;
+                }
+            }
+
+            if (next instanceof ControlSplitNode) {
+                ControlSplitNode controlSplitNode = (ControlSplitNode) next;
+                AbstractBeginNode maxProbNode = null;
+                for (Node succ : controlSplitNode.cfgSuccessors()) {
+                    AbstractBeginNode successor = (AbstractBeginNode) succ;
+                    if (maxProbNode == null || controlSplitNode.probability(successor) > controlSplitNode.probability(maxProbNode)) {
+                        maxProbNode = successor;
+                    }
+                }
+                for (Node succ : controlSplitNode.cfgSuccessors()) {
+                    AbstractBeginNode successor = (AbstractBeginNode) succ;
+                    if (successor != maxProbNode) {
+                        workQueue.add(successor);
+                    }
+                }
+                next = maxProbNode;
+            } else if (next instanceof EndNode) {
+                EndNode endNode = (EndNode) next;
+                next = endNode.merge();
+            } else if (next instanceof ControlSinkNode) {
+                return;
+            } else if (next instanceof FixedWithNextNode) {
+                FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) next;
+                next = fixedWithNextNode.next();
+            }
+        }
+    }
+
+    private FixedNode expandInvoke(Invoke invoke) {
+        if (invoke.callTarget() instanceof MethodCallTargetNode) {
+            final MethodCallTargetNode methodCallTargetNode = (MethodCallTargetNode) invoke.callTarget();
+            if ((methodCallTargetNode.invokeKind() == InvokeKind.Special || methodCallTargetNode.invokeKind() == InvokeKind.Static) &&
+                            !Modifier.isNative(methodCallTargetNode.targetMethod().getModifiers())) {
+                if (methodCallTargetNode.targetMethod().getAnnotation(ExplodeLoop.class) != null) {
+                    // Do not inline explode loop methods, they need canonicalization and forced
+                    // unrolling.
+                    return invoke.asNode();
+                }
+                StructuredGraph inlinedGraph = Debug.scope("ExpandInvoke", methodCallTargetNode.targetMethod(), new Callable<StructuredGraph>() {
+
+                    public StructuredGraph call() {
+                        StructuredGraph inlineGraph = replacements.getMethodSubstitution(methodCallTargetNode.targetMethod());
+                        if (inlineGraph == null) {
+                            inlineGraph = parseGraph(methodCallTargetNode.targetMethod());
+                        }
+                        return inlineGraph;
+                    }
+                });
+                FixedNode fixedNode = (FixedNode) invoke.predecessor();
+                InliningUtil.inline(invoke, inlinedGraph, true);
+                return fixedNode;
+            }
+        }
+        return invoke.asNode();
+    }
+
+    private StructuredGraph parseGraph(ResolvedJavaMethod method) {
+        StructuredGraph graph = new StructuredGraph(method);
+        new GraphBuilderPhase(metaAccessProvider, config, optimisticOptimizations).apply(graph);
+        // Intrinsify methods.
+        new ReplaceIntrinsicsPhase(replacements).apply(graph);
+        return graph;
+    }
+
+    private static boolean checkArgumentStamps(StructuredGraph graph, NodeInputList<ValueNode> arguments) {
+        assert graph.getNodes(LocalNode.class).count() == arguments.count();
+        for (LocalNode localNode : graph.getNodes(LocalNode.class)) {
+            Stamp newStamp = localNode.stamp().meet(arguments.get(localNode.index()).stamp());
+            if (!newStamp.equals(localNode.stamp())) {
+                if (TruffleCompilerOptions.TraceTruffleCacheDetails.getValue()) {
+                    TTY.println(String.format("[truffle] graph cache entry too specific for method %s argument %s previous stamp %s new stamp %s.", graph.method(), localNode, localNode.stamp(),
+                                    newStamp));
+                }
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Mon Jul 08 09:58:00 2013 +0200
@@ -62,6 +62,7 @@
     private final Backend backend;
     private final ResolvedJavaType[] skippedExceptionTypes;
     private final HotSpotGraalRuntime graalRuntime;
+    private final TruffleCache truffleCache;
 
     private static final Class[] SKIPPED_EXCEPTION_CLASSES = new Class[]{SlowPathException.class, UnexpectedResultException.class, ArithmeticException.class};
 
@@ -75,9 +76,13 @@
         this.backend = Graal.getRequiredCapability(Backend.class);
         this.replacements = ((GraalTruffleRuntime) Truffle.getRuntime()).getReplacements();
         this.graalRuntime = HotSpotGraalRuntime.graalRuntime();
+        this.skippedExceptionTypes = getSkippedExceptionTypes(metaAccessProvider);
 
-        this.partialEvaluator = new PartialEvaluator(metaAccessProvider, replacements);
-        this.skippedExceptionTypes = getSkippedExceptionTypes(metaAccessProvider);
+        final GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault();
+        config.setSkippedExceptionTypes(skippedExceptionTypes);
+        this.truffleCache = new TruffleCache(this.runtime, config, TruffleCompilerImpl.Optimizations, this.replacements);
+
+        this.partialEvaluator = new PartialEvaluator(metaAccessProvider, replacements, truffleCache);
 
         if (DebugEnabled.getValue()) {
             DebugEnvironment.initialize(System.out);
@@ -142,7 +147,8 @@
             @Override
             public CompilationResult call() {
                 CallingConvention cc = getCallingConvention(runtime, Type.JavaCallee, graph.method(), false);
-                return GraalCompiler.compileGraph(graph, cc, graph.method(), runtime, replacements, backend, runtime.getTarget(), null, plan, OptimisticOptimizations.ALL, new SpeculationLog(), suites, new CompilationResult());
+                return GraalCompiler.compileGraph(graph, cc, graph.method(), runtime, replacements, backend, runtime.getTarget(), null, plan, OptimisticOptimizations.ALL, new SpeculationLog(),
+                                suites, new CompilationResult());
             }
         });
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Mon Jul 08 09:58:00 2013 +0200
@@ -53,6 +53,8 @@
     public static final OptionValue<Boolean> TruffleFunctionInlining = new OptionValue<>(true);
     @Option(help = "")
     public static final OptionValue<Integer> TruffleConstantUnrollLimit = new OptionValue<>(32);
+    @Option(help = "")
+    public static final OptionValue<Integer> TruffleOperationCacheMaxNodes = new OptionValue<>(200);
 
     // tracing
     @Option(help = "")
@@ -60,6 +62,10 @@
     @Option(help = "")
     public static final OptionValue<Boolean> TraceTruffleCompilationDetails = new OptionValue<>(false);
     @Option(help = "")
+    public static final OptionValue<Boolean> TraceTruffleCacheDetails = new OptionValue<>(false);
+    @Option(help = "")
+    public static final OptionValue<Boolean> TraceTrufflePerformanceWarnings = new OptionValue<>(false);
+    @Option(help = "")
     public static final OptionValue<Boolean> TruffleInlinePrinter = new OptionValue<>(false);
     @Option(help = "")
     public static final OptionValue<Boolean> TraceTruffleCompilationExceptions = new OptionValue<>(true);
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/VerifyFrameDoesNotEscapePhase.java	Mon Jul 08 09:57:14 2013 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/phases/VerifyFrameDoesNotEscapePhase.java	Mon Jul 08 09:58:00 2013 +0200
@@ -41,7 +41,9 @@
         if (frame != null) {
             for (MethodCallTargetNode callTarget : frame.usages().filter(MethodCallTargetNode.class)) {
                 if (callTarget.invoke() != null) {
-                    Throwable exception = new VerificationError("Frame escapes at: %s#%s", callTarget, callTarget.targetMethod());
+                    String properties = callTarget.getDebugProperties().toString();
+                    String arguments = callTarget.arguments().toString();
+                    Throwable exception = new VerificationError("Frame escapes at: %s#%s\nproperties:%s\narguments: %s", callTarget, callTarget.targetMethod(), properties, arguments);
                     throw GraphUtil.approxSourceException(callTarget, exception);
                 }
             }
--- a/mx/commands.py	Mon Jul 08 09:57:14 2013 +0200
+++ b/mx/commands.py	Mon Jul 08 09:58:00 2013 +0200
@@ -706,8 +706,7 @@
     return p.find_classes_with_matching_source_line(pkgRoot, matches, includeInnerClasses)
 
 def _run_tests(args, harness, annotations, testfile):
-    pos = [a for a in args if a[0] != '-' and a[0] != '@' ]
-    neg = [a[1:] for a in args if a[0] == '-']
+    tests = [a for a in args if a[0] != '@' ]
     vmArgs = [a[1:] for a in args if a[0] == '@']
 
     def containsAny(c, substrings):
@@ -715,17 +714,28 @@
             if s in c:
                 return True
         return False
-
-    classes = []
+    
+    candidates = []
     for p in mx.projects():
         if mx.java().javaCompliance < p.javaCompliance:
             continue
-        classes += _find_classes_with_annotations(p, None, annotations).keys()
+        candidates += _find_classes_with_annotations(p, None, annotations).keys()
 
-        if len(pos) != 0:
-            classes = [c for c in classes if containsAny(c, pos)]
-        if len(neg) != 0:
-            classes = [c for c in classes if not containsAny(c, neg)]
+    classes = []
+    if len(tests) == 0:
+        classes = candidates
+    else:
+        for t in tests:
+            if t.startswith('-'):
+                mx.abort('VM option needs @ prefix (i.e., @' + t + ')')
+                
+            found = False
+            for c in candidates:
+                if t in c:
+                    found = True
+                    classes.append(c)
+            if not found:
+                mx.log('warning: no tests matched by substring "' + t)
 
     projectscp = mx.classpath([pcp.name for pcp in mx.projects() if pcp.javaCompliance <= mx.java().javaCompliance])
 
@@ -764,8 +774,7 @@
 _unittestHelpSuffix = """
 
     If filters are supplied, only tests whose fully qualified name
-    includes a filter as a substring are run. Negative filters are
-    those with a '-' prefix.
+    includes a filter as a substring are run.
     
     Options with a '@' prefix are passed to the VM.
     
--- a/src/share/vm/gc_interface/collectedHeap.hpp	Mon Jul 08 09:57:14 2013 +0200
+++ b/src/share/vm/gc_interface/collectedHeap.hpp	Mon Jul 08 09:58:00 2013 +0200
@@ -490,7 +490,9 @@
   // Total number of GC collections (started)
   unsigned int total_collections() const { return _total_collections; }
   unsigned int total_full_collections() const { return _total_full_collections;}
-
+#ifdef GRAAL
+  void* total_collections_address() { return &_total_collections;}
+#endif
   // Increment total number of GC collections (started)
   // Should be protected but used by PSMarkSweep - cleanup for 1.4.2
   void increment_total_collections(bool full = false) {
--- a/src/share/vm/graal/graalCompilerToGPU.cpp	Mon Jul 08 09:57:14 2013 +0200
+++ b/src/share/vm/graal/graalCompilerToGPU.cpp	Mon Jul 08 09:58:00 2013 +0200
@@ -24,6 +24,7 @@
 #include "precompiled.hpp"
 
 #include "graal/graalCompiler.hpp"
+#include "runtime/javaCalls.hpp"
 #include "graal/graalCompilerToVM.hpp"
 #include "graal/graalEnv.hpp"
 #include "graal/graalJavaAccess.hpp"
--- a/src/share/vm/graal/graalCompilerToVM.cpp	Mon Jul 08 09:57:14 2013 +0200
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Mon Jul 08 09:58:00 2013 +0200
@@ -907,6 +907,7 @@
   set_int("g1SATBQueueBufferOffset", in_bytes(JavaThread::satb_mark_queue_offset() + PtrQueue::byte_offset_of_buf()));
   set_address("writeBarrierPreAddress", GraalRuntime::write_barrier_pre);
   set_address("writeBarrierPostAddress", GraalRuntime::write_barrier_post);
+  set_address("gcTotalCollectionsAddress", (jlong)(address)(Universe::heap()->total_collections_address()));
 
   BarrierSet* bs = Universe::heap()->barrier_set();
   switch (bs->kind()) {