# HG changeset patch # User Christian Haeubl # Date 1355483135 -3600 # Node ID 31c4d9f9e9221c7adb95264f9911cb7cf7df5a81 # Parent fb16d8681ddc1938063552fccc892113ddf24d8c adder better CHA support added more test cases for inlining and intrinsification diff -r fb16d8681ddc -r 31c4d9f9e922 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/inlining/InliningTest.java --- /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() { + @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; + } + } +} diff -r fb16d8681ddc -r 31c4d9f9e922 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java --- 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 diff -r fb16d8681ddc -r 31c4d9f9e922 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java --- 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 diff -r fb16d8681ddc -r 31c4d9f9e922 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java --- 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; diff -r fb16d8681ddc -r 31c4d9f9e922 graal/com.oracle.graal.snippets.test/src/com/oracle/graal/snippets/IntrinsificationTest.java --- 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 { } } diff -r fb16d8681ddc -r 31c4d9f9e922 src/share/vm/graal/graalCompilerToVM.cpp --- 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)},