changeset 14619:ed3bfe43d772

Merge.
author Christian Humer <christian.humer@gmail.com>
date Wed, 19 Mar 2014 21:10:34 +0100
parents affd2b3b0cbb (current diff) c03d4de23448 (diff)
children 5507d2f586ef 4f8268dee8aa
files
diffstat 23 files changed, 486 insertions(+), 143 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/RegisterConfig.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/RegisterConfig.java	Wed Mar 19 21:10:34 2014 +0100
@@ -102,4 +102,10 @@
      * @return the register playing the role specified by {@code id}
      */
     Register getRegisterForRole(int id);
+
+    /**
+     * Determines if all {@link #getAllocatableRegisters() allocatable} registers are
+     * {@link #getCallerSaveRegisters() caller saved}.
+     */
+    boolean areAllAllocatableRegistersCallerSaved();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PoorMansEATest.java	Wed Mar 19 21:10:34 2014 +0100
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014, 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.compiler.test.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.debug.Debug.Scope;
+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.phases.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.tiers.*;
+
+/**
+ * Tests {@link AbstractNewObjectNode#simplify(com.oracle.graal.graph.spi.SimplifierTool)}.
+ * 
+ */
+public class PoorMansEATest extends GraalCompilerTest {
+    public static class A {
+        public A obj;
+    }
+
+    public static A test1Snippet() {
+        A a = new A();
+        a.obj = a;
+        return null;
+    }
+
+    @Test
+    public void test1() {
+        test("test1Snippet");
+    }
+
+    private void test(final String snippet) {
+        try (Scope s = Debug.scope("PoorMansEATest", new DebugDumpScope(snippet))) {
+            StructuredGraph graph = parse(snippet);
+            Assumptions assumptions = new Assumptions(false);
+            HighTierContext highTierContext = new HighTierContext(getProviders(), assumptions, null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
+            new InliningPhase(new CanonicalizerPhase(true)).apply(graph, highTierContext);
+            PhaseContext context = new PhaseContext(getProviders(), assumptions);
+            new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context);
+
+            // remove framestates in order to trigger the simplification.
+            cleanup: for (FrameState fs : graph.getNodes(FrameState.class).snapshot()) {
+                for (Node input : fs.inputs()) {
+                    if (input instanceof NewInstanceNode) {
+                        fs.replaceAtUsages(null);
+                        fs.safeDelete();
+                        continue cleanup;
+                    }
+                }
+            }
+            new CanonicalizerPhase(true).apply(graph, context);
+        } catch (Throwable e) {
+            throw Debug.handle(e);
+        }
+    }
+}
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Wed Mar 19 21:10:34 2014 +0100
@@ -289,9 +289,10 @@
 
         if (Debug.isMeterEnabled()) {
             List<DataPatch> ldp = compilationResult.getDataReferences();
-            DebugMetric[] dms = new DebugMetric[Kind.values().length];
+            Kind[] kindValues = Kind.values();
+            DebugMetric[] dms = new DebugMetric[kindValues.length];
             for (int i = 0; i < dms.length; i++) {
-                dms[i] = Debug.metric("DataPatches-" + Kind.values()[i].toString());
+                dms[i] = Debug.metric("DataPatches-%s", kindValues[i]);
             }
 
             for (DataPatch dp : ldp) {
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java	Wed Mar 19 21:10:34 2014 +0100
@@ -70,11 +70,8 @@
         super(allocator, unhandledFixedFirst, unhandledAnyFirst);
 
         // If all allocatable registers are caller saved, then no registers are live across a call
-        // site.
-        // The register allocator can save time not trying to find a register at a call site.
-        HashSet<Register> registers = new HashSet<>(Arrays.asList(allocator.frameMap.registerConfig.getAllocatableRegisters()));
-        registers.removeAll(Arrays.asList(allocator.frameMap.registerConfig.getCallerSaveRegisters()));
-        allocator.callKillsRegisters = registers.size() == 0;
+        // site. The register allocator can save time not trying to find a register at a call site.
+        allocator.callKillsRegisters = allocator.frameMap.registerConfig.areAllAllocatableRegistersCallerSaved();
 
         moveResolver = new MoveResolver(allocator);
         spillIntervals = Util.uncheckedCast(new List[allocator.registers.length]);
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Wed Mar 19 21:10:34 2014 +0100
@@ -23,6 +23,7 @@
 package com.oracle.graal.debug;
 
 import static com.oracle.graal.debug.Debug.Initialization.*;
+import static java.util.FormattableFlags.*;
 
 import java.io.*;
 import java.util.*;
@@ -167,9 +168,9 @@
      * @return the scope entered by this method which will be exited when its {@link Scope#close()}
      *         method is called
      */
-    public static Scope scope(CharSequence name, Object... context) {
+    public static Scope scope(Object name, Object... context) {
         if (ENABLED) {
-            return DebugScope.getInstance().scope(name, null, context);
+            return DebugScope.getInstance().scope(convertFormatArg(name).toString(), null, context);
         } else {
             return null;
         }
@@ -380,13 +381,86 @@
      * A disabled metric has virtually no overhead.
      */
     public static DebugMetric metric(CharSequence name) {
-        if (enabledMetrics != null && enabledMetrics.contains(name.toString())) {
-            return new MetricImpl(name.toString(), false);
-        } else if (ENABLED) {
-            return new MetricImpl(name.toString(), true);
-        } else {
+        if (enabledMetrics == null && !ENABLED) {
             return VOID_METRIC;
         }
+        return createMetric("%s", name, null);
+    }
+
+    public static String applyFormattingFlagsAndWidth(String s, int flags, int width) {
+        if (flags == 0 && width < 0) {
+            return s;
+        }
+        StringBuilder sb = new StringBuilder(s);
+
+        // apply width and justification
+        int len = sb.length();
+        if (len < width) {
+            for (int i = 0; i < width - len; i++) {
+                if ((flags & LEFT_JUSTIFY) == LEFT_JUSTIFY) {
+                    sb.append(' ');
+                } else {
+                    sb.insert(0, ' ');
+                }
+            }
+        }
+
+        String res = sb.toString();
+        if ((flags & UPPERCASE) == UPPERCASE) {
+            res = res.toUpperCase();
+        }
+        return res;
+    }
+
+    /**
+     * Creates a debug metric. Invoking this method is equivalent to:
+     * 
+     * <pre>
+     * Debug.metric(format, arg, null)
+     * </pre>
+     * 
+     * except that the string formatting only happens if metering is enabled.
+     * 
+     * @see #metric(String, Object, Object)
+     */
+    public static DebugMetric metric(String format, Object arg) {
+        if (enabledMetrics == null && !ENABLED) {
+            return VOID_METRIC;
+        }
+        return createMetric(format, arg, null);
+    }
+
+    /**
+     * Creates a debug metric. Invoking this method is equivalent to:
+     * 
+     * <pre>
+     * Debug.metric(String.format(format, arg1, arg2))
+     * </pre>
+     * 
+     * except that the string formatting only happens if metering is enabled. In addition, each
+     * argument is subject to the following type based conversion before being passed as an argument
+     * to {@link String#format(String, Object...)}:
+     * 
+     * <pre>
+     *     Type          | Conversion
+     * ------------------+-----------------
+     *  java.lang.Class  | arg.getSimpleName()
+     *                   |
+     * </pre>
+     * 
+     * @see #metric(CharSequence)
+     */
+    public static DebugMetric metric(String format, Object arg1, Object arg2) {
+        if (enabledMetrics == null && !ENABLED) {
+            return VOID_METRIC;
+        }
+        return createMetric(format, arg1, arg2);
+    }
+
+    private static DebugMetric createMetric(String format, Object arg1, Object arg2) {
+        String name = formatDebugName(format, arg1, arg2);
+        boolean conditional = enabledMetrics != null && enabledMetrics.contains(name);
+        return new MetricImpl(name, conditional);
     }
 
     /**
@@ -533,13 +607,72 @@
      * A disabled timer has virtually no overhead.
      */
     public static DebugTimer timer(CharSequence name) {
-        if (enabledTimers != null && enabledTimers.contains(name.toString())) {
-            return new TimerImpl(name.toString(), false);
-        } else if (ENABLED) {
-            return new TimerImpl(name.toString(), true);
-        } else {
+        if (enabledTimers == null && !ENABLED) {
+            return VOID_TIMER;
+        }
+        return createTimer("%s", name, null);
+    }
+
+    /**
+     * Creates a debug timer. Invoking this method is equivalent to:
+     * 
+     * <pre>
+     * Debug.timer(format, arg, null)
+     * </pre>
+     * 
+     * except that the string formatting only happens if timing is enabled.
+     * 
+     * @see #timer(String, Object, Object)
+     */
+    public static DebugTimer timer(String format, Object arg) {
+        if (enabledTimers == null && !ENABLED) {
             return VOID_TIMER;
         }
+        return createTimer(format, arg, null);
+    }
+
+    /**
+     * Creates a debug timer. Invoking this method is equivalent to:
+     * 
+     * <pre>
+     * Debug.timer(String.format(format, arg1, arg2))
+     * </pre>
+     * 
+     * except that the string formatting only happens if timing is enabled. In addition, each
+     * argument is subject to the following type based conversion before being passed as an argument
+     * to {@link String#format(String, Object...)}:
+     * 
+     * <pre>
+     *     Type          | Conversion
+     * ------------------+-----------------
+     *  java.lang.Class  | arg.getSimpleName()
+     *                   |
+     * </pre>
+     * 
+     * @see #timer(CharSequence)
+     */
+    public static DebugTimer timer(String format, Object arg1, Object arg2) {
+        if (enabledTimers == null && !ENABLED) {
+            return VOID_TIMER;
+        }
+        return createTimer(format, arg1, arg2);
+    }
+
+    public static Object convertFormatArg(Object arg) {
+        if (arg instanceof Class) {
+            return ((Class) arg).getSimpleName();
+        }
+        return arg;
+    }
+
+    private static String formatDebugName(String format, Object arg1, Object arg2) {
+        return String.format(format, convertFormatArg(arg1), convertFormatArg(arg2));
+    }
+
+    private static DebugTimer createTimer(String format, Object arg1, Object arg2) {
+        String name = formatDebugName(format, arg1, arg2);
+        boolean conditional = enabledTimers != null && enabledTimers.contains(name);
+        return new TimerImpl(name, conditional);
     }
 
     private static final DebugTimer VOID_TIMER = new DebugTimer() {
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Wed Mar 19 21:10:34 2014 +0100
@@ -234,7 +234,7 @@
         }
 
         isLeafNode = (this.inputOffsets.length == 0 && this.successorOffsets.length == 0);
-        nodeIterableCount = Debug.metric("NodeIterable_" + shortName);
+        nodeIterableCount = Debug.metric("NodeIterable_%s", shortName);
     }
 
     @Override
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java	Wed Mar 19 21:10:34 2014 +0100
@@ -48,6 +48,8 @@
      */
     private final Register[] callerSaved;
 
+    private final boolean allAllocatableAreCallerSaved;
+
     private final HashMap<PlatformKind, Register[]> categorized = new HashMap<>();
 
     private final RegisterAttributes[] attributesMap;
@@ -152,6 +154,7 @@
         callerSaved = callerSaveSet.toArray(new Register[callerSaveSet.size()]);
         assert callerSaved.length == allocatable.length || RegisterPressure.getValue() != null;
 
+        allAllocatableAreCallerSaved = true;
         attributesMap = RegisterAttributes.createMap(this, AMD64.allRegisters);
     }
 
@@ -161,6 +164,11 @@
     }
 
     @Override
+    public boolean areAllAllocatableRegistersCallerSaved() {
+        return allAllocatableAreCallerSaved;
+    }
+
+    @Override
     public Register getRegisterForRole(int index) {
         throw new UnsupportedOperationException();
     }
--- a/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotRegisterConfig.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotRegisterConfig.java	Wed Mar 19 21:10:34 2014 +0100
@@ -74,6 +74,11 @@
     }
 
     @Override
+    public boolean areAllAllocatableRegistersCallerSaved() {
+        throw GraalInternalError.unimplemented();
+    }
+
+    @Override
     public Register getRegisterForRole(int index) {
         throw GraalInternalError.unimplemented("PTXHotSpotRegisterConfig.getRegisterForRole()");
     }
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotRegisterConfig.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotRegisterConfig.java	Wed Mar 19 21:10:34 2014 +0100
@@ -144,6 +144,11 @@
     }
 
     @Override
+    public boolean areAllAllocatableRegistersCallerSaved() {
+        return false;
+    }
+
+    @Override
     public Register getRegisterForRole(int index) {
         throw new UnsupportedOperationException();
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethod.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethod.java	Wed Mar 19 21:10:34 2014 +0100
@@ -23,11 +23,15 @@
 package com.oracle.graal.hotspot.meta;
 
 import static com.oracle.graal.api.meta.MetaUtil.*;
+import static com.oracle.graal.debug.Debug.*;
+import static java.util.FormattableFlags.*;
+
+import java.util.*;
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.hotspot.*;
 
-public abstract class HotSpotMethod extends CompilerObject implements JavaMethod {
+public abstract class HotSpotMethod extends CompilerObject implements JavaMethod, Formattable {
 
     private static final long serialVersionUID = 7167491397941960839L;
     protected String name;
@@ -54,4 +58,9 @@
         String fmt = String.format("HotSpotMethod<%%%c.%%n(%%p)%s>", h, suffix);
         return format(fmt, this);
     }
+
+    public void formatTo(Formatter formatter, int flags, int width, int precision) {
+        String base = (flags & ALTERNATE) == ALTERNATE ? getName() : toString();
+        formatter.format(applyFormattingFlagsAndWidth(base, flags & ~ALTERNATE, width));
+    }
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Wed Mar 19 21:10:34 2014 +0100
@@ -373,9 +373,8 @@
     }
 
     /**
-     * Gets the compilation level of the currently installed code for this method.
-     * 
-     * @return compilation level
+     * @param level
+     * @return true if the currently installed code was generated at {@code level}.
      */
     public boolean hasCompiledCodeAtLevel(int level) {
         long compiledCode = getCompiledCode();
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java	Wed Mar 19 21:10:34 2014 +0100
@@ -363,6 +363,10 @@
     @Override
     public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method) {
         assert method instanceof HotSpotMethod;
+        if (!isAbstract(method.getModifiers()) && method.getDeclaringClass().equals(this)) {
+            return method;
+        }
+
         final long resolvedMetaspaceMethod = runtime().getCompilerToVM().resolveMethod(metaspaceKlass(), method.getName(), ((HotSpotSignature) method.getSignature()).getMethodDescriptor());
         if (resolvedMetaspaceMethod == 0) {
             return null;
--- a/graal/com.oracle.graal.hsail/src/com/oracle/graal/hsail/HSAILRegisterConfig.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.hsail/src/com/oracle/graal/hsail/HSAILRegisterConfig.java	Wed Mar 19 21:10:34 2014 +0100
@@ -24,7 +24,6 @@
 package com.oracle.graal.hsail;
 
 import com.oracle.graal.api.code.*;
-
 import com.oracle.graal.api.code.CallingConvention.Type;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
@@ -168,6 +167,11 @@
     }
 
     @Override
+    public boolean areAllAllocatableRegistersCallerSaved() {
+        return false;
+    }
+
+    @Override
     public CalleeSaveLayout getCalleeSaveLayout() {
         return null;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AbstractNewObjectNode.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AbstractNewObjectNode.java	Wed Mar 19 21:10:34 2014 +0100
@@ -73,7 +73,7 @@
                 return;
             }
         }
-        for (Node usage : usages().snapshot()) {
+        for (Node usage : usages().distinct().snapshot()) {
             List<Node> snapshot = usage.inputs().snapshot();
             graph().removeFixed((FixedWithNextNode) usage);
             for (Node input : snapshot) {
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java	Wed Mar 19 21:10:34 2014 +0100
@@ -36,35 +36,7 @@
  */
 public abstract class BasePhase<C> {
 
-    /**
-     * Phase name lazily computed from the phase class.
-     */
-    class Name extends LazyName {
-
-        @Override
-        public String createString() {
-            String s = BasePhase.this.getClass().getSimpleName();
-            return s.substring(0, s.length() - "Phase".length());
-        }
-    }
-
-    /**
-     * Lazily computed debug value name composed of a prefix and a phase's name.
-     */
-    class DebugValueName extends LazyName {
-        final String prefix;
-
-        public DebugValueName(String prefix) {
-            this.prefix = prefix;
-        }
-
-        @Override
-        public String createString() {
-            return prefix + name;
-        }
-    }
-
-    private final CharSequence name;
+    private CharSequence name;
 
     private final DebugTimer phaseTimer;
     private final DebugMetric phaseMetric;
@@ -77,17 +49,15 @@
     }
 
     protected BasePhase() {
-        name = new Name();
-        assert checkName(name.toString());
-        phaseTimer = Debug.timer(new DebugValueName("PhaseTime_"));
-        phaseMetric = Debug.metric(new DebugValueName("PhaseCount_"));
+        phaseTimer = Debug.timer("PhaseTime_%s", getClass());
+        phaseMetric = Debug.metric("PhaseCount_%s", getClass());
     }
 
     protected BasePhase(String name) {
         assert checkName(name);
         this.name = name;
-        phaseTimer = Debug.timer(new DebugValueName("PhaseTime_"));
-        phaseMetric = Debug.metric(new DebugValueName("PhaseCount_"));
+        phaseTimer = Debug.timer("PhaseTime_%s", getClass());
+        phaseMetric = Debug.metric("PhaseCount_%s", getClass());
     }
 
     protected CharSequence getDetailedName() {
@@ -99,7 +69,7 @@
     }
 
     public final void apply(final StructuredGraph graph, final C context, final boolean dumpGraph) {
-        try (TimerCloseable a = phaseTimer.start(); Scope s = Debug.scope(name, this)) {
+        try (TimerCloseable a = phaseTimer.start(); Scope s = Debug.scope(getClass(), this)) {
             BasePhase.this.run(graph, context);
             phaseMetric.increment();
             if (dumpGraph) {
@@ -112,6 +82,13 @@
     }
 
     public final CharSequence getName() {
+        if (name == null) {
+            String s = BasePhase.this.getClass().getSimpleName();
+            if (s.endsWith("Phase")) {
+                s = s.substring(0, s.length() - "Phase".length());
+            }
+            name = s;
+        }
         return name;
     }
 
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java	Wed Mar 19 21:10:34 2014 +0100
@@ -29,8 +29,12 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.replacements.*;
 import com.oracle.graal.replacements.nodes.*;
+import com.oracle.graal.virtual.phases.ea.*;
 
 /**
  * Tests {@link ArraysSubstitutions}.
@@ -350,4 +354,90 @@
         }
     }
 
+    @Test
+    public void testConstants() {
+        test("testConstantsSnippet");
+    }
+
+    public static final int[] constantArray1 = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
+    public static final int[] constantArray2 = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
+
+    public static boolean testConstantsSnippet() {
+        constantArray2[0] = 10;
+        try {
+            return Arrays.equals(constantArray1, constantArray2);
+        } finally {
+            constantArray2[0] = 1;
+        }
+    }
+
+    @Test
+    public void testCanonicalLength() {
+        StructuredGraph graph = parse("testCanonicalLengthSnippet");
+        Assumptions assumptions = new Assumptions(false);
+        HighTierContext context = new HighTierContext(getProviders(), assumptions, null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
+        new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context);
+        new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), assumptions));
+
+        Assert.assertTrue(graph.getNodes(ReturnNode.class).first().result().asConstant().asLong() == 0);
+    }
+
+    public static final int[] constantArray3 = new int[]{1, 2, 3};
+
+    public static boolean testCanonicalLengthSnippet() {
+        return Arrays.equals(constantArray1, constantArray3);
+    }
+
+    @Test
+    public void testCanonicalEqual() {
+        StructuredGraph graph = parse("testCanonicalEqualSnippet");
+        Assumptions assumptions = new Assumptions(false);
+        HighTierContext context = new HighTierContext(getProviders(), assumptions, null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
+        new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context);
+        new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), assumptions));
+
+        Assert.assertTrue(graph.getNodes(ReturnNode.class).first().result().asConstant().asLong() == 1);
+    }
+
+    public static boolean testCanonicalEqualSnippet() {
+        return Arrays.equals(constantArray1, constantArray1);
+    }
+
+    @Test
+    public void testVirtualEqual() {
+        StructuredGraph graph = parse("testVirtualEqualSnippet");
+        Assumptions assumptions = new Assumptions(false);
+        HighTierContext context = new HighTierContext(getProviders(), assumptions, null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
+        new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context);
+        new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), assumptions));
+        new PartialEscapePhase(false, new CanonicalizerPhase(false)).apply(graph, context);
+        new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), assumptions));
+
+        Assert.assertTrue(graph.getNodes(ReturnNode.class).first().result().asConstant().asLong() == 1);
+    }
+
+    public static boolean testVirtualEqualSnippet() {
+        int[] array1 = new int[]{1, 2, 3, 4};
+        int[] array2 = new int[]{1, 2, 3, 4};
+        return Arrays.equals(array1, array2);
+    }
+
+    @Test
+    public void testVirtualNotEqual() {
+        StructuredGraph graph = parse("testVirtualNotEqualSnippet");
+        Assumptions assumptions = new Assumptions(false);
+        HighTierContext context = new HighTierContext(getProviders(), assumptions, null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
+        new InliningPhase(new CanonicalizerPhase(true)).apply(graph, context);
+        new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), assumptions));
+        new PartialEscapePhase(false, new CanonicalizerPhase(false)).apply(graph, context);
+        new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), assumptions));
+
+        Assert.assertTrue(graph.getNodes(ReturnNode.class).first().result().asConstant().asLong() == 0);
+    }
+
+    public static boolean testVirtualNotEqualSnippet(int x) {
+        int[] array1 = new int[]{1, 2, 100, x};
+        int[] array2 = new int[]{1, 2, 3, 4};
+        return Arrays.equals(array1, array2);
+    }
 }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Wed Mar 19 21:10:34 2014 +0100
@@ -99,7 +99,7 @@
                 FrameStateProcessing frameStateProcessing = method.getAnnotation(Snippet.class).removeAllFrameStates() ? FrameStateProcessing.Removal
                                 : FrameStateProcessing.CollapseFrameForSingleSideEffect;
                 StructuredGraph newGraph = makeGraph(method, recursiveEntry, recursiveEntry, inliningPolicy(method), frameStateProcessing);
-                Debug.metric(new MethodDebugValueName("SnippetNodeCount", method)).add(newGraph.getNodeCount());
+                Debug.metric("SnippetNodeCount[%#s]", method).add(newGraph.getNodeCount());
                 if (!UseSnippetGraphCache) {
                     return newGraph;
                 }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Wed Mar 19 21:10:34 2014 +0100
@@ -24,6 +24,8 @@
 
 import static com.oracle.graal.api.meta.LocationIdentity.*;
 import static com.oracle.graal.api.meta.MetaUtil.*;
+import static com.oracle.graal.debug.Debug.*;
+import static java.util.FormattableFlags.*;
 
 import java.io.*;
 import java.lang.reflect.*;
@@ -97,8 +99,8 @@
 
         protected SnippetInfo(ResolvedJavaMethod method) {
             this.method = method;
-            instantiationCounter = Debug.metric(new MethodDebugValueName("SnippetInstantiationCount", method));
-            instantiationTimer = Debug.timer(new MethodDebugValueName("SnippetInstantiationTime", method));
+            instantiationCounter = Debug.metric("SnippetInstantiationCount[%#s]", method);
+            instantiationTimer = Debug.timer("SnippetInstantiationTime[%#s]", method);
             assert Modifier.isStatic(method.getModifiers()) : "snippet method must be static: " + MetaUtil.format("%H.%n", method);
             int count = method.getSignature().getParameterCount(false);
             constantParameters = new boolean[count];
@@ -176,7 +178,7 @@
      * {@link SnippetTemplate#instantiate instantiated}
      * </ul>
      */
-    public static class Arguments {
+    public static class Arguments implements Formattable {
 
         protected final SnippetInfo info;
         protected final CacheKey cacheKey;
@@ -243,6 +245,30 @@
             result.append(">");
             return result.toString();
         }
+
+        public void formatTo(Formatter formatter, int flags, int width, int precision) {
+            if ((flags & ALTERNATE) == 0) {
+                formatter.format(applyFormattingFlagsAndWidth(toString(), flags, width));
+            } else {
+                StringBuilder sb = new StringBuilder();
+                sb.append(info.method.getName()).append('(');
+                String sep = "";
+                for (int i = 0; i < info.getParameterCount(); i++) {
+                    if (info.isConstantParameter(i)) {
+                        sb.append(sep);
+                        if (info.names[i] != null) {
+                            sb.append(info.names[i]);
+                        } else {
+                            sb.append(i);
+                        }
+                        sb.append('=').append(values[i]);
+                        sep = ", ";
+                    }
+                }
+                sb.append(")");
+                formatter.format(applyFormattingFlagsAndWidth(sb.toString(), flags & ~ALTERNATE, width));
+            }
+        }
     }
 
     /**
@@ -440,38 +466,13 @@
         return false;
     }
 
-    private static String debugValueName(String category, Arguments args) {
-        if (Debug.isEnabled()) {
-            StringBuilder result = new StringBuilder(category).append('[');
-            SnippetInfo info = args.info;
-            result.append(info.method.getName()).append('(');
-            String sep = "";
-            for (int i = 0; i < info.getParameterCount(); i++) {
-                if (info.isConstantParameter(i)) {
-                    result.append(sep);
-                    if (info.names[i] != null) {
-                        result.append(info.names[i]);
-                    } else {
-                        result.append(i);
-                    }
-                    result.append('=').append(args.values[i]);
-                    sep = ", ";
-                }
-            }
-            result.append(")]");
-            return result.toString();
-
-        }
-        return null;
-    }
-
     /**
      * Creates a snippet template.
      */
     protected SnippetTemplate(final Providers providers, Arguments args) {
         StructuredGraph snippetGraph = providers.getReplacements().getSnippet(args.info.method);
-        instantiationTimer = Debug.timer(debugValueName("SnippetTemplateInstantiationTime", args));
-        instantiationCounter = Debug.metric(debugValueName("SnippetTemplateInstantiationCount", args));
+        instantiationTimer = Debug.timer("SnippetTemplateInstantiationTime[%#s]", args);
+        instantiationCounter = Debug.metric("SnippetTemplateInstantiationCount[%#s]", args);
 
         ResolvedJavaMethod method = snippetGraph.method();
         Signature signature = method.getSignature();
@@ -665,7 +666,7 @@
             }
         }
 
-        Debug.metric(debugValueName("SnippetTemplateNodeCount", args)).add(nodes.size());
+        Debug.metric("SnippetTemplateNodeCount[%#s]", args).add(nodes.size());
         args.info.notifyNewTemplate();
         Debug.dump(snippet, "SnippetTemplate final state");
     }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java	Wed Mar 19 21:10:34 2014 +0100
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.replacements.nodes;
 
-import java.util.*;
-
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.gen.*;
 import com.oracle.graal.compiler.target.*;
@@ -31,12 +29,14 @@
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.lir.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.util.*;
 
 /**
  * Compares two arrays with the same length.
  */
-public class ArrayEqualsNode extends FixedWithNextNode implements LIRGenLowerable, Canonicalizable {
+public class ArrayEqualsNode extends FixedWithNextNode implements LIRGenLowerable, Canonicalizable, Virtualizable {
 
     /** {@link Kind} of the arrays to compare. */
     private final Kind kind;
@@ -65,42 +65,45 @@
 
     @Override
     public Node canonical(CanonicalizerTool tool) {
-        if (!array1.isConstant() || !array2.isConstant()) {
-            return this;
+        if (usages().isEmpty()) {
+            return null;
         }
+        if (GraphUtil.unproxify(array1) == GraphUtil.unproxify(array2)) {
+            return ConstantNode.forBoolean(true, graph());
+        }
+        return this;
+    }
 
-        Object a1 = array1.asConstant().asObject();
-        Object a2 = array2.asConstant().asObject();
-        boolean x;
-        switch (kind) {
-            case Boolean:
-                x = Arrays.equals((boolean[]) a1, (boolean[]) a2);
-                break;
-            case Byte:
-                x = Arrays.equals((byte[]) a1, (byte[]) a2);
-                break;
-            case Char:
-                x = Arrays.equals((char[]) a1, (char[]) a2);
-                break;
-            case Short:
-                x = Arrays.equals((short[]) a1, (short[]) a2);
-                break;
-            case Int:
-                x = Arrays.equals((int[]) a1, (int[]) a2);
-                break;
-            case Long:
-                x = Arrays.equals((long[]) a1, (long[]) a2);
-                break;
-            case Float:
-                x = Arrays.equals((float[]) a1, (float[]) a2);
-                break;
-            case Double:
-                x = Arrays.equals((double[]) a1, (double[]) a2);
-                break;
-            default:
-                throw GraalInternalError.shouldNotReachHere("unknown kind " + kind);
+    public void virtualize(VirtualizerTool tool) {
+        State state1 = tool.getObjectState(array1);
+        if (state1 != null) {
+            State state2 = tool.getObjectState(array2);
+            if (state2 != null) {
+                if (state1.getVirtualObject() == state2.getVirtualObject()) {
+                    // the same virtual objects will always have the same contents
+                    tool.replaceWithValue(ConstantNode.forBoolean(true, graph()));
+                } else if (state1.getVirtualObject().entryCount() == state2.getVirtualObject().entryCount()) {
+                    int entryCount = state1.getVirtualObject().entryCount();
+                    boolean allEqual = true;
+                    for (int i = 0; i < entryCount; i++) {
+                        ValueNode entry1 = state1.getEntry(i);
+                        ValueNode entry2 = state2.getEntry(i);
+                        if (entry1 != entry2) {
+                            // the contents might be different
+                            allEqual = false;
+                        }
+                        if (entry1.stamp().alwaysDistinct(entry2.stamp())) {
+                            // the contents are different
+                            tool.replaceWithValue(ConstantNode.forBoolean(false, graph()));
+                            return;
+                        }
+                    }
+                    if (allEqual) {
+                        tool.replaceWithValue(ConstantNode.forBoolean(true, graph()));
+                    }
+                }
+            }
         }
-        return ConstantNode.forBoolean(x, graph());
     }
 
     @NodeIntrinsic
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Wed Mar 19 21:10:34 2014 +0100
@@ -250,7 +250,7 @@
         StructuredGraph graph = truffleCache.lookup(targetMethod, arguments, assumptions, canonicalizer);
 
         if (targetMethod.getAnnotation(ExplodeLoop.class) != null) {
-            assert graph.hasLoops();
+            assert graph.hasLoops() : graph + " does not contain a loop";
             final StructuredGraph graphCopy = graph.copy();
             final List<Node> modifiedNodes = new ArrayList<>();
             for (ParameterNode param : graphCopy.getNodes(ParameterNode.class)) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Wed Mar 19 19:54:07 2014 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Wed Mar 19 21:10:34 2014 +0100
@@ -335,15 +335,6 @@
             ObjectState[] objStates = new ObjectState[states.size()];
             boolean materialized;
             do {
-                Iterator<Map.Entry<VirtualObjectNode, ObjectState>> iter = newState.objectStates.entrySet().iterator();
-                while (iter.hasNext()) {
-                    Map.Entry<VirtualObjectNode, ObjectState> entry = iter.next();
-                    if (!virtualObjTemp.contains(entry.getValue())) {
-                        iter.remove();
-                    }
-                }
-                mergeEffects.clear();
-                afterMergeEffects.clear();
                 materialized = false;
                 for (VirtualObjectNode object : virtualObjTemp) {
                     for (int i = 0; i < objStates.length; i++) {
@@ -389,6 +380,11 @@
                         materialized |= processPhi(phi, states, virtualObjTemp);
                     }
                 }
+                if (materialized) {
+                    newState.objectStates.clear();
+                    mergeEffects.clear();
+                    afterMergeEffects.clear();
+                }
             } while (materialized);
         }
 
--- a/mx/mx_graal.py	Wed Mar 19 19:54:07 2014 +0100
+++ b/mx/mx_graal.py	Wed Mar 19 21:10:34 2014 +0100
@@ -83,6 +83,12 @@
 
 JDK_UNIX_PERMISSIONS = 0755
 
+def isVMSupported(vm):
+    if 'client' in vm and len(platform.mac_ver()[0]) != 0:
+        # Client VM not supported: java launcher on Mac OS X translates '-client' to '-server'
+        return False
+    return True
+
 def _get_vm():
     """
     Gets the configured VM, presenting a dialogue if there is no currently configured VM.
@@ -569,6 +575,10 @@
                 mx.log('only product build of original VM exists')
             continue
 
+        if not isVMSupported(vm):
+            mx.log('The ' + vm + ' VM is not supported on this platform - skipping')
+            continue
+
         vmDir = join(_vmLibDirInJdk(jdk), vm)
         if not exists(vmDir):
             if mx.get_os() != 'windows':
@@ -718,8 +728,8 @@
     if vm is None:
         vm = _get_vm()
 
-    if 'client' in vm and len(platform.mac_ver()[0]) != 0:
-        mx.abort("Client VM not supported: java launcher on Mac OS X translates '-client' to '-server'")
+    if not isVMSupported(vm):
+        mx.abort('The ' + vm + ' is not supported on this platform')
 
     if cwd is None:
         cwd = _vm_cwd
@@ -927,6 +937,10 @@
 
     allStart = time.time()
     for v in vms:
+        if not isVMSupported(v):
+            mx.log('The ' + v + ' VM is not supported on this platform - skipping')
+            continue
+
         for vmbuild in builds:
             if v == 'original' and vmbuild != 'product':
                 continue
@@ -1043,6 +1057,9 @@
 
         for vmbuild in ['product', 'fastdebug']:
             for theVm in ['client', 'server']:
+                if not isVMSupported(theVm):
+                    mx.log('The' + theVm + ' VM is not supported on this platform')
+                    continue
                 with VM(theVm, vmbuild):
                     t = Task('DaCapo_pmd:' + theVm + ':' + vmbuild)
                     dacapo(['pmd'])
--- a/src/share/vm/graal/graalRuntime.cpp	Wed Mar 19 19:54:07 2014 +0100
+++ b/src/share/vm/graal/graalRuntime.cpp	Wed Mar 19 21:10:34 2014 +0100
@@ -539,7 +539,12 @@
   Handle receiverHandle(thread, receiver);
   MutexLockerEx ml(thread->threadObj() == (void*)receiver ? NULL : Threads_lock);
   JavaThread* receiverThread = java_lang_Thread::thread(receiverHandle());
-  return (jint) Thread::is_interrupted(receiverThread, clear_interrupted != 0);
+  if (receiverThread == NULL) {
+    // The other thread may exit during this process, which is ok so return false.
+    return JNI_FALSE;
+  } else {
+    return (jint) Thread::is_interrupted(receiverThread, clear_interrupted != 0);
+  }
 JRT_END
 
 // JVM_InitializeGraalRuntime