changeset 7225:31c4d9f9e922

adder better CHA support added more test cases for inlining and intrinsification
author Christian Haeubl <haeubl@ssw.jku.at>
date Fri, 14 Dec 2012 12:05:35 +0100
parents fb16d8681ddc
children 8a3efb8c831d
files graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/inlining/InliningTest.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java graal/com.oracle.graal.snippets.test/src/com/oracle/graal/snippets/IntrinsificationTest.java src/share/vm/graal/graalCompilerToVM.cpp
diffstat 6 files changed, 394 insertions(+), 41 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/inlining/InliningTest.java	Fri Dec 14 12:05:35 2012 +0100
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.test.inlining;
+
+import static org.junit.Assert.*;
+
+import java.util.concurrent.*;
+
+import org.junit.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.compiler.test.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.*;
+
+// TODO (chaeubl): add more test cases
+@SuppressWarnings("unused")
+public class InliningTest extends GraalCompilerTest {
+    @Test
+    public void testInvokeStaticInlining() {
+        assertInlined(getGraph("invokeStaticSnippet"));
+        assertInlined(getGraph("invokeStaticOnInstanceSnippet"));
+    }
+
+    @SuppressWarnings("all")
+    public static Boolean invokeStaticSnippet(boolean value) {
+        return Boolean.valueOf(value);
+    }
+    @SuppressWarnings("all")
+    public static Boolean invokeStaticOnInstanceSnippet(Boolean obj, boolean value) {
+        return obj.valueOf(value);
+    }
+
+
+    @Test
+    public void testStaticBindableInlining() {
+        assertInlined(getGraph("invokeConstructorSnippet"));
+        assertInlined(getGraph("invokeFinalMethodSnippet"));
+        assertInlined(getGraph("invokeMethodOnFinalClassSnippet"));
+    }
+
+    @SuppressWarnings("all")
+    public static Object invokeConstructorSnippet(int value) {
+        return new SuperClass(value);
+    }
+    @SuppressWarnings("all")
+    public static int invokeFinalMethodSnippet(SuperClass superClass, SubClassA subClassA, FinalSubClass finalSubClass) {
+        return superClass.publicFinalMethod() +
+               subClassA.publicFinalMethod() +
+               finalSubClass.publicFinalMethod() +
+               superClass.protectedFinalMethod() +
+               subClassA.protectedFinalMethod() +
+               finalSubClass.protectedFinalMethod();
+    }
+    @SuppressWarnings("all")
+    public static int invokeMethodOnFinalClassSnippet(FinalSubClass finalSubClass) {
+        return finalSubClass.publicFinalMethod() +
+               finalSubClass.publicNotOverriddenMethod() +
+               finalSubClass.publicOverriddenMethod() +
+               finalSubClass.protectedFinalMethod() +
+               finalSubClass.protectedNotOverriddenMethod() +
+               finalSubClass.protectedOverriddenMethod();
+    }
+
+
+    @Test
+    public void testClassHierarchyAnalysis() {
+        assertInlined(getGraph("invokeLeafClassMethodSnippet"));
+        assertInlined(getGraph("invokeConcreteMethodSnippet"));
+        assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet"));
+        assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet"));
+
+        assertNotInlined(getGraph("invokeOverriddenInterfaceMethodSnippet"));
+    }
+
+    @SuppressWarnings("all")
+    public static int invokeLeafClassMethodSnippet(SubClassA subClassA) {
+        return subClassA.publicFinalMethod() +
+               subClassA.publicNotOverriddenMethod() +
+               subClassA.publicOverriddenMethod();
+    }
+    @SuppressWarnings("all")
+    public static int invokeConcreteMethodSnippet(SuperClass superClass) {
+        return superClass.publicNotOverriddenMethod() +
+               superClass.protectedNotOverriddenMethod();
+    }
+    @SuppressWarnings("all")
+    public static int invokeSingleImplementorInterfaceSnippet(SingleImplementorInterface testInterface) {
+        return testInterface.publicNotOverriddenMethod() +
+               testInterface.publicOverriddenMethod();
+    }
+    @SuppressWarnings("all")
+    public static int invokeConcreteInterfaceMethodSnippet(MultipleImplementorsInterface testInterface) {
+        return testInterface.publicNotOverriddenMethod();
+    }
+    @SuppressWarnings("all")
+    public static int invokeOverriddenInterfaceMethodSnippet(MultipleImplementorsInterface testInterface) {
+        return testInterface.publicOverriddenMethod();
+    }
+
+    private StructuredGraph getGraph(final String snippet) {
+        return Debug.scope("InliningTest", new DebugDumpScope(snippet), new Callable<StructuredGraph>() {
+            @Override
+            public StructuredGraph call() {
+                StructuredGraph graph = parse(snippet);
+                PhasePlan phasePlan = getDefaultPhasePlan();
+                Assumptions assumptions = new Assumptions(true);
+                new ComputeProbabilityPhase().apply(graph);
+                Debug.dump(graph, "Graph");
+                new InliningPhase(null, runtime(), null, assumptions, null, phasePlan, OptimisticOptimizations.ALL).apply(graph);
+                Debug.dump(graph, "Graph");
+                new CanonicalizerPhase(null, runtime(), assumptions).apply(graph);
+                new DeadCodeEliminationPhase().apply(graph);
+                return graph;
+            }
+        });
+    }
+
+    private static StructuredGraph assertInlined(StructuredGraph graph) {
+        return assertNotInGraph(graph, Invoke.class);
+    }
+
+    private static StructuredGraph assertNotInlined(StructuredGraph graph) {
+        return assertInGraph(graph, Invoke.class);
+    }
+
+    private static StructuredGraph assertNotInGraph(StructuredGraph graph, Class<?> clazz) {
+        for (Node node: graph.getNodes()) {
+            if (clazz.isInstance(node)) {
+                fail(node.toString());
+            }
+        }
+        return graph;
+    }
+
+    private static StructuredGraph assertInGraph(StructuredGraph graph, Class<?> clazz) {
+        for (Node node: graph.getNodes()) {
+            if (clazz.isInstance(node)) {
+                return graph;
+            }
+        }
+        fail("Graph does not contain a node of class " + clazz.getName());
+        return graph;
+    }
+
+
+    // some interfaces and classes for testing
+    private interface MultipleImplementorsInterface {
+        int publicNotOverriddenMethod();
+        int publicOverriddenMethod();
+    }
+
+    private interface SingleImplementorInterface {
+        int publicNotOverriddenMethod();
+        int publicOverriddenMethod();
+    }
+
+    private static class SuperClass implements MultipleImplementorsInterface {
+        protected int value;
+
+        public SuperClass(int value) {
+            this.value = value;
+        }
+
+        public int publicNotOverriddenMethod() {
+            return value;
+        }
+
+        public int publicOverriddenMethod() {
+            return value;
+        }
+
+        protected int protectedNotOverriddenMethod() {
+            return value;
+        }
+
+        protected int protectedOverriddenMethod() {
+            return value;
+        }
+
+        public final int publicFinalMethod() {
+            return value + 255;
+        }
+
+        protected final int protectedFinalMethod() {
+            return value + 255;
+        }
+    }
+
+    private static class SubClassA extends SuperClass implements SingleImplementorInterface {
+        public SubClassA(int value) {
+            super(value);
+        }
+
+        @Override
+        public int publicOverriddenMethod() {
+            return value + 2;
+        }
+
+        @Override
+        protected int protectedOverriddenMethod() {
+            return value * 2;
+        }
+    }
+
+    private static class SubClassB extends SuperClass {
+        public SubClassB(int value) {
+            super(value);
+        }
+
+        @Override
+        public int publicOverriddenMethod() {
+            return value + 3;
+        }
+
+        @Override
+        protected int protectedOverriddenMethod() {
+            return value * 3;
+        }
+    }
+
+    private static class SubClassC extends SuperClass {
+        public SubClassC(int value) {
+            super(value);
+        }
+
+        @Override
+        public int publicOverriddenMethod() {
+            return value + 4;
+        }
+
+        @Override
+        protected int protectedOverriddenMethod() {
+            return value * 4;
+        }
+    }
+
+    private static final class FinalSubClass extends SuperClass {
+        public FinalSubClass(int value) {
+            super(value);
+        }
+
+        @Override
+        public int publicOverriddenMethod() {
+            return value + 5;
+        }
+
+        @Override
+        protected int protectedOverriddenMethod() {
+            return value * 5;
+        }
+    }
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java	Wed Dec 12 15:05:21 2012 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java	Fri Dec 14 12:05:35 2012 +0100
@@ -81,6 +81,14 @@
     long getUniqueConcreteMethod(long metaspaceMethod, HotSpotResolvedObjectType[] resultHolder);
 
     /**
+     * Used to determine if an interface has exactly one implementor.
+     *
+     * @param interfaceType interface for which the implementor should be returned
+     * @return the unique implementor of the interface or null if the interface has 0 or more than 1 implementor
+     */
+    ResolvedJavaType getUniqueImplementor(HotSpotResolvedObjectType interfaceType);
+
+    /**
      * Gets the invocation count for a method.
      *
      * @param metaspaceMethod the metaspace Method object to query
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java	Wed Dec 12 15:05:21 2012 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java	Fri Dec 14 12:05:35 2012 +0100
@@ -69,6 +69,9 @@
     public native long getUniqueConcreteMethod(long metaspaceMethod, HotSpotResolvedObjectType[] resultHolder);
 
     @Override
+    public native ResolvedJavaType getUniqueImplementor(HotSpotResolvedObjectType interfaceType);
+
+    @Override
     public native int getInvocationCount(long metaspaceMethod);
 
     @Override
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java	Wed Dec 12 15:05:21 2012 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java	Fri Dec 14 12:05:35 2012 +0100
@@ -166,6 +166,8 @@
         HotSpotVMConfig config = HotSpotGraalRuntime.getInstance().getConfig();
         if (isArray()) {
             return isFinal(getElementalType(this).getModifiers()) ? this : null;
+        } else if (isInterface()) {
+            return HotSpotGraalRuntime.getInstance().getCompilerToVM().getUniqueImplementor(this);
         } else {
             HotSpotResolvedObjectType type = this;
             while (isAbstract(type.getModifiers())) {
@@ -175,7 +177,7 @@
                 }
                 type = (HotSpotResolvedObjectType) fromMetaspaceKlass(subklass);
             }
-            if (type.isInterface() || unsafeReadWord(type.metaspaceKlass + config.subklassOffset) != 0) {
+            if (isAbstract(type.getModifiers()) || type.isInterface() || unsafeReadWord(type.metaspaceKlass + config.subklassOffset) != 0) {
                 return null;
             }
             return type;
--- a/graal/com.oracle.graal.snippets.test/src/com/oracle/graal/snippets/IntrinsificationTest.java	Wed Dec 12 15:05:21 2012 +0100
+++ b/graal/com.oracle.graal.snippets.test/src/com/oracle/graal/snippets/IntrinsificationTest.java	Fri Dec 14 12:05:35 2012 +0100
@@ -33,11 +33,18 @@
 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.calc.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
+import com.oracle.graal.snippets.nodes.*;
 
-
+/**
+ * Tests if compiler intrinsics are inlined correctly. Most test cases only assert that there are no remaining
+ * invocations in the graph. This is sufficient if the method that is being intrinsified is a native method.
+ * For Java methods, additional checks are necessary.
+ */
 public class IntrinsificationTest extends GraalCompilerTest {
     @Test
     public void testObjectIntrinsics() {
@@ -46,11 +53,11 @@
     }
 
     @SuppressWarnings("all")
-    public static boolean getClassSnippet(Object obj) {
-        return obj.getClass() == String.class;
+    public static boolean getClassSnippet(Object obj, Class<?> clazz) {
+        return obj.getClass() == clazz;
     }
     @SuppressWarnings("all")
-    public static int objectHashCodeSnippet(A obj) {
+    public static int objectHashCodeSnippet(TestClassA obj) {
         return obj.hashCode();
     }
 
@@ -126,7 +133,8 @@
 
     @SuppressWarnings("all")
     public static long systemTimeSnippet() {
-        return System.currentTimeMillis() + System.nanoTime();
+        return System.currentTimeMillis() +
+               System.nanoTime();
     }
     @SuppressWarnings("all")
     public static int systemIdentityHashCode(Object obj) {
@@ -296,13 +304,17 @@
 
     @Test
     public void testMathIntrinsics() {
+        assertInGraph(assertNotInGraph(test("mathAbsSnippet"), IfNode.class), MathIntrinsicNode.class);     // Java
         test("mathSnippet");
     }
 
     @SuppressWarnings("all")
+    public static double mathAbsSnippet(double value) {
+        return Math.abs(value);
+    }
+    @SuppressWarnings("all")
     public static double mathSnippet(double value) {
-        return Math.abs(value) +
-               Math.sqrt(value) +
+        return Math.sqrt(value) +
                Math.log(value) +
                Math.log10(value) +
                Math.sin(value) +
@@ -315,50 +327,75 @@
 
     @Test
     public void testIntegerIntrinsics() {
-        // TODO (chaeubl): some methods have Java implementations -> check more than Invoke nodes
-        test("integerSnippet");
+        assertInGraph(test("integerReverseBytesSnippet"), ReverseBytesNode.class);              // Java
+        assertInGraph(test("integerNumberOfLeadingZerosSnippet"), BitScanReverseNode.class);    // Java
+        assertInGraph(test("integerNumberOfTrailingZerosSnippet"), BitScanForwardNode.class);   // Java
     }
 
     @SuppressWarnings("all")
-    public static int integerSnippet(int value) {
-        return Integer.reverseBytes(value) +
-               Integer.numberOfLeadingZeros(value) +
-               Integer.numberOfTrailingZeros(value);
+    public static int integerReverseBytesSnippet(int value) {
+        return Integer.reverseBytes(value);
+    }
+    @SuppressWarnings("all")
+    public static int integerNumberOfLeadingZerosSnippet(int value) {
+        return Integer.numberOfLeadingZeros(value);
+    }
+    @SuppressWarnings("all")
+    public static int integerNumberOfTrailingZerosSnippet(int value) {
+        return Integer.numberOfTrailingZeros(value);
     }
 
 
     @Test
     public void testLongIntrinsics() {
-        test("longSnippet");
+        assertInGraph(test("longReverseBytesSnippet"), ReverseBytesNode.class);              // Java
+        assertInGraph(test("longNumberOfLeadingZerosSnippet"), BitScanReverseNode.class);    // Java
+        assertInGraph(test("longNumberOfTrailingZerosSnippet"), BitScanForwardNode.class);   // Java
     }
 
     @SuppressWarnings("all")
-    public static long longSnippet(long value) {
-        return Long.reverseBytes(value) +
-               Long.numberOfLeadingZeros(value) +
-               Long.numberOfTrailingZeros(value);
+    public static long longReverseBytesSnippet(long value) {
+        return Long.reverseBytes(value);
+    }
+    @SuppressWarnings("all")
+    public static long longNumberOfLeadingZerosSnippet(long value) {
+        return Long.numberOfLeadingZeros(value);
+    }
+    @SuppressWarnings("all")
+    public static long longNumberOfTrailingZerosSnippet(long value) {
+        return Long.numberOfTrailingZeros(value);
     }
 
 
     @Test
     public void testFloatIntrinsics() {
-        test("floatSnippet");
+        assertInGraph(test("floatToIntBitsSnippet"), ConvertNode.class); // Java
+        test("intBitsToFloatSnippet");
     }
 
     @SuppressWarnings("all")
-    public static float floatSnippet(float value) {
-        return Float.intBitsToFloat(Float.floatToIntBits(value));
+    public static int floatToIntBitsSnippet(float value) {
+        return Float.floatToIntBits(value);
+    }
+    @SuppressWarnings("all")
+    public static float intBitsToFloatSnippet(int value) {
+        return Float.intBitsToFloat(value);
     }
 
 
     @Test
     public void testDoubleIntrinsics() {
-        test("doubleSnippet");
+        assertInGraph(test("doubleToLongBitsSnippet"), ConvertNode.class); // Java
+        test("longBitsToDoubleSnippet");
     }
 
     @SuppressWarnings("all")
-    public static double doubleSnippet(double value) {
-        return Double.longBitsToDouble(Double.doubleToLongBits(value));
+    public static long doubleToLongBitsSnippet(double value) {
+        return Double.doubleToLongBits(value);
+    }
+    @SuppressWarnings("all")
+    public static double longBitsToDoubleSnippet(long value) {
+        return Double.longBitsToDouble(value);
     }
 
 
@@ -376,19 +413,31 @@
                 new CanonicalizerPhase(null, runtime(), assumptions).apply(graph);
                 new DeadCodeEliminationPhase().apply(graph);
 
-                assertNoInvokes(graph);
+                assertNotInGraph(graph, Invoke.class);
                 return graph;
             }
         });
     }
 
-    private static boolean assertNoInvokes(StructuredGraph graph) {
-        for (Invoke invoke: graph.getInvokes()) {
-            fail(invoke.toString());
+    private static StructuredGraph assertNotInGraph(StructuredGraph graph, Class<?> clazz) {
+        for (Node node: graph.getNodes()) {
+            if (clazz.isInstance(node)) {
+                fail(node.toString());
+            }
         }
-        return false;
+        return graph;
     }
 
-    private static class A {
+    private static StructuredGraph assertInGraph(StructuredGraph graph, Class<?> clazz) {
+        for (Node node: graph.getNodes()) {
+            if (clazz.isInstance(node)) {
+                return graph;
+            }
+        }
+        fail("Graph does not contain a node of class " + clazz.getName());
+        return graph;
+    }
+
+    private static class TestClassA {
     }
 }
--- a/src/share/vm/graal/graalCompilerToVM.cpp	Wed Dec 12 15:05:21 2012 +0100
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Fri Dec 14 12:05:35 2012 +0100
@@ -224,16 +224,17 @@
 C2V_VMENTRY(jlong, getUniqueConcreteMethod, (JNIEnv *, jobject, jlong metaspace_method, jobject resultHolder))
   methodHandle method = asMethod(metaspace_method);
   KlassHandle holder = method->method_holder();
-  if (holder->is_interface()) {
-    // Cannot trust interfaces. Because of:
-    // interface I { void foo(); }
-    // class A { public void foo() {} }
-    // class B extends A implements I { }
-    // class C extends B { public void foo() { } }
-    // class D extends B { }
-    // Would lead to identify C.foo() as the unique concrete method for I.foo() without seeing A.foo().
-    return 0L;
-  }
+  // TODO (chaeubl): check if the following is necessary
+  //if (holder->is_interface()) {
+  //  // Cannot trust interfaces. Because of:
+  //  // interface I { void foo(); }
+  //  // class A { public void foo() {} }
+  //  // class B extends A implements I { }
+  //  // class C extends B { public void foo() { } }
+  //  // class D extends B { }
+  //  // Would lead to identify C.foo() as the unique concrete method for I.foo() without seeing A.foo().
+  //  return 0L;
+  //}
   methodHandle ucm;
   {
     ResourceMark rm;
@@ -250,6 +251,19 @@
   return (jlong) (address) ucm();
 C2V_END
 
+C2V_VMENTRY(jobject, getUniqueImplementor, (JNIEnv *, jobject, jobject interface_type))
+  InstanceKlass* klass = (InstanceKlass*) asKlass(HotSpotResolvedObjectType::metaspaceKlass(interface_type));
+  assert(klass->is_interface(), "must be");
+  if (klass->nof_implementors() == 1) {
+    InstanceKlass* implementor = (InstanceKlass*) klass->implementor();
+    if (!implementor->is_abstract() && !implementor->is_interface() && implementor->is_leaf_class()) {
+      Handle type = GraalCompiler::get_JavaType(implementor, CHECK_NULL);
+      return JNIHandles::make_local(THREAD, type());
+    }
+  }
+  return NULL;
+C2V_END
+
 C2V_ENTRY(jint, getInvocationCount, (JNIEnv *, jobject, jlong metaspace_method))
   Method* method = asMethod(metaspace_method);
   return method->invocation_count();
@@ -947,6 +961,7 @@
   {CC"initializeExceptionHandlers",   CC"("METASPACE_METHOD EXCEPTION_HANDLERS")"EXCEPTION_HANDLERS,    FN_PTR(initializeExceptionHandlers)},
   {CC"hasBalancedMonitors",           CC"("METASPACE_METHOD")Z",                                        FN_PTR(hasBalancedMonitors)},
   {CC"getUniqueConcreteMethod",       CC"("METASPACE_METHOD"["HS_RESOLVED_TYPE")"METASPACE_METHOD,      FN_PTR(getUniqueConcreteMethod)},
+  {CC"getUniqueImplementor",          CC"("HS_RESOLVED_TYPE")"RESOLVED_TYPE,                            FN_PTR(getUniqueImplementor)},
   {CC"getStackTraceElement",          CC"("METASPACE_METHOD"I)"STACK_TRACE_ELEMENT,                     FN_PTR(getStackTraceElement)},
   {CC"initializeMethod",              CC"("METASPACE_METHOD HS_RESOLVED_METHOD")V",                     FN_PTR(initializeMethod)},
   {CC"initializeMethodData",          CC"("METASPACE_METHOD_DATA METHOD_DATA")V",                       FN_PTR(initializeMethodData)},