changeset 12639:50aca0c0dff4

Merge.
author Christian Humer <christian.humer@gmail.com>
date Wed, 30 Oct 2013 11:08:00 +0100
parents 02f844c76626 (current diff) 321229a79f7a (diff)
children 6860d5528902
files
diffstat 18 files changed, 627 insertions(+), 238 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Tue Oct 29 16:55:42 2013 +0100
+++ b/.hgignore	Wed Oct 30 11:08:00 2013 +0100
@@ -16,6 +16,7 @@
 \.checkstyle$
 \.classpath
 \.factorypath
+\.currentAnnotationProcessors
 \.externalToolBuilders
 \.project
 \.settings/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EAMergingTest.java	Wed Oct 30 11:08:00 2013 +0100
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.test.ea;
+
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import com.oracle.graal.nodes.*;
+
+public class EAMergingTest extends EATestBase {
+
+    @Test
+    public void testSimpleMerge() {
+        testEscapeAnalysis("simpleMergeSnippet", null, false);
+        assertTrue(returnNode.result() instanceof PhiNode);
+        PhiNode phi = (PhiNode) returnNode.result();
+        assertTrue(phi.valueAt(0) instanceof LocalNode);
+        assertTrue(phi.valueAt(1) instanceof LocalNode);
+    }
+
+    public static int simpleMergeSnippet(boolean b, int u, int v) {
+        TestClassInt obj;
+        if (b) {
+            obj = new TestClassInt(u, 0);
+            notInlineable();
+        } else {
+            obj = new TestClassInt(v, 0);
+            notInlineable();
+        }
+        return obj.x;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java	Wed Oct 30 11:08:00 2013 +0100
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.test.ea;
+
+import org.junit.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.test.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.java.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.virtual.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.tiers.*;
+import com.oracle.graal.virtual.phases.ea.*;
+
+//JaCoCo Exclude
+
+/**
+ * This base class for all Escape Analysis tests does not contain tests itself, therefore it is not
+ * automatically excluded from JaCoCo. Since it includes code that is used in the test snippets, it
+ * needs to be excluded manually.
+ */
+public class EATestBase extends GraalCompilerTest {
+
+    public static class TestClassInt {
+        public int x;
+        public int y;
+
+        public TestClassInt() {
+            this(0, 0);
+        }
+
+        public TestClassInt(int x) {
+            this(x, 0);
+        }
+
+        public TestClassInt(int x, int y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            TestClassInt other = (TestClassInt) obj;
+            return x == other.x && y == other.y;
+        }
+
+        @Override
+        public String toString() {
+            return "{" + x + "," + y + "}";
+        }
+
+        @Override
+        public int hashCode() {
+            return x + 13 * y;
+        }
+    }
+
+    public static class TestClassObject {
+        public Object x;
+        public Object y;
+
+        public TestClassObject() {
+            this(null, null);
+        }
+
+        public TestClassObject(Object x) {
+            this(x, null);
+        }
+
+        public TestClassObject(Object x, Object y) {
+            this.x = x;
+            this.y = y;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            TestClassObject other = (TestClassObject) obj;
+            return x == other.x && y == other.y;
+        }
+
+        @Override
+        public String toString() {
+            return "{" + x + "," + y + "}";
+        }
+
+        @Override
+        public int hashCode() {
+            return (x == null ? 0 : x.hashCode()) + 13 * (y == null ? 0 : y.hashCode());
+        }
+    }
+
+    protected static native void notInlineable();
+
+    protected StructuredGraph graph;
+    protected HighTierContext context;
+    protected ReturnNode returnNode;
+
+    /**
+     * Runs Escape Analysis on the given snippet and makes sure that no allocations remain in the
+     * graph.
+     * 
+     * @param snippet the name of the method whose graph should be processed
+     * @param expectedConstantResult if this is non-null, the resulting graph needs to have the
+     *            given constant return value
+     * @param iterativeEscapeAnalysis true if escape analysis should be run for more than one
+     *            iteration
+     */
+    protected void testEscapeAnalysis(String snippet, final Constant expectedConstantResult, final boolean iterativeEscapeAnalysis) {
+        prepareGraph(snippet, iterativeEscapeAnalysis);
+        if (expectedConstantResult != null) {
+            Assert.assertTrue(returnNode.result().toString(), returnNode.result().isConstant());
+            Assert.assertEquals(expectedConstantResult, returnNode.result().asConstant());
+        }
+        int newInstanceCount = graph.getNodes().filter(NewInstanceNode.class).count() + graph.getNodes().filter(NewArrayNode.class).count() +
+                        graph.getNodes().filter(CommitAllocationNode.class).count();
+        Assert.assertEquals(0, newInstanceCount);
+    }
+
+    protected void prepareGraph(String snippet, final boolean iterativeEscapeAnalysis) {
+        ResolvedJavaMethod method = getMetaAccess().lookupJavaMethod(getMethod(snippet));
+        graph = new StructuredGraph(method);
+        Debug.scope(getClass().getSimpleName(), new Object[]{graph, method, getCodeCache()}, new Runnable() {
+
+            public void run() {
+                new GraphBuilderPhase(getMetaAccess(), getForeignCalls(), GraphBuilderConfiguration.getEagerDefault(), OptimisticOptimizations.ALL).apply(graph);
+                Assumptions assumptions = new Assumptions(false);
+                context = new HighTierContext(getProviders(), assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL);
+                new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context);
+                new DeadCodeEliminationPhase().apply(graph);
+                new CanonicalizerPhase(true).apply(graph, context);
+                new PartialEscapePhase(iterativeEscapeAnalysis, false, new CanonicalizerPhase(true)).apply(graph, context);
+                Assert.assertEquals(1, graph.getNodes().filter(ReturnNode.class).count());
+                returnNode = graph.getNodes().filter(ReturnNode.class).first();
+            }
+        });
+    }
+}
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java	Wed Oct 30 11:08:00 2013 +0100
@@ -24,28 +24,20 @@
 
 import org.junit.*;
 
-import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.compiler.test.*;
-import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
-import com.oracle.graal.java.*;
 import com.oracle.graal.loop.phases.*;
-import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
-import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.virtual.*;
-import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.schedule.*;
-import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.virtual.phases.ea.*;
 
 /**
  * The PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct
  * values.
  */
-public class EscapeAnalysisTest extends GraalCompilerTest {
+public class EscapeAnalysisTest extends EATestBase {
 
     @Test
     public void test1() {
@@ -82,8 +74,6 @@
         testEscapeAnalysis("testMonitorSnippet", Constant.forInt(0), false);
     }
 
-    private static native void notInlineable();
-
     public static int testMonitorSnippet() {
         Integer x = new Integer(0);
         Double y = new Double(0);
@@ -126,19 +116,8 @@
         testEscapeAnalysis("testMerge1Snippet", Constant.forInt(0), true);
     }
 
-    public static class TestObject {
-
-        int x;
-        int y;
-
-        public TestObject(int x, int y) {
-            this.x = x;
-            this.y = y;
-        }
-    }
-
     public static int testMerge1Snippet(int a) {
-        TestObject obj = new TestObject(1, 0);
+        TestClassInt obj = new TestClassInt(1, 0);
         if (a < 0) {
             obj.x = obj.x + 1;
         } else {
@@ -157,7 +136,7 @@
     }
 
     public int testSimpleLoopSnippet(int a) {
-        TestObject obj = new TestObject(1, 2);
+        TestClassInt obj = new TestClassInt(1, 2);
         for (int i = 0; i < a; i++) {
             notInlineable();
         }
@@ -170,7 +149,7 @@
     }
 
     public int testModifyingLoopSnippet(int a) {
-        TestObject obj = new TestObject(1, 2);
+        TestClassInt obj = new TestClassInt(1, 2);
         for (int i = 0; i < a; i++) {
             obj.x = 3;
             notInlineable();
@@ -178,24 +157,15 @@
         return obj.x <= 3 ? 1 : 0;
     }
 
-    public static class TestObject2 {
-
-        Object o;
-
-        public TestObject2(Object o) {
-            this.o = o;
-        }
-    }
-
     @Test
     public void testCheckCast() {
-        testEscapeAnalysis("testCheckCastSnippet", Constant.forObject(TestObject2.class), false);
+        testEscapeAnalysis("testCheckCastSnippet", Constant.forObject(TestClassObject.class), false);
     }
 
     public Object testCheckCastSnippet() {
-        TestObject2 obj = new TestObject2(TestObject2.class);
-        TestObject2 obj2 = new TestObject2(obj);
-        return ((TestObject2) obj2.o).o;
+        TestClassObject obj = new TestClassObject(TestClassObject.class);
+        TestClassObject obj2 = new TestClassObject(obj);
+        return ((TestClassObject) obj2.x).x;
     }
 
     @Test
@@ -204,9 +174,9 @@
     }
 
     public boolean testInstanceOfSnippet() {
-        TestObject2 obj = new TestObject2(TestObject2.class);
-        TestObject2 obj2 = new TestObject2(obj);
-        return obj2.o instanceof TestObject2;
+        TestClassObject obj = new TestClassObject(TestClassObject.class);
+        TestClassObject obj2 = new TestClassObject(obj);
+        return obj2.x instanceof TestClassObject;
     }
 
     @SuppressWarnings("unused")
@@ -223,98 +193,56 @@
         testEscapeAnalysis("testNewNodeSnippet", null, false);
     }
 
-    private static final TestObject2 staticObj = new TestObject2(null);
+    private static final TestClassObject staticObj = new TestClassObject();
 
     public static Object testFullyUnrolledLoopSnippet() {
         /*
          * This tests a case that can appear if PEA is performed both before and after loop
-         * unrolling/peeling: If the VirtualInstanceNode is not anchored correctly to stay within
-         * the loop, it will not be duplicated, and therefore the resulting object will reference
-         * itself, and not a second (different) object.
+         * unrolling/peeling: If the VirtualInstanceNode is not duplicated correctly with the loop,
+         * the resulting object will reference itself, and not a second (different) object.
          */
-        TestObject2 obj = staticObj;
+        TestClassObject obj = staticObj;
         for (int i = 0; i < 2; i++) {
-            obj = new TestObject2(obj);
+            obj = new TestClassObject(obj);
         }
-        return obj.o;
+        return obj.x;
     }
 
     @Test
     public void testFullyUnrolledLoop() {
-        testEscapeAnalysisUnrolled("testFullyUnrolledLoopSnippet");
+        prepareGraph("testFullyUnrolledLoopSnippet", false);
+        new LoopFullUnrollPhase(new CanonicalizerPhase(true)).apply(graph, context);
+        new PartialEscapePhase(false, new CanonicalizerPhase(true)).apply(graph, context);
+        Assert.assertTrue(returnNode.result() instanceof AllocatedObjectNode);
+        CommitAllocationNode commit = ((AllocatedObjectNode) returnNode.result()).getCommit();
+        Assert.assertEquals(2, commit.getValues().size());
+        Assert.assertEquals(1, commit.getVirtualObjects().size());
+        Assert.assertTrue("non-cyclic data structure expected", commit.getVirtualObjects().get(0) != commit.getValues().get(0));
     }
 
-    private static Object staticField;
+    @SuppressWarnings("unused") private static Object staticField;
 
-    private static TestObject2 inlinedPart(TestObject2 obj) {
-        TestObject2 ret = new TestObject2(obj);
+    private static TestClassObject inlinedPart(TestClassObject obj) {
+        TestClassObject ret = new TestClassObject(obj);
         staticField = null;
         return ret;
     }
 
     public static Object testPeeledLoopSnippet() {
-        TestObject2 obj = staticObj;
+        TestClassObject obj = staticObj;
         int i = 0;
         do {
             obj = inlinedPart(obj);
         } while (i++ < 10);
         staticField = obj;
-        return obj.o;
+        return obj.x;
     }
 
     @Test
     public void testPeeledLoop() {
-        testEscapeAnalysisPeeled("testPeeledLoopSnippet");
-    }
-
-    private StructuredGraph graph;
-    private HighTierContext context;
-    private ReturnNode returnNode;
-
-    private void testEscapeAnalysis(String snippet, final Constant expectedConstantResult, final boolean iterativeEscapeAnalysis) {
-        prepareGraph(snippet, iterativeEscapeAnalysis);
-        if (expectedConstantResult != null) {
-            Assert.assertTrue(returnNode.result().toString(), returnNode.result().isConstant());
-            Assert.assertEquals(expectedConstantResult, returnNode.result().asConstant());
-        }
-        int newInstanceCount = graph.getNodes().filter(NewInstanceNode.class).count() + graph.getNodes().filter(NewArrayNode.class).count() +
-                        graph.getNodes().filter(CommitAllocationNode.class).count();
-        Assert.assertEquals(0, newInstanceCount);
-    }
-
-    private void testEscapeAnalysisUnrolled(String snippet) {
-        prepareGraph(snippet, false);
-        new LoopFullUnrollPhase(new CanonicalizerPhase(true)).apply(graph, context);
-        new PartialEscapePhase(false, new CanonicalizerPhase(true)).apply(graph, context);
-        Assert.assertTrue(returnNode.result() instanceof AllocatedObjectNode);
-        CommitAllocationNode commit = ((AllocatedObjectNode) returnNode.result()).getCommit();
-        Assert.assertEquals(1, commit.getValues().size());
-        Assert.assertEquals(1, commit.getVirtualObjects().size());
-        Assert.assertTrue("non-cyclic data structure expected", commit.getVirtualObjects().get(0) != commit.getValues().get(0));
-    }
-
-    private void testEscapeAnalysisPeeled(String snippet) {
-        prepareGraph(snippet, false);
+        prepareGraph("testPeeledLoopSnippet", false);
         new LoopTransformHighPhase().apply(graph);
         new LoopTransformLowPhase().apply(graph);
         new SchedulePhase().apply(graph);
     }
-
-    private void prepareGraph(String snippet, final boolean iterativeEscapeAnalysis) {
-        graph = new StructuredGraph(getMetaAccess().lookupJavaMethod(getMethod(snippet)));
-        Debug.scope("GraalCompiler", new Object[]{graph, getMetaAccess().lookupJavaMethod(getMethod(snippet)), getCodeCache()}, new Runnable() {
-
-            public void run() {
-                new GraphBuilderPhase(getMetaAccess(), getForeignCalls(), GraphBuilderConfiguration.getEagerDefault(), OptimisticOptimizations.ALL).apply(graph);
-                Assumptions assumptions = new Assumptions(false);
-                context = new HighTierContext(getProviders(), assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL);
-                new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context);
-                new DeadCodeEliminationPhase().apply(graph);
-                new CanonicalizerPhase(true).apply(graph, context);
-                new PartialEscapePhase(iterativeEscapeAnalysis, new CanonicalizerPhase(true)).apply(graph, context);
-                Assert.assertEquals(1, graph.getNodes().filter(ReturnNode.class).count());
-                returnNode = graph.getNodes().filter(ReturnNode.class).first();
-            }
-        });
-    }
 }
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/IterativeInliningTest.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/IterativeInliningTest.java	Wed Oct 30 11:08:00 2013 +0100
@@ -30,6 +30,7 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.compiler.test.*;
+import com.oracle.graal.compiler.test.ea.EATestBase.TestClassInt;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.phases.*;
@@ -50,14 +51,10 @@
         }
     }
 
-    public static class TestInt implements Callable<Integer> {
-
-        public int x;
-        public int y;
+    public static class TestInt extends TestClassInt implements Callable<Integer> {
 
         public TestInt(int x, int y) {
-            this.x = x;
-            this.y = y;
+            super(x, y);
         }
 
         @Override
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Wed Oct 30 11:08:00 2013 +0100
@@ -23,29 +23,23 @@
 package com.oracle.graal.compiler.test.ea;
 
 import java.lang.ref.*;
-import java.util.concurrent.*;
 
 import org.junit.*;
 
-import com.oracle.graal.api.code.*;
 import com.oracle.graal.compiler.test.*;
-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.util.*;
 import com.oracle.graal.nodes.virtual.*;
-import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.graph.*;
-import com.oracle.graal.phases.tiers.*;
-import com.oracle.graal.virtual.phases.ea.*;
 
 /**
  * The PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct
  * values.
  */
-public class PartialEscapeAnalysisTest extends GraalCompilerTest {
+public class PartialEscapeAnalysisTest extends EATestBase {
 
     public static class TestObject {
 
@@ -71,7 +65,7 @@
 
     @Test
     public void test1() {
-        testMaterialize("test1Snippet", 0.25, 1);
+        testPartialEscapeAnalysis("test1Snippet", 0.25, 1);
     }
 
     @SuppressWarnings("all")
@@ -90,7 +84,7 @@
 
     @Test
     public void test2() {
-        testMaterialize("test2Snippet", 1.5, 3, LoadIndexedNode.class);
+        testPartialEscapeAnalysis("test2Snippet", 1.5, 3, LoadIndexedNode.class);
     }
 
     public static Object test2Snippet(int a, Object x, Object y, Object z) {
@@ -109,7 +103,7 @@
 
     @Test
     public void test3() {
-        testMaterialize("test3Snippet", 0.5, 1, StoreFieldNode.class, LoadFieldNode.class);
+        testPartialEscapeAnalysis("test3Snippet", 0.5, 1, StoreFieldNode.class, LoadFieldNode.class);
     }
 
     public static Object test3Snippet(int a) {
@@ -127,7 +121,7 @@
 
     @Test
     public void testCache() {
-        testMaterialize("testCacheSnippet", 0.75, 1);
+        testPartialEscapeAnalysis("testCacheSnippet", 0.75, 1);
     }
 
     public static class CacheKey {
@@ -175,57 +169,40 @@
 
     @Test
     public void testReference1() {
-        assertEquals(1, processMethod("testReference1Snippet").getNodes().filter(NewInstanceNode.class).count());
+        prepareGraph("testReference1Snippet", false);
+        assertEquals(1, graph.getNodes().filter(NewInstanceNode.class).count());
     }
 
     @SafeVarargs
-    final void testMaterialize(final String snippet, double expectedProbability, int expectedCount, Class<? extends Node>... invalidNodeClasses) {
-        StructuredGraph result = processMethod(snippet);
+    protected final void testPartialEscapeAnalysis(final String snippet, double expectedProbability, int expectedCount, Class<? extends Node>... invalidNodeClasses) {
+        prepareGraph(snippet, false);
+        for (MergeNode merge : graph.getNodes(MergeNode.class)) {
+            merge.setStateAfter(null);
+        }
+        new DeadCodeEliminationPhase().apply(graph);
+        new CanonicalizerPhase(true).apply(graph, context);
         try {
-            Assert.assertTrue("partial escape analysis should have removed all NewInstanceNode allocations", result.getNodes().filter(NewInstanceNode.class).isEmpty());
-            Assert.assertTrue("partial escape analysis should have removed all NewArrayNode allocations", result.getNodes().filter(NewArrayNode.class).isEmpty());
+            Assert.assertTrue("partial escape analysis should have removed all NewInstanceNode allocations", graph.getNodes().filter(NewInstanceNode.class).isEmpty());
+            Assert.assertTrue("partial escape analysis should have removed all NewArrayNode allocations", graph.getNodes().filter(NewArrayNode.class).isEmpty());
 
-            NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(result).apply();
+            NodesToDoubles nodeProbabilities = new ComputeProbabilityClosure(graph).apply();
             double probabilitySum = 0;
             int materializeCount = 0;
-            for (CommitAllocationNode materialize : result.getNodes().filter(CommitAllocationNode.class)) {
+            for (CommitAllocationNode materialize : graph.getNodes().filter(CommitAllocationNode.class)) {
                 probabilitySum += nodeProbabilities.get(materialize) * materialize.getVirtualObjects().size();
                 materializeCount += materialize.getVirtualObjects().size();
             }
             Assert.assertEquals("unexpected number of MaterializeObjectNodes", expectedCount, materializeCount);
             Assert.assertEquals("unexpected probability of MaterializeObjectNodes", expectedProbability, probabilitySum, 0.01);
-            for (Node node : result.getNodes()) {
+            for (Node node : graph.getNodes()) {
                 for (Class<? extends Node> clazz : invalidNodeClasses) {
                     Assert.assertFalse("instance of invalid class: " + clazz.getSimpleName(), clazz.isInstance(node) && node.usages().isNotEmpty());
                 }
             }
         } catch (AssertionError e) {
-            TypeSystemTest.outputGraph(result, snippet + ": " + e.getMessage());
+            TypeSystemTest.outputGraph(graph, snippet + ": " + e.getMessage());
             throw e;
         }
     }
 
-    private StructuredGraph processMethod(final String snippet) {
-        return Debug.scope("PartialEscapeAnalysisTest " + snippet, new DebugDumpScope(snippet), new Callable<StructuredGraph>() {
-
-            @Override
-            public StructuredGraph call() {
-                StructuredGraph graph = parse(snippet);
-
-                Assumptions assumptions = new Assumptions(false);
-                HighTierContext context = new HighTierContext(getProviders(), assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL);
-                new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context);
-                new DeadCodeEliminationPhase().apply(graph);
-                new CanonicalizerPhase(true).apply(graph, context);
-                new PartialEscapePhase(false, new CanonicalizerPhase(true)).apply(graph, context);
-
-                for (MergeNode merge : graph.getNodes(MergeNode.class)) {
-                    merge.setStateAfter(null);
-                }
-                new DeadCodeEliminationPhase().apply(graph);
-                new CanonicalizerPhase(true).apply(graph, context);
-                return graph;
-            }
-        });
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/UnsafeEATest.java	Wed Oct 30 11:08:00 2013 +0100
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.test.ea;
+
+import org.junit.*;
+
+import sun.misc.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+
+public class UnsafeEATest extends EATestBase {
+
+    public static int zero = 0;
+
+    private static final Unsafe unsafe;
+    private static final long fieldOffsetX;
+    private static final long fieldOffsetY;
+
+    static {
+        unsafe = UnsafeAccess.unsafe;
+        try {
+            fieldOffsetX = unsafe.objectFieldOffset(TestClassInt.class.getField("x"));
+            fieldOffsetY = unsafe.objectFieldOffset(TestClassInt.class.getField("y"));
+            assert fieldOffsetY == fieldOffsetX + 4;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Test
+    public void testSimpleInt() {
+        testEscapeAnalysis("testSimpleIntSnippet", Constant.forInt(101), false);
+    }
+
+    public static int testSimpleIntSnippet() {
+        TestClassInt x = new TestClassInt();
+        unsafe.putInt(x, fieldOffsetX, 101);
+        return unsafe.getInt(x, fieldOffsetX);
+    }
+
+    @Test
+    public void testMaterializedInt() {
+        test("testMaterializedIntSnippet");
+    }
+
+    public static TestClassInt testMaterializedIntSnippet() {
+        TestClassInt x = new TestClassInt();
+        unsafe.putInt(x, fieldOffsetX, 101);
+        return x;
+    }
+
+    @Test
+    public void testSimpleDouble() {
+        testEscapeAnalysis("testSimpleDoubleSnippet", Constant.forDouble(10.1), false);
+    }
+
+    public static double testSimpleDoubleSnippet() {
+        TestClassInt x = new TestClassInt();
+        unsafe.putDouble(x, fieldOffsetX, 10.1);
+        return unsafe.getDouble(x, fieldOffsetX);
+    }
+
+    @Test
+    public void testMergedDouble() {
+        testEscapeAnalysis("testMergedDoubleSnippet", null, false);
+        Assert.assertTrue(returnNode.result() instanceof PhiNode);
+        PhiNode phi = (PhiNode) returnNode.result();
+        Assert.assertTrue(phi.valueAt(0) instanceof LoadFieldNode);
+        Assert.assertTrue(phi.valueAt(1) instanceof LoadFieldNode);
+    }
+
+    public static double testMergedDoubleSnippet(boolean a) {
+        TestClassInt x;
+        if (a) {
+            x = new TestClassInt(0, 0);
+            unsafe.putDouble(x, fieldOffsetX, doubleField);
+        } else {
+            x = new TestClassInt();
+            unsafe.putDouble(x, fieldOffsetX, doubleField2);
+        }
+        return unsafe.getDouble(x, fieldOffsetX);
+    }
+
+    @Test
+    public void testMaterializedDouble() {
+        test("testMaterializedDoubleSnippet");
+    }
+
+    public static TestClassInt testMaterializedDoubleSnippet() {
+        TestClassInt x = new TestClassInt();
+        unsafe.putDouble(x, fieldOffsetX, 10.1);
+        return x;
+    }
+
+    @Test
+    public void testDeoptDoubleVar() {
+        test("testDeoptDoubleVarSnippet");
+    }
+
+    public static double doubleField = 10.1e99;
+    public static double doubleField2;
+
+    public static TestClassInt testDeoptDoubleVarSnippet() {
+        TestClassInt x = new TestClassInt();
+        unsafe.putDouble(x, fieldOffsetX, doubleField);
+        doubleField2 = 123;
+        try {
+            doubleField = ((int) unsafe.getDouble(x, fieldOffsetX)) / zero;
+        } catch (RuntimeException e) {
+            return x;
+        }
+        return x;
+    }
+
+    @Test
+    public void testDeoptDoubleConstant() {
+        test("testDeoptDoubleConstantSnippet");
+    }
+
+    public static TestClassInt testDeoptDoubleConstantSnippet() {
+        TestClassInt x = new TestClassInt();
+        unsafe.putDouble(x, fieldOffsetX, 10.123);
+        doubleField2 = 123;
+        try {
+            doubleField = ((int) unsafe.getDouble(x, fieldOffsetX)) / zero;
+        } catch (RuntimeException e) {
+            return x;
+        }
+        return x;
+    }
+
+    @Test
+    public void testDeoptLongVar() {
+        test("testDeoptLongVarSnippet");
+    }
+
+    public static long longField = 0x133443218aaaffffL;
+    public static long longField2;
+
+    public static TestClassInt testDeoptLongVarSnippet() {
+        TestClassInt x = new TestClassInt();
+        unsafe.putLong(x, fieldOffsetX, longField);
+        longField2 = 123;
+        try {
+            longField = unsafe.getLong(x, fieldOffsetX) / zero;
+        } catch (RuntimeException e) {
+            return x;
+        }
+        return x;
+    }
+
+    @Test
+    public void testDeoptLongConstant() {
+        test("testDeoptLongConstantSnippet");
+    }
+
+    public static TestClassInt testDeoptLongConstantSnippet() {
+        TestClassInt x = new TestClassInt();
+        unsafe.putLong(x, fieldOffsetX, 0x2222222210123L);
+        longField2 = 123;
+        try {
+            longField = unsafe.getLong(x, fieldOffsetX) / zero;
+        } catch (RuntimeException e) {
+            return x;
+        }
+        return x;
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostLoweringProvider.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotHostLoweringProvider.java	Wed Oct 30 11:08:00 2013 +0100
@@ -350,9 +350,12 @@
                                 // Constant.illegal is always the defaultForKind, so it is skipped
                                 Kind componentKind = element.getKind();
                                 Kind accessKind;
-                                if (value.kind().getStackKind() != componentKind.getStackKind()) {
-                                    assert value.kind() == Kind.Long || value.kind() == Kind.Double;
-                                    accessKind = value.kind();
+                                Kind valueKind = value.kind();
+                                if (valueKind.getStackKind() != componentKind.getStackKind()) {
+                                    // Given how Truffle uses unsafe, it can happen that
+                                    // valueKind is Kind.Int
+                                    // assert valueKind == Kind.Long || valueKind == Kind.Double;
+                                    accessKind = valueKind;
                                 } else {
                                     accessKind = componentKind;
                                 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java	Wed Oct 30 11:08:00 2013 +0100
@@ -194,28 +194,41 @@
              */
             assert !Modifier.isStatic(flags);
             Object object = receiver.asObject();
-            if (Modifier.isFinal(getModifiers())) {
-                Constant value = readValue(receiver);
-                if (assumeNonStaticFinalFieldsAsFinal(object.getClass()) || !value.isDefaultForKind()) {
-                    return value;
-                }
-            } else if (isStable()) {
-                Constant value = readValue(receiver);
-                if (assumeDefaultStableFieldsAsFinal(object.getClass()) || !value.isDefaultForKind()) {
-                    return value;
-                }
-            } else {
-                Class<?> clazz = object.getClass();
-                if (StableOptionValue.class.isAssignableFrom(clazz)) {
-                    assert getName().equals("value") : "Unexpected field in " + StableOptionValue.class.getName() + " hierarchy:" + this;
-                    StableOptionValue<?> option = (StableOptionValue<?>) object;
-                    return Constant.forObject(option.getValue());
+
+            // Canonicalization may attempt to process an unsafe read before
+            // processing a guard (e.g. a type check) for this read
+            // so we need to type check the object being read
+            if (isInObject(object)) {
+                if (Modifier.isFinal(getModifiers())) {
+                    Constant value = readValue(receiver);
+                    if (assumeNonStaticFinalFieldsAsFinal(object.getClass()) || !value.isDefaultForKind()) {
+                        return value;
+                    }
+                } else if (isStable()) {
+                    Constant value = readValue(receiver);
+                    if (assumeDefaultStableFieldsAsFinal(object.getClass()) || !value.isDefaultForKind()) {
+                        return value;
+                    }
+                } else {
+                    Class<?> clazz = object.getClass();
+                    if (StableOptionValue.class.isAssignableFrom(clazz)) {
+                        assert getName().equals("value") : "Unexpected field in " + StableOptionValue.class.getName() + " hierarchy:" + this;
+                        StableOptionValue<?> option = (StableOptionValue<?>) object;
+                        return Constant.forObject(option.getValue());
+                    }
                 }
             }
         }
         return null;
     }
 
+    /**
+     * Determines if a given object contains this field.
+     */
+    public boolean isInObject(Object object) {
+        return getDeclaringClass().isAssignableFrom(HotSpotResolvedObjectType.fromClass(object.getClass()));
+    }
+
     @Override
     public Constant readValue(Constant receiver) {
         if (receiver == null) {
@@ -226,7 +239,9 @@
             return null;
         } else {
             assert !Modifier.isStatic(flags);
-            return runtime().getHostProviders().getConstantReflection().readUnsafeConstant(getKind(), receiver.asObject(), offset, getKind() == Kind.Object);
+            Object object = receiver.asObject();
+            assert object != null && isInObject(object);
+            return runtime().getHostProviders().getConstantReflection().readUnsafeConstant(getKind(), object, offset, getKind() == Kind.Object);
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorExitNode.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorExitNode.java	Wed Oct 30 11:08:00 2013 +0100
@@ -24,22 +24,24 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
 
 /**
- * The {@code MonitorEnterNode} represents a monitor release.
+ * The {@code MonitorExitNode} represents a monitor release. If it is the release of the monitor of
+ * a synchronized method, then the return value of the method will be referenced, so that it will be
+ * materialized before releasing the monitor.
  */
-public final class MonitorExitNode extends AccessMonitorNode implements Virtualizable, Lowerable, IterableNodeType, MonitorExit, MemoryCheckpoint.Single, MonitorReference {
+public final class MonitorExitNode extends AccessMonitorNode implements Virtualizable, Simplifiable, Lowerable, IterableNodeType, MonitorExit, MemoryCheckpoint.Single, MonitorReference {
+
+    @Input private ValueNode escapedReturnValue;
 
     private int lockDepth;
-    @Input private ValueNode escapedReturnValue;
 
     /**
      * Creates a new MonitorExitNode.
-     * 
-     * @param object the instruction produces the object value
      */
     public MonitorExitNode(ValueNode object, ValueNode escapedReturnValue, int lockDepth) {
         super(object);
@@ -58,6 +60,15 @@
     }
 
     @Override
+    public void simplify(SimplifierTool tool) {
+        if (escapedReturnValue != null && stateAfter().bci != FrameState.AFTER_BCI) {
+            ValueNode returnValue = escapedReturnValue;
+            setEscapedReturnValue(null);
+            tool.removeIfUnused(returnValue);
+        }
+    }
+
+    @Override
     public void lower(LoweringTool tool) {
         tool.getLowerer().lower(this, tool);
     }
@@ -72,20 +83,13 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        /*
-         * The last MonitorExitNode of a synchronized method cannot be removed anyway, and we need
-         * it to materialize the return value.
-         * 
-         * TODO: replace this with correct handling of AFTER_BCI frame states in the runtime.
-         */
-        if (stateAfter().bci != FrameState.AFTER_BCI) {
-            setEscapedReturnValue(null);
-            State state = tool.getObjectState(object());
-            if (state != null && state.getState() == EscapeState.Virtual && state.getVirtualObject().hasIdentity()) {
-                int removedLock = state.removeLock();
-                assert removedLock == getLockDepth();
-                tool.delete();
-            }
+        State state = tool.getObjectState(object());
+        // the monitor exit for a synchronized method should never be virtualized
+        assert stateAfter().bci != FrameState.AFTER_BCI || state == null;
+        if (state != null && state.getState() == EscapeState.Virtual && state.getVirtualObject().hasIdentity()) {
+            int removedLock = state.removeLock();
+            assert removedLock == getLockDepth();
+            tool.delete();
         }
     }
 }
--- a/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64ConvertSnippets.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.replacements.amd64/src/com/oracle/graal/replacements/amd64/AMD64ConvertSnippets.java	Wed Oct 30 11:08:00 2013 +0100
@@ -167,20 +167,13 @@
 
             StructuredGraph graph = convert.graph();
 
-            // Insert a unique placeholder node in place of the Convert node so that the
-            // Convert node can be used as an input to the snippet. All usage of the
-            // Convert node are replaced by the placeholder which in turn is replaced by the
-            // snippet.
-
-            LocalNode replacee = graph.addWithoutUnique(new LocalNode(Integer.MAX_VALUE, convert.stamp()));
-            convert.replaceAtUsages(replacee);
             Arguments args = new Arguments(key, graph.getGuardsStage());
             args.add("input", convert.value());
-            args.add("result", convert.graph().unique(new AMD64ConvertNode(convert.opcode, convert.value())));
+            args.add("result", graph.unique(new AMD64ConvertNode(convert.opcode, convert.value())));
 
             SnippetTemplate template = template(args);
             Debug.log("Lowering %s in %s: node=%s, template=%s, arguments=%s", convert.opcode, graph, convert, template, args);
-            template.instantiate(providers.getMetaAccess(), replacee, DEFAULT_REPLACER, tool, args);
+            template.instantiate(providers.getMetaAccess(), convert, DEFAULT_REPLACER, tool, args);
             graph.removeFloating(convert);
         }
     }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetCounter.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetCounter.java	Wed Oct 30 11:08:00 2013 +0100
@@ -134,7 +134,8 @@
      */
     public void inc() {
         if (group != null) {
-            // TODO: instead of ANY_LOCATION we should actually use the location for the field "value".
+            // TODO: instead of ANY_LOCATION we should actually
+            // use the location for the field "value".
             DirectObjectStoreNode.storeLong(this, countOffset(), 0, value + 1, LocationIdentity.ANY_LOCATION);
         }
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Wed Oct 30 11:08:00 2013 +0100
@@ -104,7 +104,7 @@
 
         final StructuredGraph graph = new StructuredGraph(executeHelperMethod);
 
-        Debug.scope("createGraph", new Runnable() {
+        Debug.scope("createGraph", graph, new Runnable() {
 
             @Override
             public void run() {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Wed Oct 30 11:08:00 2013 +0100
@@ -217,7 +217,7 @@
             @Override
             public CompilationResult call() {
                 try (TimerCloseable a = CompilationTime.start()) {
-                    return Debug.scope("GraalCompiler", new Object[]{providers.getCodeCache()}, new Callable<CompilationResult>() {
+                    return Debug.scope("GraalCompiler", new Object[]{graph, providers.getCodeCache()}, new Callable<CompilationResult>() {
                         public CompilationResult call() {
                             CodeCacheProvider codeCache = providers.getCodeCache();
                             CallingConvention cc = getCallingConvention(codeCache, Type.JavaCallee, graph.method(), false);
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue Oct 29 16:55:42 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Wed Oct 30 11:08:00 2013 +0100
@@ -425,7 +425,7 @@
                         // skip the next entry
                         valueIndex++;
                     } else {
-                        assert entryKind.getStackKind() == otherKind.getStackKind() : entryKind + " vs " + otherKind;
+                        assert entryKind.getStackKind() == otherKind.getStackKind() || entryKind.getBitCount() >= otherKind.getBitCount() : entryKind + " vs " + otherKind;
                     }
                     valueIndex++;
                 }
--- a/make/bsd/makefiles/gcc.make	Tue Oct 29 16:55:42 2013 +0100
+++ b/make/bsd/makefiles/gcc.make	Wed Oct 30 11:08:00 2013 +0100
@@ -144,7 +144,8 @@
     PCH_FLAG/sharedRuntimeTrig.o = $(PCH_FLAG/NO_PCH)
     PCH_FLAG/sharedRuntimeTrans.o = $(PCH_FLAG/NO_PCH)
     PCH_FLAG/unsafe.o = $(PCH_FLAG/NO_PCH)
-  
+    PCH_FLAG/graalCompilerToVM.o = $(PCH_FLAG/NO_PCH)
+
   endif
 else # ($(USE_CLANG), true)
   # check for precompiled headers support
@@ -309,17 +310,16 @@
 
 # Work around some compiler bugs.
 ifeq ($(USE_CLANG), true)
+  # Clang 4.2
   ifeq ($(shell expr $(CC_VER_MAJOR) = 4 \& $(CC_VER_MINOR) = 2), 1)
     OPT_CFLAGS/loopTransform.o += $(OPT_CFLAGS/NOOPT)
     OPT_CFLAGS/unsafe.o += -O1
   endif
   # Clang 5.0
   ifeq ($(shell expr $(CC_VER_MAJOR) = 5 \& $(CC_VER_MINOR) = 0), 1)
-    OPT_CFLAGS/graalCompilerToVM.o += -O1
+    OPT_CFLAGS/loopTransform.o += $(OPT_CFLAGS/NOOPT)
     OPT_CFLAGS/unsafe.o += -O1
-    # Specific optimization level plus precompiled headers produces:
-    #     error: __OPTIMIZE_SIZE__ predefined macro was enabled in PCH file but is currently disabled
-    USE_PRECOMPILED_HEADER = 0
+    OPT_CFLAGS/graalCompilerToVM.o += -O1
   endif
 else
   # 6835796. Problem in GCC 4.3.0 with mulnode.o optimized compilation.
@@ -328,6 +328,14 @@
   endif
 endif
 
+# We want to use libc++ on Clang 5.0
+ifeq ($(USE_CLANG), true)
+  # Clang 5.0
+  ifeq ($(shell expr $(CC_VER_MAJOR) = 5 \& $(CC_VER_MINOR) = 0), 1)
+    CFLAGS += -stdlib=libc++
+  endif
+endif
+
 # Flags for generating make dependency flags.
 DEPFLAGS = -MMD -MP -MF $(DEP_DIR)/$(@:%=%.d)
 ifeq ($(USE_CLANG),)
--- a/mx/projects	Tue Oct 29 16:55:42 2013 +0100
+++ b/mx/projects	Wed Oct 30 11:08:00 2013 +0100
@@ -138,6 +138,7 @@
 project@com.oracle.graal.hotspot.sparc@sourceDirs=src
 project@com.oracle.graal.hotspot.sparc@dependencies=com.oracle.graal.compiler.sparc
 project@com.oracle.graal.hotspot.sparc@checkstyle=com.oracle.graal.graph
+project@com.oracle.graal.hotspot.sparc@annotationProcessors=com.oracle.graal.service.processor
 project@com.oracle.graal.hotspot.sparc@javaCompliance=1.7
 project@com.oracle.graal.hotspot.sparc@workingSets=Graal,HotSpot,SPARC
 
--- a/mxtool/mx.py	Tue Oct 29 16:55:42 2013 +0100
+++ b/mxtool/mx.py	Wed Oct 30 11:08:00 2013 +0100
@@ -326,6 +326,24 @@
             self._annotationProcessors = list(ap)
         return self._annotationProcessors
 
+    def update_current_annotation_processors_file(self):
+        aps = self.annotation_processors()
+        outOfDate = False
+        currentApsFile = join(self.dir, '.currentAnnotationProcessors')
+        if exists(currentApsFile):
+            with open(currentApsFile) as fp:
+                currentAps = [l.strip() for l in fp.readlines()]
+                if currentAps != aps:
+                    outOfDate = True
+        else:
+            outOfDate = True
+        if outOfDate:
+            with open(currentApsFile, 'w') as fp:
+                for ap in aps:
+                    print >> fp, ap
+        return outOfDate
+
+
 class Library(Dependency):
     def __init__(self, suite, name, path, mustExist, urls, sourcePath, sourceUrls):
         Dependency.__init__(self, suite, name)
@@ -769,7 +787,7 @@
             abort('Missing "suite=<name>" in ' + projectsFile)
 
     def _commands_name(self):
-        return 'mx_' + self.name.replace('-', '_')
+        return 'mx_' + self.name.replace('-','_')
 
     def _find_commands(self, name):
         commandsPath = join(self.mxDir, name + '.py')
@@ -804,6 +822,12 @@
             mod.mx_init(self)
             self.commands = mod
 
+    def _imports_file(self):
+        return join(self.mxDir, 'imports')
+
+    def import_timestamp(self):
+        return TimeStampFile(self._imports_file())
+
     def visit_imports(self, visitor, **extra_args):
         """
         Visitor support for the imports file.
@@ -817,7 +841,7 @@
         as this function only visits the imports of a single suite. If a (recursive) visitor function
         wishes to visit a suite exactly once, it must manage that through extra_args.
         """
-        importsFile = join(self.mxDir, 'imports')
+        importsFile = self._imports_file()
         if exists(importsFile):
             update_versions = extra_args.has_key('update_versions') and extra_args['update_versions']
             out = StringIO.StringIO() if update_versions else None
@@ -1847,6 +1871,22 @@
     if args.java:
         ideinit([], refreshOnly=True, buildProcessorJars=False)
 
+    def prepareOutputDirs(p, clean):
+        outputDir = p.output_dir()
+        if exists(outputDir):
+            if clean:
+                log('Cleaning {0}...'.format(outputDir))
+                shutil.rmtree(outputDir)
+                os.mkdir(outputDir)
+        else:
+            os.mkdir(outputDir)
+        genDir = p.source_gen_dir()
+        if genDir != '' and exists(genDir) and clean:
+            log('Cleaning {0}...'.format(genDir))
+            for f in os.listdir(genDir):
+                shutil.rmtree(join(genDir, f))
+        return outputDir
+
     for p in sortedProjects:
         if p.native:
             if args.native:
@@ -1869,14 +1909,7 @@
             log('Excluding {0} from build (Java compliance level {1} required)'.format(p.name, p.javaCompliance))
             continue
 
-        outputDir = p.output_dir()
-        if exists(outputDir):
-            if args.clean:
-                log('Cleaning {0}...'.format(outputDir))
-                shutil.rmtree(outputDir)
-                os.mkdir(outputDir)
-        else:
-            os.mkdir(outputDir)
+        outputDir = prepareOutputDirs(p, args.clean)
 
         cp = classpath(p.name, includeSelf=True)
         sourceDirs = p.source_dirs()
@@ -1886,6 +1919,7 @@
                 if dep.name in built:
                     mustBuild = True
 
+
         jasminAvailable = None
         javafilelist = []
         for sourceDir in sourceDirs:
@@ -1935,11 +1969,17 @@
 
                 if not mustBuild:
                     for javafile in javafiles:
-                        classfile = outputDir + javafile[len(sourceDir):-len('java')] + 'class'
-                        if not exists(classfile) or os.path.getmtime(javafile) > os.path.getmtime(classfile):
+                        classfile = TimeStampFile(outputDir + javafile[len(sourceDir):-len('java')] + 'class')
+                        if not classfile.exists() or classfile.isOlderThan(javafile):
                             mustBuild = True
                             break
 
+        aps = p.annotation_processors()
+        apsOutOfDate = p.update_current_annotation_processors_file()
+        if apsOutOfDate:
+            logv('[annotation processors for {0} changed]'.format(p.name))
+            mustBuild = True
+
         if not mustBuild:
             logv('[all class files for {0} are up to date - skipping]'.format(p.name))
             continue
@@ -1948,6 +1988,9 @@
             logv('[no Java sources for {0} - skipping]'.format(p.name))
             continue
 
+        # Ensure that the output directories are clean
+        prepareOutputDirs(p, True)
+
         built.add(p.name)
 
         argfileName = join(p.dir, 'javafilelist.txt')
@@ -1957,9 +2000,8 @@
 
         processorArgs = []
 
-        ap = p.annotation_processors()
-        if len(ap) > 0:
-            processorPath = classpath(ap, resolve=True)
+        if len(aps) > 0:
+            processorPath = classpath(aps, resolve=True)
             genDir = p.source_gen_dir()
             if exists(genDir):
                 shutil.rmtree(genDir)
@@ -2132,13 +2174,17 @@
     return 0
 
 def processorjars():
-
+    for s in suites(True):
+        _processorjars_suite(s)
+
+def _processorjars_suite(s):
     projs = set()
-    for p in sorted_deps():
+    candidates = sorted_project_deps(s.projects)
+    for p in candidates:
         if _isAnnotationProcessorDependency(p):
             projs.add(p)
 
-    if len(projs) < 0:
+    if len(projs) <= 0:
         return
 
     pnames = [p.name for p in projs]
@@ -2378,7 +2424,12 @@
     def isOlderThan(self, arg):
         if not self.timestamp:
             return True
-        if isinstance(arg, types.ListType):
+        if isinstance(arg, TimeStampFile):
+            if arg.timestamp is None:
+                return False
+            else:
+                return arg.timestamp > self.timestamp
+        elif isinstance(arg, types.ListType):
             files = arg
         else:
             files = [arg]
@@ -2641,16 +2692,15 @@
         igv.close('graph')
         igv.close('group')
         igv.close('graphDocument')
-        
+
         if args.igv:
             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
             s.connect(('127.0.0.1', 4444))
             s.send(igv.xml())
         else:
-            print igv.xml(indent='  ', newl='\n');
+            print igv.xml(indent='  ', newl='\n')
         return
-        
-            
+
     print 'digraph projects {'
     print 'rankdir=BT;'
     print 'node [shape=rect];'
@@ -2787,20 +2837,26 @@
 
     generate_eclipse_workingsets()
 
+def _check_ide_timestamp(suite, timestamp):
+    """return True if and only if the projects file, imports file, and mx itself are all older than timestamp"""
+    projectsFile = join(suite.mxDir, 'projects')
+    projectsFileOlder = not timestamp.isOlderThan(projectsFile)
+    importsFileOlder = not timestamp.isOlderThan(suite.import_timestamp())
+    # Assume that any mx change might imply changes to the generated IDE files
+    mxOlder = not timestamp.isOlderThan(__file__)
+    return projectsFileOlder and importsFileOlder and mxOlder
 
 def _eclipseinit_suite(args, suite, buildProcessorJars=True, refreshOnly=False):
-    projectsFile = join(suite.mxDir, 'projects')
     timestamp = TimeStampFile(join(suite.mxDir, 'eclipseinit.timestamp'))
     if refreshOnly and not timestamp.exists():
         return
 
-    if not timestamp.isOlderThan(projectsFile) and not TimeStampFile(projectsFile).isOlderThan(__file__):
+    if _check_ide_timestamp(suite, timestamp):
         logv('[Eclipse configurations are up to date - skipping]')
         return
 
     if buildProcessorJars:
-        # todo suite specific
-        processorjars()
+        _processorjars_suite(suite)
 
     projToDist = dict()
     for dist in _dists.values():
@@ -3199,12 +3255,11 @@
         _netbeansinit_suite(args, suite, refreshOnly, buildProcessorJars)
 
 def _netbeansinit_suite(args, suite, refreshOnly=False, buildProcessorJars=True):
-    projectsFile = join(suite.mxDir, 'projects')
     timestamp = TimeStampFile(join(suite.mxDir, 'netbeansinit.timestamp'))
     if refreshOnly and not timestamp.exists():
         return
 
-    if not timestamp.isOlderThan(projectsFile) and not TimeStampFile(projectsFile).isOlderThan(__file__):
+    if _check_ide_timestamp(suite, timestamp):
         logv('[NetBeans configurations are up to date - skipping]')
         return