# HG changeset patch # User Christian Haeubl # Date 1353923822 -3600 # Node ID c2a3b92c9e79a418dd8703fbcef981c70b3a970a # Parent e5ec98288b9166d1b735724288b4537b0000df8c# Parent 17eeac928874df1e0c25420bbccf313c2e218537 Merge. diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestMetaAccessProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestMetaAccessProvider.java Mon Nov 26 10:57:02 2012 +0100 @@ -0,0 +1,170 @@ +/* + * 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.api.meta.test; + +import static com.oracle.graal.api.meta.MetaUtil.*; +import static org.junit.Assert.*; + +import java.io.*; +import java.lang.reflect.*; +import java.util.*; + +import org.junit.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.runtime.*; + + +public class TestMetaAccessProvider { + + public TestMetaAccessProvider() { + } + + public static final MetaAccessProvider runtime = Graal.getRequiredCapability(MetaAccessProvider.class); + public static final List> classes = new ArrayList<>(Arrays.asList( + void.class, + boolean.class, + byte.class, + short.class, + char.class, + int.class, + float.class, + long.class, + double.class, + Object.class, + Serializable.class, + Cloneable.class, + Test.class, + TestMetaAccessProvider.class + )); + + static { + for (Class c : new ArrayList<>(classes)) { + if (c != void.class) { + classes.add(Array.newInstance(c, 0).getClass()); + } + } + } + + @Test + public void lookupJavaTypeTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + assertNotNull(type); + assertTrue(type.isClass(c)); + assertEquals(c.getModifiers(), type.getModifiers()); + if (!type.isArrayClass()) { + assertEquals(type.getName(), toInternalName(c.getName())); + assertEquals(toJavaName(type), c.getName()); + } + } + } + + @Test + public void lookupJavaMethodTest() { + for (Class c : classes) { + for (Method reflect : c.getDeclaredMethods()) { + ResolvedJavaMethod method = runtime.lookupJavaMethod(reflect); + assertNotNull(method); + assertEquals(reflect.getModifiers(), method.getModifiers()); + assertTrue(method.getDeclaringClass().isClass(reflect.getDeclaringClass())); + } + } + } + + @Test + public void lookupJavaFieldTest() { + for (Class c : classes) { + for (Field reflect : c.getDeclaredFields()) { + ResolvedJavaField field = runtime.lookupJavaField(reflect); + assertNotNull(field); + assertEquals(reflect.getModifiers(), field.getModifiers()); + assertTrue(field.getDeclaringClass().isClass(reflect.getDeclaringClass())); + } + } + } + + public static final List constants = new ArrayList<>(); + static { + for (Field f : Constant.class.getDeclaredFields()) { + int mods = f.getModifiers(); + if (f.getType() == Constant.class && Modifier.isPublic(mods) && Modifier.isStatic(mods) && Modifier.isFinal(mods)) { + try { + Constant c = (Constant) f.get(null); + if (c != null) { + constants.add(c); + } + } catch (Exception e) { + } + } + } + for (Class c : classes) { + if (c != void.class) { + constants.add(Constant.forObject(Array.newInstance(c, 42))); + } + } + } + + @Test + public void lookupJavaTypeConstantTest() { + for (Constant c : constants) { + if (c.getKind().isObject() && !c.isNull()) { + Object o = c.asObject(); + ResolvedJavaType type = runtime.lookupJavaType(c); + assertNotNull(type); + assertTrue(type.isClass(o.getClass())); + } else { + assertEquals(runtime.lookupJavaType(c), null); + } + } + } + + @Test + public void constantEqualsTest() { + for (Constant c1 : constants) { + for (Constant c2 : constants) { + // test symmetry + assertEquals(runtime.constantEquals(c1, c2), runtime.constantEquals(c2, c1)); + if (!c1.getKind().isObject() && !c2.getKind().isObject()) { + assertEquals(c1.equals(c2), runtime.constantEquals(c2, c1)); + } + } + } + } + + @Test + public void lookupArrayLengthTest() { + for (Constant c : constants) { + if (!c.getKind().isObject() || c.isNull() || !c.asObject().getClass().isArray()) { + try { + int length = runtime.lookupArrayLength(c); + fail("Expected " + IllegalArgumentException.class.getName() + " for " + c + ", not " + length); + } catch (IllegalArgumentException e) { + // pass + } + } else { + assertEquals(Array.getLength(c.asObject()), runtime.lookupArrayLength(c)); + } + } + } +} diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java Mon Nov 26 10:57:02 2012 +0100 @@ -56,8 +56,10 @@ /** * Compares two constants for equality. - * This is used instead of {@link Constant#equals(Object)} in case where the runtime - * may have an interpretation for object equality other than {@code x.asObject() == y.asObject()}. + * This is used instead of {@link Constant#equals(Object)} in case the runtime + * has an interpretation for object equality other than {@code x.asObject() == y.asObject()}. + * For primitive constants, this is equivalent to calling {@code x.equals(y)}. + * The equality relationship is symmetric. * * @return {@code true} if the two parameters represent the same runtime object, {@code false} otherwise */ @@ -65,6 +67,8 @@ /** * Returns the length of an array that is wrapped in a {@link Constant} object. + * + * @throws IllegalArgumentException if {@code array} is not an array */ int lookupArrayLength(Constant array); } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaUtil.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaUtil.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaUtil.java Mon Nov 26 10:57:02 2012 +0100 @@ -46,9 +46,16 @@ } /** + * Determines if a given type represents a primitive type. + */ + public static boolean isPrimitive(ResolvedJavaType type) { + return type.getSuperclass() == null && !type.isInstanceClass(); + } + + /** * Extends the functionality of {@link Class#getSimpleName()} to include a non-empty string for anonymous and local * classes. - * + * * @param clazz the class for which the simple name is being requested * @param withEnclosingClass specifies if the returned name should be qualified with the name(s) of the enclosing * class/classes of {@code clazz} (if any). This option is ignored if {@code clazz} denotes an anonymous @@ -84,7 +91,7 @@ /** * Converts a given type to its Java programming language name. The following are examples of strings returned by * this method: - * + * *
      *     qualified == true:
      *         java.lang.Object
@@ -95,7 +102,7 @@
      *         int
      *         boolean[][]
      * 
- * + * * @param type the type to be converted to a Java name * @param qualified specifies if the package prefix of the type should be included in the returned name * @return the Java name corresponding to {@code type} @@ -111,13 +118,13 @@ /** * Converts a given type to its Java programming language name. The following are examples of strings returned by * this method: - * + * *
      *      java.lang.Object
      *      int
      *      boolean[][]
      * 
- * + * * @param type the type to be converted to a Java name * @return the Java name corresponding to {@code type} */ @@ -153,7 +160,7 @@ * composed of characters that are to be copied verbatim to the result and specifiers that denote an attribute of * the method that is to be copied to the result. A specifier is a single character preceded by a '%' character. The * accepted specifiers and the method attributes they denote are described below: - * + * *
      *     Specifier | Description                                          | Example(s)
      *     ----------+------------------------------------------------------------------------------------------
@@ -167,7 +174,7 @@
      *     'f'       | Indicator if method is unresolved, static or virtual | "unresolved" "static" "virtual"
      *     '%'       | A '%' character                                      | "%"
      * 
- * + * * @param format a format specification * @param method the method to be formatted * @return the result of formatting this method according to {@code format} @@ -246,7 +253,7 @@ * composed of characters that are to be copied verbatim to the result and specifiers that denote an attribute of * the field that is to be copied to the result. A specifier is a single character preceded by a '%' character. The * accepted specifiers and the field attributes they denote are described below: - * + * *
      *     Specifier | Description                                          | Example(s)
      *     ----------+------------------------------------------------------------------------------------------
@@ -258,7 +265,7 @@
      *     'f'       | Indicator if field is unresolved, static or instance | "unresolved" "static" "instance"
      *     '%'       | A '%' character                                      | "%"
      * 
- * + * * @param format a format specification * @param field the field to be formatted * @return the result of formatting this field according to {@code format} @@ -316,7 +323,7 @@ /** * Gets the annotations of a particular type for the formal parameters of a given method. - * + * * @param annotationClass the Class object corresponding to the annotation type * @param method the method for which a parameter annotations are being requested * @return the annotation of type {@code annotationClass} (if any) for each formal parameter present @@ -337,7 +344,7 @@ /** * Gets the annotation of a particular type for a formal parameter of a given method. - * + * * @param annotationClass the Class object corresponding to the annotation type * @param parameterIndex the index of a formal parameter of {@code method} * @param method the method for which a parameter annotation is being requested @@ -370,18 +377,18 @@ * {@linkplain ResolvedJavaMethod#asStackTraceElement(int) available} for the given method, then the string returned * is the {@link StackTraceElement#toString()} value of the stack trace element, suffixed by the bci location. For * example: - * + * *
      *     java.lang.String.valueOf(String.java:2930) [bci: 12]
      * 
- * + * * Otherwise, the string returned is the value of applying {@link #format(String, JavaMethod)} with the format * string {@code "%H.%n(%p)"}, suffixed by the bci location. For example: - * + * *
      *     java.lang.String.valueOf(int) [bci: 12]
      * 
- * + * * @param sb * @param method * @param bci @@ -433,7 +440,7 @@ /** * Formats some profiling information associated as a string. - * + * * @param info the profiling info to format * @param method an optional method that augments the profile string returned * @param sep the separator to use for each separate profile record @@ -500,12 +507,46 @@ /** * Converts a Java source-language class name into the internal form. - * + * * @param className the class name * @return the internal name form of the class name */ public static String toInternalName(String className) { - return "L" + className.replace('.', '/') + ";"; + String prefix = ""; + String base = className; + while (base.endsWith("[]")) { + prefix += "["; + base = base.substring(base.length() - 2); + } + + if (className.equals("boolean")) { + return prefix + "Z"; + } + if (className.equals("byte")) { + return prefix + "B"; + } + if (className.equals("short")) { + return prefix + "S"; + } + if (className.equals("char")) { + return prefix + "C"; + } + if (className.equals("int")) { + return prefix + "I"; + } + if (className.equals("float")) { + return prefix + "F"; + } + if (className.equals("long")) { + return prefix + "J"; + } + if (className.equals("double")) { + return prefix + "D"; + } + if (className.equals("void")) { + return prefix + "V"; + } + return prefix + "L" + className.replace('.', '/') + ";"; } /** diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaType.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaType.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaType.java Mon Nov 26 10:57:02 2012 +0100 @@ -155,7 +155,8 @@ ResolvedJavaType getSuperclass(); /** - * Gets the interfaces that this type defines. This method is analogous to {@link Class#getInterfaces()}. + * Gets the interfaces implemented or extended by this type. This method is analogous to {@link Class#getInterfaces()} + * and as such, only returns the interfaces directly implemented or extended by this type. */ ResolvedJavaType[] getInterfaces(); @@ -223,6 +224,11 @@ T getAnnotation(Class annotationClass); /** + * Determines if this type is the same as that represented by a given {@link Class}. + */ + boolean isClass(Class c); + + /** * Returns the {@link java.lang.Class} object representing this type. */ Class< ? > toJava(); diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java Mon Nov 26 10:57:02 2012 +0100 @@ -22,18 +22,18 @@ */ package com.oracle.graal.compiler.test.ea; -import junit.framework.Assert; +import junit.framework.*; import org.junit.Test; 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.nodes.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; +import com.oracle.graal.virtual.nodes.*; import com.oracle.graal.virtual.phases.ea.*; /** @@ -44,46 +44,42 @@ @Test public void test1() { - test("test1Snippet", Constant.forInt(101)); + testEscapeAnalysis("test1Snippet", Constant.forInt(101), false); } - @SuppressWarnings("all") - public static int test1Snippet(int a) { + public static int test1Snippet() { Integer x = new Integer(101); return x.intValue(); } @Test public void test2() { - test("test2Snippet", Constant.forInt(0)); + testEscapeAnalysis("test2Snippet", Constant.forInt(0), false); } - @SuppressWarnings("all") - public static int test2Snippet(int a) { + public static int test2Snippet() { Integer[] x = new Integer[0]; return x.length; } @Test public void test3() { - test("test3Snippet", Constant.forObject(null)); + testEscapeAnalysis("test3Snippet", Constant.forObject(null), false); } - @SuppressWarnings("all") - public static Object test3Snippet(int a) { + public static Object test3Snippet() { Integer[] x = new Integer[1]; return x[0]; } @Test public void testMonitor() { - test("testMonitorSnippet", Constant.forInt(0)); + testEscapeAnalysis("testMonitorSnippet", Constant.forInt(0), false); } private static native void notInlineable(); - @SuppressWarnings("all") - public static int testMonitorSnippet(int a) { + public static int testMonitorSnippet() { Integer x = new Integer(0); Integer[] y = new Integer[0]; Integer[] z = new Integer[1]; @@ -99,14 +95,13 @@ @Test public void testMonitor2() { - test("testMonitor2Snippet", Constant.forInt(0)); + testEscapeAnalysis("testMonitor2Snippet", Constant.forInt(0), false); } /** * This test case differs from the last one in that it requires inlining within a synchronized region. */ - @SuppressWarnings("all") - public static int testMonitor2Snippet(int a) { + public static int testMonitor2Snippet() { Integer x = new Integer(0); Integer[] y = new Integer[0]; Integer[] z = new Integer[1]; @@ -122,7 +117,7 @@ @Test public void testMerge() { - test("testMerge1Snippet", Constant.forInt(0)); + testEscapeAnalysis("testMerge1Snippet", Constant.forInt(0), true); } public static class TestObject { @@ -150,7 +145,7 @@ @Test public void testSimpleLoop() { - test("testSimpleLoopSnippet", Constant.forInt(1)); + testEscapeAnalysis("testSimpleLoopSnippet", Constant.forInt(1), false); } public int testSimpleLoopSnippet(int a) { @@ -161,38 +156,60 @@ return obj.x; } - private void test(final String snippet, final Constant expectedResult) { - Debug.scope("EscapeAnalysisTest", new DebugDumpScope(snippet), new Runnable() { - public void run() { - StructuredGraph graph = parse(snippet); - for (Invoke n : graph.getInvokes()) { - n.node().setProbability(100000); - } + public static class TestObject2 { + Object o; + public TestObject2(Object o) { + this.o = o; + } + } + + @Test + public void testCheckCast() { + testEscapeAnalysis("testCheckCastSnippet", Constant.forObject(TestObject2.class), false); + } + + public Object testCheckCastSnippet() { + TestObject2 obj = new TestObject2(TestObject2.class); + TestObject2 obj2 = new TestObject2(obj); + return ((TestObject2) obj2.o).o; + } + + @Test + public void testInstanceOf() { + ReturnNode returnNode = testEscapeAnalysis("testInstanceOfSnippet", null, false); + ValueNode result = returnNode.result(); + Assert.assertTrue(result instanceof MaterializeNode); + Assert.assertEquals(Constant.TRUE, ((MaterializeNode) result).condition().asConstant()); + } - Assumptions assumptions = new Assumptions(false); - new InliningPhase(null, runtime(), null, assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph); - new DeadCodeEliminationPhase().apply(graph); - Debug.dump(graph, "Graph"); - new PartialEscapeAnalysisPhase(null, runtime(), assumptions).apply(graph); - new CullFrameStatesPhase().apply(graph); - new CanonicalizerPhase(null, runtime(), assumptions).apply(graph); - Debug.dump(graph, "Graph"); - int retCount = 0; - for (ReturnNode ret : graph.getNodes(ReturnNode.class)) { - Assert.assertTrue(ret.result().isConstant()); - Assert.assertEquals(ret.result().asConstant(), expectedResult); - retCount++; - } - Assert.assertEquals(1, retCount); - int newInstanceCount = 0; - for (@SuppressWarnings("unused") NewInstanceNode n : graph.getNodes(NewInstanceNode.class)) { - newInstanceCount++; - } - for (@SuppressWarnings("unused") NewObjectArrayNode n : graph.getNodes(NewObjectArrayNode.class)) { - newInstanceCount++; - } - Assert.assertEquals(0, newInstanceCount); + public boolean testInstanceOfSnippet() { + TestObject2 obj = new TestObject2(TestObject2.class); + TestObject2 obj2 = new TestObject2(obj); + return obj2.o instanceof TestObject2; + } + + private ReturnNode testEscapeAnalysis(String snippet, Constant expectedConstantResult, boolean iterativeEscapeAnalysis) { + StructuredGraph graph = parse(snippet); + try { + for (Invoke n : graph.getInvokes()) { + n.node().setProbability(100000); } - }); + + Assumptions assumptions = new Assumptions(false); + new InliningPhase(null, runtime(), null, assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph); + new DeadCodeEliminationPhase().apply(graph); + new PartialEscapeAnalysisPhase(null, runtime(), null, assumptions, iterativeEscapeAnalysis).apply(graph); + Assert.assertEquals(1, graph.getNodes(ReturnNode.class).count()); + ReturnNode returnNode = graph.getNodes(ReturnNode.class).first(); + if (expectedConstantResult != null) { + Assert.assertTrue(returnNode.result().toString(), returnNode.result().isConstant()); + Assert.assertEquals(expectedConstantResult, returnNode.result().asConstant()); + } + int newInstanceCount = graph.getNodes(NewInstanceNode.class).count() + graph.getNodes(NewObjectArrayNode.class).count() + graph.getNodes(MaterializeObjectNode.class).count(); + Assert.assertEquals(0, newInstanceCount); + return returnNode; + } catch (AssertionFailedError t) { + throw new RuntimeException(t.getMessage() + "\n" + getCanonicalGraphString(graph), t); + } } } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Mon Nov 26 10:57:02 2012 +0100 @@ -22,15 +22,13 @@ */ package com.oracle.graal.compiler.test.ea; -import java.util.concurrent.*; - +import junit.framework.*; import junit.framework.Assert; import org.junit.Test; 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.*; @@ -146,27 +144,24 @@ } private StructuredGraph processMethod(final String snippet) { - return Debug.scope(getClass().getSimpleName(), new Callable() { - @Override - public StructuredGraph call() throws Exception { - StructuredGraph graph = parse(snippet); - new ComputeProbabilityPhase().apply(graph); - for (Invoke n : graph.getInvokes()) { - n.node().setProbability(100000); - } - Assumptions assumptions = new Assumptions(false); - new InliningPhase(null, runtime(), null, assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph); - new DeadCodeEliminationPhase().apply(graph); - new CanonicalizerPhase(null, runtime(), assumptions).apply(graph); -// TypeSystemTest.outputGraph(graph, "before EscapeAnalysis " + snippet); - new PartialEscapeAnalysisPhase(null, runtime(), assumptions).apply(graph); -// TypeSystemTest.outputGraph(graph, "after EscapeAnalysis " + snippet); - new CullFrameStatesPhase().apply(graph); - new DeadCodeEliminationPhase().apply(graph); - new CanonicalizerPhase(null, runtime(), assumptions).apply(graph); -// TypeSystemTest.outputGraph(graph, "after CullFrameStates " + snippet); - return graph; + StructuredGraph graph = parse(snippet); + try { + new ComputeProbabilityPhase().apply(graph); + for (Invoke n : graph.getInvokes()) { + n.node().setProbability(100000); } - }); + Assumptions assumptions = new Assumptions(false); + new InliningPhase(null, runtime(), null, assumptions, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph); + new DeadCodeEliminationPhase().apply(graph); + new CanonicalizerPhase(null, runtime(), assumptions).apply(graph); + new PartialEscapeAnalysisPhase(null, runtime(), null, assumptions, false).apply(graph); + + new CullFrameStatesPhase().apply(graph); + new DeadCodeEliminationPhase().apply(graph); + new CanonicalizerPhase(null, runtime(), assumptions).apply(graph); + return graph; + } catch (AssertionFailedError t) { + throw new RuntimeException(t.getMessage() + "\n" + getCanonicalGraphString(graph), t); + } } } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java Mon Nov 26 10:57:02 2012 +0100 @@ -149,7 +149,7 @@ } if (GraalOptions.PartialEscapeAnalysis && !plan.isPhaseDisabled(PartialEscapeAnalysisPhase.class)) { - new PartialEscapeAnalysisPhase(target, runtime, assumptions).apply(graph); + new PartialEscapeAnalysisPhase(target, runtime, assumptions, true).apply(graph); } if (GraalOptions.OptLoopTransform) { new LoopTransformHighPhase().apply(graph); diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Mon Nov 26 10:57:02 2012 +0100 @@ -119,9 +119,6 @@ printConfig(config); } - AddressMap.log(config.cardtableStartAddress, "CARDTABLE"); - AddressMap.log(config.cardtableStartAddress, "SAFEPOINT_POLL_PAGE"); - target = createTarget(); assert wordKind == null || wordKind.equals(target.wordKind); wordKind = target.wordKind; diff -r e5ec98288b91 -r c2a3b92c9e79 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 Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java Mon Nov 26 10:57:02 2012 +0100 @@ -143,13 +143,6 @@ JavaType getUniqueConcreteSubtype(HotSpotResolvedJavaType klass); - int getArrayLength(Constant array); - - /** - * Gets the type of an object constant. - */ - JavaType getJavaType(Constant constant); - HotSpotResolvedJavaField[] getInstanceFields(HotSpotResolvedJavaType klass); /** diff -r e5ec98288b91 -r c2a3b92c9e79 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 Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java Mon Nov 26 10:57:02 2012 +0100 @@ -113,20 +113,6 @@ public native ResolvedJavaType getResolvedType(Class javaClass); @Override - public int getArrayLength(Constant array) { - return Array.getLength(array.asObject()); - } - - @Override - public JavaType getJavaType(Constant constant) { - Object o = constant.asObject(); - if (o == null) { - return null; - } - return HotSpotResolvedJavaType.fromClass(o.getClass()); - } - - @Override public native HotSpotResolvedJavaField[] getInstanceFields(HotSpotResolvedJavaType klass); @Override diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Mon Nov 26 10:57:02 2012 +0100 @@ -527,7 +527,6 @@ // lost the race - return the existing value instead type = (HotSpotResolvedJavaType) unsafe.getObject(javaMirror, offset); } - AddressMap.log(metaspaceKlass, type.toJava().getName()); return type; } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/AddressMap.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/AddressMap.java Mon Nov 26 10:51:39 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/* - * 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.hotspot.meta; - -import java.io.*; - -import com.oracle.graal.debug.*; -import com.oracle.graal.graph.*; -import com.oracle.graal.phases.*; - -/** - * Utility for logging an address to symbol mapping to a file. - * This is useful when looking at disassembled code. - * - * @see GraalOptions#PrintAddressMap - */ -public class AddressMap { - - private static PrintStream addressMapStream; - static { - synchronized (AddressMap.class) { - if (GraalOptions.PrintAddressMap) { - File file = new File("addressMap-" + System.currentTimeMillis() + ".log"); - try { - addressMapStream = new PrintStream(new FileOutputStream(file), true); - } catch (FileNotFoundException e) { - throw new GraalInternalError("Could not open " + file.getAbsolutePath()); - } - TTY.println("Logging {address -> symbol} map to %s", file); - } - } - } - - public static void log(long address, String symbol) { - if (addressMapStream != null) { - synchronized (addressMapStream) { - addressMapStream.println("0x" + Long.toHexString(address) + " " + symbol); - } - } - } -} diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java Mon Nov 26 10:57:02 2012 +0100 @@ -63,7 +63,6 @@ this.metaspaceMethod = metaspaceMethod; this.holder = holder; HotSpotGraalRuntime.getInstance().getCompilerToVM().initializeMethod(metaspaceMethod, this); - AddressMap.log(metaspaceMethod, MetaUtil.format("%H.%n(%P):%R", this)); } @Override @@ -203,7 +202,6 @@ long metaspaceMethodData = unsafe.getLong(null, metaspaceMethod + HotSpotGraalRuntime.getInstance().getConfig().methodDataOffset); if (metaspaceMethodData != 0) { methodData = new HotSpotMethodData(metaspaceMethodData); - AddressMap.log(metaspaceMethodData, MetaUtil.format("MethodData{%H.%n(%P):%R}", this)); } } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaType.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaType.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaType.java Mon Nov 26 10:57:02 2012 +0100 @@ -254,7 +254,10 @@ @Override public boolean isInstance(Constant obj) { - return javaMirror.isInstance(obj); + if (obj.getKind().isObject() && !obj.isNull()) { + return javaMirror.isInstance(obj.asObject()); + } + return false; } @Override @@ -392,6 +395,11 @@ } @Override + public boolean isClass(Class c) { + return c == javaMirror; + } + + @Override public T getAnnotation(Class annotationClass) { return javaMirror.getAnnotation(annotationClass); } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java Mon Nov 26 10:57:02 2012 +0100 @@ -330,7 +330,11 @@ @Override public ResolvedJavaType lookupJavaType(Constant constant) { - return (ResolvedJavaType) graalRuntime.getCompilerToVM().getJavaType(constant); + if (!constant.getKind().isObject() || constant.isNull()) { + return null; + } + Object o = constant.asObject(); + return HotSpotResolvedJavaType.fromClass(o.getClass()); } @Override @@ -363,7 +367,10 @@ @Override public int lookupArrayLength(Constant array) { - return graalRuntime.getCompilerToVM().getArrayLength(array); + if (!array.getKind().isObject() || array.isNull() || !array.asObject().getClass().isArray()) { + throw new IllegalArgumentException(array + " is not an array"); + } + return Array.getLength(array.asObject()); } @Override @@ -388,7 +395,7 @@ AbstractCallTargetNode loweredCallTarget = null; if (callTarget.invokeKind() == InvokeKind.Virtual && GraalOptions.InlineVTableStubs && - (GraalOptions.AlwaysInlineVTableStubs || invoke.isMegamorphic())) { + (GraalOptions.AlwaysInlineVTableStubs || invoke.isPolymorphic())) { HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) callTarget.targetMethod(); if (!hsMethod.getDeclaringClass().isInterface()) { diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotTypePrimitive.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotTypePrimitive.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotTypePrimitive.java Mon Nov 26 10:57:02 2012 +0100 @@ -167,6 +167,11 @@ } @Override + public boolean isClass(Class c) { + return c == javaMirror; + } + + @Override public ResolvedJavaType resolve(ResolvedJavaType accessingClass) { return this; } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/BytecodeInterpreter.java --- a/graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/BytecodeInterpreter.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.interpreter/src/com/oracle/graal/interpreter/BytecodeInterpreter.java Mon Nov 26 10:57:02 2012 +0100 @@ -1207,7 +1207,7 @@ } private void instanceOf(InterpreterFrame frame, char cpi) { - frame.pushInt(resolveType(frame, Bytecodes.INSTANCEOF, cpi).toJava().isInstance(frame.popObject()) ? 1 : 0); + frame.pushInt(resolveType(frame, Bytecodes.INSTANCEOF, cpi).isInstance(Constant.forObject(frame.popObject())) ? 1 : 0); } private void pushCPConstant(InterpreterFrame frame, char cpi) { diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java Mon Nov 26 10:57:02 2012 +0100 @@ -62,11 +62,11 @@ void setUseForInlining(boolean value); /** - * True if this invocation is almost certainly megamorphic, false when in doubt. + * True if this invocation is almost certainly polymorphic, false when in doubt. */ - boolean isMegamorphic(); + boolean isPolymorphic(); - void setMegamorphic(boolean value); + void setPolymorphic(boolean value); long leafGraphId(); } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeNode.java Mon Nov 26 10:57:02 2012 +0100 @@ -39,7 +39,7 @@ @Input private final CallTargetNode callTarget; private final int bci; - private boolean megamorphic; + private boolean polymorphic; private boolean useForInlining; private final long leafGraphId; @@ -54,7 +54,7 @@ this.callTarget = callTarget; this.bci = bci; this.leafGraphId = leafGraphId; - this.megamorphic = false; + this.polymorphic = false; this.useForInlining = true; } @@ -69,13 +69,13 @@ } @Override - public boolean isMegamorphic() { - return megamorphic; + public boolean isPolymorphic() { + return polymorphic; } @Override - public void setMegamorphic(boolean value) { - this.megamorphic = value; + public void setPolymorphic(boolean value) { + this.polymorphic = value; } public boolean useForInlining() { diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/InvokeWithExceptionNode.java Mon Nov 26 10:57:02 2012 +0100 @@ -39,8 +39,7 @@ @Input private final CallTargetNode callTarget; @Input private FrameState stateAfter; private final int bci; - // megamorph should only be true when the compiler is sure that the call site is megamorph, and false when in doubt - private boolean megamorphic; + private boolean polymorphic; private boolean useForInlining; private final long leafGraphId; @@ -49,7 +48,7 @@ this.bci = bci; this.callTarget = callTarget; this.leafGraphId = leafGraphId; - this.megamorphic = true; + this.polymorphic = false; this.useForInlining = true; } @@ -78,13 +77,13 @@ } @Override - public boolean isMegamorphic() { - return megamorphic; + public boolean isPolymorphic() { + return polymorphic; } @Override - public void setMegamorphic(boolean value) { - this.megamorphic = value; + public void setPolymorphic(boolean value) { + this.polymorphic = value; } @Override diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BoxingMethodPool.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BoxingMethodPool.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BoxingMethodPool.java Mon Nov 26 10:57:02 2012 +0100 @@ -133,7 +133,7 @@ if (boxing == null) { return false; } - return method.getDeclaringClass().toJava() == boxing.type && method.getName().equals("valueOf"); + return method.getDeclaringClass().isClass(boxing.type) && method.getName().equals("valueOf"); } public static boolean isUnboxingMethodStatic(ResolvedJavaMethod method) { diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/InstanceOfNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/InstanceOfNode.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/InstanceOfNode.java Mon Nov 26 10:57:02 2012 +0100 @@ -28,11 +28,12 @@ import com.oracle.graal.nodes.calc.*; import com.oracle.graal.nodes.spi.*; import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.virtual.*; /** * The {@code InstanceOfNode} represents an instanceof test. */ -public final class InstanceOfNode extends BooleanNode implements Canonicalizable, Lowerable, LIRLowerable { +public final class InstanceOfNode extends BooleanNode implements Canonicalizable, Lowerable, LIRLowerable, Virtualizable { @Input private ValueNode object; private final ResolvedJavaType type; @@ -129,4 +130,12 @@ } return super.verify(); } + + @Override + public void virtualize(VirtualizerTool tool) { + VirtualObjectNode virtual = tool.getVirtualState(object()); + if (virtual != null) { + tool.replaceWithValue(ConstantNode.forBoolean(virtual.type().isSubtypeOf(type()), graph())); + } + } } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java Mon Nov 26 10:57:02 2012 +0100 @@ -105,32 +105,17 @@ } @Override - public EscapeOp getEscapeOp() { + public ObjectDesc[] getAllocations(long nextVirtualId, MetaAccessProvider metaAccess) { if (length().asConstant() != null) { final int constantLength = length().asConstant().asInt(); if (constantLength >= 0 && constantLength < MaximumEscapeAnalysisArrayLength) { - return new EscapeOp() { - - @Override - public ValueNode[] fieldState() { - ValueNode[] state = new ValueNode[constantLength]; - ConstantNode defaultForKind = constantLength == 0 ? null : ConstantNode.defaultForKind(elementType().getKind(), graph()); - for (int i = 0; i < constantLength; i++) { - state[i] = defaultForKind; - } - return state; - } - - @Override - public VirtualObjectNode virtualObject(long virtualId) { - return new VirtualArrayNode(virtualId, elementType, constantLength); - } - - @Override - public int lockCount() { - return 0; - } - }; + ValueNode[] state = new ValueNode[constantLength]; + ConstantNode defaultForKind = constantLength == 0 ? null : ConstantNode.defaultForKind(elementType().getKind(), graph()); + for (int i = 0; i < constantLength; i++) { + state[i] = defaultForKind; + } + VirtualObjectNode virtualObject = new VirtualArrayNode(nextVirtualId, elementType, constantLength); + return new ObjectDesc[]{new ObjectDesc(virtualObject, state, 0)}; } } return null; diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java Mon Nov 26 10:57:02 2012 +0100 @@ -82,31 +82,16 @@ } @Override - public EscapeOp getEscapeOp() { + public ObjectDesc[] getAllocations(long nextVirtualId, MetaAccessProvider metaAccess) { if (instanceClass != null) { assert !instanceClass().isArrayClass(); - final ResolvedJavaField[] fields = instanceClass().getInstanceFields(true); - return new EscapeOp() { - - @Override - public ValueNode[] fieldState() { - ValueNode[] state = new ValueNode[fields.length]; - for (int i = 0; i < state.length; i++) { - state[i] = ConstantNode.defaultForKind(fields[i].getType().getKind(), graph()); - } - return state; - } - - @Override - public VirtualObjectNode virtualObject(long virtualId) { - return new VirtualInstanceNode(virtualId, instanceClass(), fields); - } - - @Override - public int lockCount() { - return 0; - } - }; + ResolvedJavaField[] fields = instanceClass().getInstanceFields(true); + ValueNode[] state = new ValueNode[fields.length]; + for (int i = 0; i < state.length; i++) { + state[i] = ConstantNode.defaultForKind(fields[i].getType().getKind(), graph()); + } + VirtualObjectNode virtualObject = new VirtualInstanceNode(nextVirtualId, instanceClass(), fields); + return new ObjectDesc[]{new ObjectDesc(virtualObject, state, 0)}; } return null; } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeAnalyzable.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeAnalyzable.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeAnalyzable.java Mon Nov 26 10:57:02 2012 +0100 @@ -22,7 +22,24 @@ */ package com.oracle.graal.nodes.spi; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.virtual.*; + public interface EscapeAnalyzable { - EscapeOp getEscapeOp(); + public static class ObjectDesc { + + public final VirtualObjectNode virtualObject; + public final ValueNode[] entryState; + public final int lockCount; + + public ObjectDesc(VirtualObjectNode virtualObject, ValueNode[] entryState, int lockCount) { + this.virtualObject = virtualObject; + this.entryState = entryState; + this.lockCount = lockCount; + } + } + + ObjectDesc[] getAllocations(long nextVirtualId, MetaAccessProvider metaAccess); } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeOp.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeOp.java Mon Nov 26 10:51:39 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,39 +0,0 @@ -/* - * 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.nodes.spi; - -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.virtual.*; - -public abstract class EscapeOp { - - /** - * Returns the initial value of all fields. - */ - public abstract ValueNode[] fieldState(); - - public abstract VirtualObjectNode virtualObject(long virtualId); - - public abstract int lockCount(); - -} diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java Mon Nov 26 10:57:02 2012 +0100 @@ -1,1004 +1,1000 @@ -/* - * 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.phases.common; - -import java.lang.reflect.*; -import java.util.*; -import java.util.concurrent.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType; -import com.oracle.graal.api.meta.ResolvedJavaType.Representation; -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.nodes.extended.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; -import com.oracle.graal.nodes.spi.*; -import com.oracle.graal.nodes.type.*; -import com.oracle.graal.nodes.util.*; -import com.oracle.graal.phases.*; - -public class InliningUtil { - private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication"); - private static final String inliningDecisionsScopeString = "InliningDecisions"; - - public interface InliningCallback { - StructuredGraph buildGraph(final ResolvedJavaMethod method); - } - - public interface InliningPolicy { - void initialize(StructuredGraph graph); - boolean continueInlining(StructuredGraph graph); - InlineInfo next(); - void scanInvokes(Iterable newNodes); - double inliningWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke); - boolean isWorthInlining(InlineInfo info); - } - - public interface WeightComputationPolicy { - double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke); - } - - public static void logNotInlinedMethod(InlineInfo info, String msg, Object... args) { - logInliningDecision(info, false, msg, args); - } - - public static void logInliningDecision(InlineInfo info, boolean success, String msg, final Object... args) { - if (shouldLogInliningDecision()) { - logInliningDecision(methodName(info), success, msg, args); - } - } - - public static void logInliningDecision(final String msg, final Object... args) { - Debug.scope(inliningDecisionsScopeString, new Runnable() { - public void run() { - Debug.log(msg, args); - } - }); - } - - private static boolean logNotInlinedMethodAndReturnFalse(Invoke invoke, String msg) { - if (shouldLogInliningDecision()) { - String methodString = invoke.callTarget() == null ? "callTarget=null" : invoke.callTarget().targetName(); - logInliningDecision(methodString, false, msg, new Object[0]); - } - return false; - } - - private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, ResolvedJavaMethod method, String msg) { - if (shouldLogInliningDecision()) { - String methodString = methodName(method, invoke); - logInliningDecision(methodString, false, msg, new Object[0]); - } - return null; - } - - private static boolean logNotInlinedMethodAndReturnFalse(Invoke invoke, ResolvedJavaMethod method, String msg) { - if (shouldLogInliningDecision()) { - String methodString = methodName(method, invoke); - logInliningDecision(methodString, false, msg, new Object[0]); - } - return false; - } - - private static void logInliningDecision(final String methodString, final boolean success, final String msg, final Object... args) { - String inliningMsg = "inlining " + methodString + ": " + msg; - if (!success) { - inliningMsg = "not " + inliningMsg; - } - logInliningDecision(inliningMsg, args); - } - - private static boolean shouldLogInliningDecision() { - return Debug.scope(inliningDecisionsScopeString, new Callable() { - public Boolean call() { - return Debug.isLogEnabled(); - } - }); - } - - private static String methodName(ResolvedJavaMethod method, Invoke invoke) { - if (invoke != null && invoke.stateAfter() != null) { - return methodName(invoke.stateAfter(), invoke.bci()) + ": " + MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)"; - } else { - return MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)"; - } - } - - private static String methodName(InlineInfo info) { - if (info == null) { - return "null"; - } else if (info.invoke() != null && info.invoke().stateAfter() != null) { - return methodName(info.invoke().stateAfter(), info.invoke().bci()) + ": " + info.toString(); - } else { - return info.toString(); - } - } - - private static String methodName(FrameState frameState, int bci) { - StringBuilder sb = new StringBuilder(); - if (frameState.outerFrameState() != null) { - sb.append(methodName(frameState.outerFrameState(), frameState.outerFrameState().bci)); - sb.append("->"); - } - sb.append(MetaUtil.format("%h.%n", frameState.method())); - sb.append("@").append(bci); - return sb.toString(); - } - - /** - * Represents an opportunity for inlining at the given invoke, with the given weight and level. - * The weight is the amortized weight of the additional code - so smaller is better. - * The level is the number of nested inlinings that lead to this invoke. - */ - public interface InlineInfo extends Comparable { - Invoke invoke(); - double weight(); - int level(); - int compiledCodeSize(); - int compareTo(InlineInfo o); - - /** - * Performs the inlining described by this object and returns the node that represents the return value of the - * inlined method (or null for void methods and methods that have no non-exceptional exit). - */ - void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions); - } - - public abstract static class AbstractInlineInfo implements InlineInfo { - protected final Invoke invoke; - protected final double weight; - - public AbstractInlineInfo(Invoke invoke, double weight) { - this.invoke = invoke; - this.weight = weight; - } - - @Override - public int compareTo(InlineInfo o) { - return (weight < o.weight()) ? -1 : (weight > o.weight()) ? 1 : 0; - } - - public Invoke invoke() { - return invoke; - } - - public double weight() { - return weight; - } - - public int level() { - return computeInliningLevel(invoke); - } - - protected static StructuredGraph getGraph(final Invoke invoke, final ResolvedJavaMethod concrete, final GraalCodeCacheProvider runtime, final InliningCallback callback) { - return Debug.scope("GetInliningGraph", concrete, new Callable() { - @Override - public StructuredGraph call() throws Exception { - StructuredGraph result = getIntrinsicGraph(invoke, concrete, runtime); - if (result == null) { - assert !Modifier.isNative(concrete.getModifiers()); - result = callback.buildGraph(concrete); - } - return result; - } - }); - } - } - - /** - * Represents an inlining opportunity where the compiler can statically determine a monomorphic target method and - * therefore is able to determine the called method exactly. - */ - private static class ExactInlineInfo extends AbstractInlineInfo { - public final ResolvedJavaMethod concrete; - - public ExactInlineInfo(Invoke invoke, double weight, ResolvedJavaMethod concrete) { - super(invoke, weight); - this.concrete = concrete; - } - - @Override - public void inline(StructuredGraph compilerGraph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { - StructuredGraph graph = getGraph(invoke, concrete, runtime, callback); - assumptions.recordMethodContents(concrete); - InliningUtil.inline(invoke, graph, true); - } - - @Override - public int compiledCodeSize() { - return concrete.getCompiledCodeSize(); - } - - @Override - public String toString() { - return "exact " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - } - - /** - * Represents an inlining opportunity for which profiling information suggests a monomorphic receiver, but for which - * the receiver type cannot be proven. A type check guard will be generated if this inlining is performed. - */ - private static class TypeGuardInlineInfo extends AbstractInlineInfo { - public final ResolvedJavaMethod concrete; - public final ResolvedJavaType type; - - public TypeGuardInlineInfo(Invoke invoke, double weight, ResolvedJavaMethod concrete, ResolvedJavaType type) { - super(invoke, weight); - this.concrete = concrete; - this.type = type; - } - - @Override - public int compiledCodeSize() { - return concrete.getCompiledCodeSize(); - } - - @Override - public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { - // receiver null check must be before the type check - InliningUtil.receiverNullCheck(invoke); - ValueNode receiver = invoke.methodCallTarget().receiver(); - ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(Representation.ObjectHub), runtime, graph); - LoadHubNode receiverHub = graph.add(new LoadHubNode(receiver, typeHub.kind())); - CompareNode typeCheck = CompareNode.createCompareNode(Condition.EQ, receiverHub, typeHub); - FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile, invoke.leafGraphId())); - ValueAnchorNode anchor = graph.add(new ValueAnchorNode()); - assert invoke.predecessor() != null; - - ValueNode anchoredReceiver = createAnchoredReceiver(graph, anchor, type, receiver, true); - invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver); - - graph.addBeforeFixed(invoke.node(), receiverHub); - graph.addBeforeFixed(invoke.node(), guard); - graph.addBeforeFixed(invoke.node(), anchor); - - StructuredGraph calleeGraph = getGraph(invoke, concrete, runtime, callback); - assumptions.recordMethodContents(concrete); - InliningUtil.inline(invoke, calleeGraph, false); - } - - @Override - public String toString() { - return "type-checked " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - } - - /** - * Polymorphic inlining of m methods with n type checks (n >= m) in case that the profiling information suggests a reasonable - * amounts of different receiver types and different methods. If an unknown type is encountered a deoptimization is triggered. - */ - private static class MultiTypeGuardInlineInfo extends AbstractInlineInfo { - public final List concretes; - public final ProfiledType[] ptypes; - public final int[] typesToConcretes; - public final double notRecordedTypeProbability; - - public MultiTypeGuardInlineInfo(Invoke invoke, double weight, List concretes, ProfiledType[] ptypes, - int[] typesToConcretes, double notRecordedTypeProbability) { - super(invoke, weight); - assert concretes.size() > 0 && concretes.size() <= ptypes.length : "must have at least one method but no more than types methods"; - assert ptypes.length == typesToConcretes.length : "array lengths must match"; - - this.concretes = concretes; - this.ptypes = ptypes; - this.typesToConcretes = typesToConcretes; - this.notRecordedTypeProbability = notRecordedTypeProbability; - } - - @Override - public int compiledCodeSize() { - int result = 0; - for (ResolvedJavaMethod m: concretes) { - result += m.getCompiledCodeSize(); - } - return result; - } - - @Override - public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { - int numberOfMethods = concretes.size(); - boolean hasReturnValue = invoke.node().kind() != Kind.Void; - - // receiver null check must be the first node - InliningUtil.receiverNullCheck(invoke); - if (numberOfMethods > 1 || shouldFallbackToInvoke()) { - inlineMultipleMethods(graph, runtime, callback, assumptions, numberOfMethods, hasReturnValue); - } else { - inlineSingleMethod(graph, runtime, callback, assumptions); - } - } - - private boolean shouldFallbackToInvoke() { - return notRecordedTypeProbability > 0; - } - - private void inlineMultipleMethods(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions, int numberOfMethods, boolean hasReturnValue) { - FixedNode continuation = invoke.next(); - - ValueNode originalReceiver = invoke.methodCallTarget().receiver(); - // setup merge and phi nodes for results and exceptions - MergeNode returnMerge = graph.add(new MergeNode()); - returnMerge.setProbability(invoke.probability()); - returnMerge.setStateAfter(invoke.stateAfter().duplicate(invoke.stateAfter().bci)); - - PhiNode returnValuePhi = null; - if (hasReturnValue) { - returnValuePhi = graph.unique(new PhiNode(invoke.node().kind(), returnMerge)); - } - - MergeNode exceptionMerge = null; - PhiNode exceptionObjectPhi = null; - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; - DispatchBeginNode exceptionEdge = invokeWithException.exceptionEdge(); - ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); - - exceptionMerge = graph.add(new MergeNode()); - exceptionMerge.setProbability(exceptionEdge.probability()); - - FixedNode exceptionSux = exceptionObject.next(); - graph.addBeforeFixed(exceptionSux, exceptionMerge); - exceptionObjectPhi = graph.unique(new PhiNode(Kind.Object, exceptionMerge)); - exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, Kind.Void, exceptionObjectPhi)); - } - - // create one separate block for each invoked method - BeginNode[] successors = new BeginNode[numberOfMethods + 1]; - for (int i = 0; i < numberOfMethods; i++) { - double probability = 0; - for (int j = 0; j < typesToConcretes.length; j++) { - if (typesToConcretes[j] == i) { - probability += ptypes[j].getProbability(); - } - } - - successors[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, invoke.probability() * probability, true); - } - - // create the successor for an unknown type - FixedNode unknownTypeSux; - if (shouldFallbackToInvoke()) { - unknownTypeSux = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, notRecordedTypeProbability, false); - } else { - unknownTypeSux = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated, invoke.leafGraphId())); - } - successors[successors.length - 1] = BeginNode.begin(unknownTypeSux); - - // replace the invoke exception edge - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke; - BeginNode exceptionEdge = invokeWithExceptionNode.exceptionEdge(); - ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); - exceptionObject.replaceAtUsages(exceptionObjectPhi); - exceptionObject.setNext(null); - GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge()); - } - - // get all graphs and record assumptions - assert invoke.node().isAlive(); - StructuredGraph[] calleeGraphs = new StructuredGraph[numberOfMethods]; - for (int i = 0; i < numberOfMethods; i++) { - ResolvedJavaMethod concrete = concretes.get(i); - calleeGraphs[i] = getGraph(invoke, concrete, runtime, callback); - assumptions.recordMethodContents(concrete); - } - - // replace the invoke with a switch on the type of the actual receiver - Kind hubKind = invoke.methodCallTarget().targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind(); - LoadHubNode receiverHub = graph.add(new LoadHubNode(invoke.methodCallTarget().receiver(), hubKind)); - graph.addBeforeFixed(invoke.node(), receiverHub); - FixedNode dispatchOnType = createDispatchOnType(graph, receiverHub, successors); - - assert invoke.next() == continuation; - invoke.setNext(null); - returnMerge.setNext(continuation); - invoke.node().replaceAtUsages(returnValuePhi); - invoke.node().replaceAndDelete(dispatchOnType); - - ArrayList replacements = new ArrayList<>(); - - // do the actual inlining for every invoke - for (int i = 0; i < numberOfMethods; i++) { - BeginNode node = successors[i]; - Invoke invokeForInlining = (Invoke) node.next(); - - ResolvedJavaType commonType = getLeastCommonType(i); - ValueNode receiver = invokeForInlining.methodCallTarget().receiver(); - boolean exact = getTypeCount(i) == 1; - PiNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact); - invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver); - - StructuredGraph calleeGraph = calleeGraphs[i]; - InliningUtil.inline(invokeForInlining, calleeGraph, false); - replacements.add(anchoredReceiver); - } - if (shouldFallbackToInvoke()) { - replacements.add(null); - } - if (GraalOptions.OptTailDuplication) { - /* - * We might want to perform tail duplication at the merge after a type switch, if there are invokes that would - * benefit from the improvement in type information. - */ - FixedNode current = returnMerge; - int opportunities = 0; - do { - if (current instanceof InvokeNode && ((InvokeNode) current).methodCallTarget().receiver() == originalReceiver) { - opportunities++; - } else if (current.inputs().contains(originalReceiver)) { - opportunities++; - } - current = ((FixedWithNextNode) current).next(); - } while (current instanceof FixedWithNextNode); - if (opportunities > 0) { - metricInliningTailDuplication.increment(); - Debug.log("MultiTypeGuardInlineInfo starting tail duplication (%d opportunities)", opportunities); - TailDuplicationPhase.tailDuplicate(returnMerge, TailDuplicationPhase.TRUE_DECISION, replacements); - } - } - } - - private int getTypeCount(int concreteMethodIndex) { - int count = 0; - for (int i = 0; i < typesToConcretes.length; i++) { - if (typesToConcretes[i] == concreteMethodIndex) { - count++; - } - } - return count; - } - - private ResolvedJavaType getLeastCommonType(int concreteMethodIndex) { - ResolvedJavaType commonType = null; - for (int i = 0; i < typesToConcretes.length; i++) { - if (typesToConcretes[i] == concreteMethodIndex) { - if (commonType == null) { - commonType = ptypes[i].getType(); - } else { - commonType = commonType.findLeastCommonAncestor(ptypes[i].getType()); - } - } - } - assert commonType != null; - return commonType; - } - - private void inlineSingleMethod(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { - assert concretes.size() == 1 && ptypes.length > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0; - - BeginNode calleeEntryNode = graph.add(new BeginNode()); - calleeEntryNode.setProbability(invoke.probability()); - Kind hubKind = invoke.methodCallTarget().targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind(); - LoadHubNode receiverHub = graph.add(new LoadHubNode(invoke.methodCallTarget().receiver(), hubKind)); - graph.addBeforeFixed(invoke.node(), receiverHub); - - BeginNode unknownTypeSux = BeginNode.begin(graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated, invoke.leafGraphId()))); - BeginNode[] successors = new BeginNode[] {calleeEntryNode, unknownTypeSux}; - FixedNode dispatchOnType = createDispatchOnType(graph, receiverHub, successors); - - FixedWithNextNode pred = (FixedWithNextNode) invoke.node().predecessor(); - pred.setNext(dispatchOnType); - calleeEntryNode.setNext(invoke.node()); - - ResolvedJavaMethod concrete = concretes.get(0); - StructuredGraph calleeGraph = getGraph(invoke, concrete, runtime, callback); - assumptions.recordMethodContents(concrete); - InliningUtil.inline(invoke, calleeGraph, false); - } - - private FixedNode createDispatchOnType(StructuredGraph graph, LoadHubNode hub, BeginNode[] successors) { - assert ptypes.length > 1; - - ResolvedJavaType[] keys = new ResolvedJavaType[ptypes.length]; - double[] keyProbabilities = new double[ptypes.length + 1]; - int[] keySuccessors = new int[ptypes.length + 1]; - for (int i = 0; i < ptypes.length; i++) { - keys[i] = ptypes[i].getType(); - keyProbabilities[i] = ptypes[i].getProbability(); - keySuccessors[i] = typesToConcretes[i]; - assert keySuccessors[i] < successors.length - 1 : "last successor is the unknownTypeSux"; - } - keyProbabilities[keyProbabilities.length - 1] = notRecordedTypeProbability; - keySuccessors[keySuccessors.length - 1] = successors.length - 1; - - double[] successorProbabilities = SwitchNode.successorProbabilites(successors.length, keySuccessors, keyProbabilities); - TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(hub, successors, successorProbabilities, keys, keyProbabilities, keySuccessors)); - - return typeSwitch; - } - - private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, - MergeNode exceptionMerge, PhiNode exceptionObjectPhi, double probability, boolean useForInlining) { - Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining, probability); - BeginNode calleeEntryNode = graph.add(new BeginNode()); - calleeEntryNode.setNext(duplicatedInvoke.node()); - calleeEntryNode.setProbability(probability); - - EndNode endNode = graph.add(new EndNode()); - endNode.setProbability(probability); - - duplicatedInvoke.setNext(endNode); - returnMerge.addForwardEnd(endNode); - - if (returnValuePhi != null) { - returnValuePhi.addInput(duplicatedInvoke.node()); - } - return calleeEntryNode; - } - - private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining, double probability) { - Invoke result = (Invoke) invoke.node().copyWithInputs(); - Node callTarget = result.callTarget().copyWithInputs(); - result.node().replaceFirstInput(result.callTarget(), callTarget); - result.setUseForInlining(useForInlining); - result.setProbability(probability); - - Kind kind = invoke.node().kind(); - if (!kind.isVoid()) { - FrameState stateAfter = invoke.stateAfter(); - stateAfter = stateAfter.duplicate(stateAfter.bci); - stateAfter.replaceFirstInput(invoke.node(), result.node()); - result.setStateAfter(stateAfter); - } - - if (invoke instanceof InvokeWithExceptionNode) { - assert exceptionMerge != null && exceptionObjectPhi != null; - - InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; - BeginNode exceptionEdge = invokeWithException.exceptionEdge(); - ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); - FrameState stateAfterException = exceptionObject.stateAfter(); - - BeginNode newExceptionEdge = (BeginNode) exceptionEdge.copyWithInputs(); - ExceptionObjectNode newExceptionObject = (ExceptionObjectNode) exceptionObject.copyWithInputs(); - // set new state (pop old exception object, push new one) - newExceptionObject.setStateAfter(stateAfterException.duplicateModified(stateAfterException.bci, stateAfterException.rethrowException(), Kind.Object, newExceptionObject)); - newExceptionEdge.setNext(newExceptionObject); - - EndNode endNode = graph.add(new EndNode()); - newExceptionObject.setNext(endNode); - exceptionMerge.addForwardEnd(endNode); - exceptionObjectPhi.addInput(newExceptionObject); - - ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge); - } - return result; - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(shouldFallbackToInvoke() ? "megamorphic" : "polymorphic"); - builder.append(String.format(", %d methods with %d type checks:", concretes.size(), ptypes.length)); - for (int i = 0; i < concretes.size(); i++) { - builder.append(MetaUtil.format(" %H.%n(%p):%r", concretes.get(i))); - } - return builder.toString(); - } - } - - - /** - * Represents an inlining opportunity where the current class hierarchy leads to a monomorphic target method, - * but for which an assumption has to be registered because of non-final classes. - */ - private static class AssumptionInlineInfo extends ExactInlineInfo { - public final ResolvedJavaType context; - - public AssumptionInlineInfo(Invoke invoke, double weight, ResolvedJavaType context, ResolvedJavaMethod concrete) { - super(invoke, weight, concrete); - this.context = context; - } - - @Override - public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { - if (Debug.isLogEnabled()) { - String targetName = MetaUtil.format("%H.%n(%p):%r", invoke.methodCallTarget().targetMethod()); - String concreteName = MetaUtil.format("%H.%n(%p):%r", concrete); - Debug.log("recording concrete method assumption: %s on receiver type %s -> %s", targetName, context, concreteName); - } - assumptions.recordConcreteMethod(invoke.methodCallTarget().targetMethod(), context, concrete); - - super.inline(graph, runtime, callback, assumptions); - } - - @Override - public String toString() { - return "assumption " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - } - - /** - * Determines if inlining is possible at the given invoke node. - * @param invoke the invoke that should be inlined - * @param runtime a GraalRuntime instance used to determine of the invoke can be inlined and/or should be intrinsified - * @param inliningPolicy used to determine the weight of a specific inlining - * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke - */ - public static InlineInfo getInlineInfo(Invoke invoke, GraalCodeCacheProvider runtime, Assumptions assumptions, InliningPolicy inliningPolicy, OptimisticOptimizations optimisticOpts) { - if (!checkInvokeConditions(invoke)) { - return null; - } - ResolvedJavaMethod caller = getCaller(invoke); - MethodCallTargetNode callTarget = invoke.methodCallTarget(); - ResolvedJavaMethod targetMethod = callTarget.targetMethod(); - - if (callTarget.invokeKind() == InvokeKind.Special || targetMethod.canBeStaticallyBound()) { - if (!checkTargetConditions(invoke, targetMethod, optimisticOpts, runtime)) { - return null; - } - double weight = inliningPolicy.inliningWeight(caller, targetMethod, invoke); - return new ExactInlineInfo(invoke, weight, targetMethod); - } - ObjectStamp receiverStamp = callTarget.receiver().objectStamp(); - ResolvedJavaType receiverType = receiverStamp.type(); - if (receiverStamp.isExactType()) { - assert receiverType.isSubtypeOf(targetMethod.getDeclaringClass()) : receiverType + " subtype of " + targetMethod.getDeclaringClass() + " for " + targetMethod; - ResolvedJavaMethod resolved = receiverType.resolveMethod(targetMethod); - if (!checkTargetConditions(invoke, resolved, optimisticOpts, runtime)) { - return null; - } - double weight = inliningPolicy.inliningWeight(caller, resolved, invoke); - return new ExactInlineInfo(invoke, weight, resolved); - } - ResolvedJavaType holder = targetMethod.getDeclaringClass(); - - if (receiverStamp.type() != null) { - // the invoke target might be more specific than the holder (happens after inlining: locals lose their declared type...) - // TODO (lstadler) fix this - if (receiverType != null && receiverType.isSubtypeOf(holder)) { - holder = receiverType; - } - } - // TODO (thomaswue) fix this - if (assumptions.useOptimisticAssumptions()) { - ResolvedJavaMethod concrete = holder.findUniqueConcreteMethod(targetMethod); - if (concrete != null) { - if (!checkTargetConditions(invoke, concrete, optimisticOpts, runtime)) { - return null; - } - double weight = inliningPolicy.inliningWeight(caller, concrete, invoke); - return new AssumptionInlineInfo(invoke, weight, holder, concrete); - } - } - - // type check based inlining - return getTypeCheckedInlineInfo(invoke, inliningPolicy, caller, targetMethod, optimisticOpts, runtime); - } - - private static InlineInfo getTypeCheckedInlineInfo(Invoke invoke, InliningPolicy inliningPolicy, ResolvedJavaMethod caller, - ResolvedJavaMethod targetMethod, OptimisticOptimizations optimisticOpts, GraalCodeCacheProvider runtime) { - ProfilingInfo profilingInfo = caller.getProfilingInfo(); - JavaTypeProfile typeProfile = profilingInfo.getTypeProfile(invoke.bci()); - if (typeProfile == null) { - return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no type profile exists"); - } - - ProfiledType[] ptypes = typeProfile.getTypes(); - if (ptypes == null || ptypes.length <= 0) { - return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no types/probabilities were recorded"); - } - - double notRecordedTypeProbability = typeProfile.getNotRecordedProbability(); - if (ptypes.length == 1 && notRecordedTypeProbability == 0) { - if (!optimisticOpts.inlineMonomorphicCalls()) { - return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining monomorphic calls is disabled"); - } - - ResolvedJavaType type = ptypes[0].getType(); - ResolvedJavaMethod concrete = type.resolveMethod(targetMethod); - if (!checkTargetConditions(invoke, concrete, optimisticOpts, runtime)) { - return null; - } - double weight = inliningPolicy.inliningWeight(caller, concrete, invoke); - return new TypeGuardInlineInfo(invoke, weight, concrete, type); - } else { - invoke.setMegamorphic(true); - - if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) { - return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining polymorphic calls is disabled"); - } - if (!optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) { - return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining megamorphic calls is disabled"); - } - - // TODO (chaeubl) inlining of multiple methods should work differently - // 1. check which methods can be inlined - // 2. for those methods, use weight and probability to compute which of them should be inlined - // 3. do the inlining - // a) all seen methods can be inlined -> do so and guard with deopt - // b) some methods can be inlined -> inline them and fall back to invocation if violated - - // determine concrete methods and map type to specific method - ArrayList concreteMethods = new ArrayList<>(); - int[] typesToConcretes = new int[ptypes.length]; - for (int i = 0; i < ptypes.length; i++) { - ResolvedJavaMethod concrete = ptypes[i].getType().resolveMethod(targetMethod); - - int index = concreteMethods.indexOf(concrete); - if (index < 0) { - index = concreteMethods.size(); - concreteMethods.add(concrete); - } - typesToConcretes[i] = index; - } - - double totalWeight = 0; - for (ResolvedJavaMethod concrete: concreteMethods) { - if (!checkTargetConditions(invoke, concrete, optimisticOpts, runtime)) { - return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined"); - } - totalWeight += inliningPolicy.inliningWeight(caller, concrete, invoke); - } - return new MultiTypeGuardInlineInfo(invoke, totalWeight, concreteMethods, ptypes, typesToConcretes, notRecordedTypeProbability); - } - } - - - private static ResolvedJavaMethod getCaller(Invoke invoke) { - return invoke.stateAfter().method(); - } - - private static PiNode createAnchoredReceiver(StructuredGraph graph, FixedNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) { - // to avoid that floating reads on receiver fields float above the type check - return graph.unique(new PiNode(receiver, anchor, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredNonNull(commonType))); - } - - private static boolean checkInvokeConditions(Invoke invoke) { - if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { - return logNotInlinedMethodAndReturnFalse(invoke, "the invoke has already been lowered, or has been created as a low-level node"); - } else if (invoke.methodCallTarget().targetMethod() == null) { - return logNotInlinedMethodAndReturnFalse(invoke, "target method is null"); - } else if (invoke.stateAfter() == null) { - return logNotInlinedMethodAndReturnFalse(invoke, "the invoke has no after state"); - } else if (invoke.predecessor() == null || !invoke.node().isAlive()) { - return logNotInlinedMethodAndReturnFalse(invoke, "the invoke is dead code"); - } else if (!invoke.useForInlining()) { - return logNotInlinedMethodAndReturnFalse(invoke, "the invoke is marked to be not used for inlining"); - } else { - return true; - } - } - - private static boolean checkTargetConditions(Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts, GraalCodeCacheProvider runtime) { - if (method == null) { - return logNotInlinedMethodAndReturnFalse(invoke, method, "the method is not resolved"); - } else if (Modifier.isNative(method.getModifiers()) && (!GraalOptions.Intrinsify || !InliningUtil.canIntrinsify(invoke, method, runtime))) { - return logNotInlinedMethodAndReturnFalse(invoke, method, "it is a non-intrinsic native method"); - } else if (Modifier.isAbstract(method.getModifiers())) { - return logNotInlinedMethodAndReturnFalse(invoke, method, "it is an abstract method"); - } else if (!method.getDeclaringClass().isInitialized()) { - return logNotInlinedMethodAndReturnFalse(invoke, method, "the method's class is not initialized"); - } else if (!method.canBeInlined()) { - return logNotInlinedMethodAndReturnFalse(invoke, method, "it is marked non-inlinable"); - } else if (computeInliningLevel(invoke) > GraalOptions.MaximumInlineLevel) { - return logNotInlinedMethodAndReturnFalse(invoke, method, "it exceeds the maximum inlining depth"); - } else if (computeRecursiveInliningLevel(invoke.stateAfter(), method) > GraalOptions.MaximumRecursiveInlining) { - return logNotInlinedMethodAndReturnFalse(invoke, method, "it exceeds the maximum recursive inlining depth"); - } else if (new OptimisticOptimizations(method).lessOptimisticThan(optimisticOpts)) { - return logNotInlinedMethodAndReturnFalse(invoke, method, "the callee uses less optimistic optimizations than caller"); - } else { - return true; - } - } - - private static int computeInliningLevel(Invoke invoke) { - int count = -1; - FrameState curState = invoke.stateAfter(); - while (curState != null) { - count++; - curState = curState.outerFrameState(); - } - return count; - } - - private static int computeRecursiveInliningLevel(FrameState state, ResolvedJavaMethod method) { - assert state != null; - - int count = 0; - FrameState curState = state; - while (curState != null) { - if (curState.method() == method) { - count++; - } - curState = curState.outerFrameState(); - } - return count; - } - - /** - * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph. - * - * @param invoke the invoke that will be replaced - * @param inlineGraph the graph that the invoke will be replaced with - * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings, false if no such check is required - */ - public static void inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) { - NodeInputList parameters = invoke.callTarget().arguments(); - StructuredGraph graph = (StructuredGraph) invoke.node().graph(); - - FrameState stateAfter = invoke.stateAfter(); - assert stateAfter.isAlive(); - - IdentityHashMap replacements = new IdentityHashMap<>(); - ArrayList nodes = new ArrayList<>(); - ReturnNode returnNode = null; - UnwindNode unwindNode = null; - StartNode entryPointNode = inlineGraph.start(); - FixedNode firstCFGNode = entryPointNode.next(); - for (Node node : inlineGraph.getNodes()) { - if (node == entryPointNode || node == entryPointNode.stateAfter()) { - // Do nothing. - } else if (node instanceof LocalNode) { - replacements.put(node, parameters.get(((LocalNode) node).index())); - } else { - nodes.add(node); - if (node instanceof ReturnNode) { - assert returnNode == null; - returnNode = (ReturnNode) node; - } else if (node instanceof UnwindNode) { - assert unwindNode == null; - unwindNode = (UnwindNode) node; - } - } - } - replacements.put(entryPointNode, BeginNode.prevBegin(invoke.node())); // ensure proper anchoring of things that where anchored to the StartNode - - assert invoke.node().successors().first() != null : invoke; - assert invoke.node().predecessor() != null; - - Map duplicates = graph.addDuplicates(nodes, replacements); - FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode); - if (receiverNullCheck) { - receiverNullCheck(invoke); - } - invoke.node().replaceAtPredecessor(firstCFGNodeDuplicate); - - FrameState stateAtExceptionEdge = null; - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); - if (unwindNode != null) { - assert unwindNode.predecessor() != null; - assert invokeWithException.exceptionEdge().successors().count() == 1; - ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge().next(); - stateAtExceptionEdge = obj.stateAfter(); - UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); - obj.replaceAtUsages(unwindDuplicate.exception()); - unwindDuplicate.clearInputs(); - Node n = obj.next(); - obj.setNext(null); - unwindDuplicate.replaceAndDelete(n); - } else { - invokeWithException.killExceptionEdge(); - } - } else { - if (unwindNode != null) { - UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); - DeoptimizeNode deoptimizeNode = new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler, invoke.leafGraphId()); - unwindDuplicate.replaceAndDelete(graph.add(deoptimizeNode)); - // move the deopt upwards if there is a monitor exit that tries to use the "after exception" frame state - // (because there is no "after exception" frame state!) - if (deoptimizeNode.predecessor() instanceof MonitorExitNode) { - MonitorExitNode monitorExit = (MonitorExitNode) deoptimizeNode.predecessor(); - if (monitorExit.stateAfter() != null && monitorExit.stateAfter().bci == FrameState.AFTER_EXCEPTION_BCI) { - FrameState monitorFrameState = monitorExit.stateAfter(); - graph.removeFixed(monitorExit); - monitorFrameState.safeDelete(); - } - } - } - } - - FrameState outerFrameState = null; - double invokeProbability = invoke.node().probability(); - for (Node node : duplicates.values()) { - if (GraalOptions.ProbabilityAnalysis) { - if (node instanceof FixedNode) { - FixedNode fixed = (FixedNode) node; - double newProbability = fixed.probability() * invokeProbability; - if (GraalOptions.LimitInlinedProbability) { - newProbability = Math.min(newProbability, invokeProbability); - } - fixed.setProbability(newProbability); - } - } - if (node instanceof FrameState) { - FrameState frameState = (FrameState) node; - assert frameState.bci != FrameState.BEFORE_BCI; - if (frameState.bci == FrameState.AFTER_BCI) { - frameState.replaceAndDelete(stateAfter); - } else if (frameState.bci == FrameState.AFTER_EXCEPTION_BCI) { - if (frameState.isAlive()) { - assert stateAtExceptionEdge != null; - frameState.replaceAndDelete(stateAtExceptionEdge); - } else { - assert stateAtExceptionEdge == null; - } - } else { - // only handle the outermost frame states - if (frameState.outerFrameState() == null) { - assert frameState.method() == inlineGraph.method(); - if (outerFrameState == null) { - outerFrameState = stateAfter.duplicateModified(invoke.bci(), stateAfter.rethrowException(), invoke.node().kind()); - outerFrameState.setDuringCall(true); - } - frameState.setOuterFrameState(outerFrameState); - } - } - } - } - - Node returnValue = null; - if (returnNode != null) { - if (returnNode.result() instanceof LocalNode) { - returnValue = replacements.get(returnNode.result()); - } else { - returnValue = duplicates.get(returnNode.result()); - } - invoke.node().replaceAtUsages(returnValue); - Node returnDuplicate = duplicates.get(returnNode); - returnDuplicate.clearInputs(); - Node n = invoke.next(); - invoke.setNext(null); - returnDuplicate.replaceAndDelete(n); - } - - invoke.node().clearInputs(); - invoke.node().replaceAtUsages(null); - GraphUtil.killCFG(invoke.node()); - - if (stateAfter.usages().isEmpty()) { - stateAfter.safeDelete(); - } - } - - public static void receiverNullCheck(Invoke invoke) { - MethodCallTargetNode callTarget = invoke.methodCallTarget(); - StructuredGraph graph = (StructuredGraph) invoke.graph(); - NodeInputList parameters = callTarget.arguments(); - ValueNode firstParam = parameters.size() <= 0 ? null : parameters.get(0); - if (!callTarget.isStatic() && firstParam.kind() == Kind.Object && !firstParam.objectStamp().nonNull()) { - graph.addBeforeFixed(invoke.node(), graph.add(new FixedGuardNode(graph.unique(new IsNullNode(firstParam)), DeoptimizationReason.NullCheckException, DeoptimizationAction.InvalidateReprofile, true, invoke.leafGraphId()))); - } - } - - public static boolean canIntrinsify(Invoke invoke, ResolvedJavaMethod target, GraalCodeCacheProvider runtime) { - return getIntrinsicGraph(invoke, target, runtime) != null; - } - - private static StructuredGraph getIntrinsicGraph(Invoke invoke, ResolvedJavaMethod target, GraalCodeCacheProvider runtime) { - assert invoke.node().isAlive(); - - StructuredGraph intrinsicGraph = (StructuredGraph) target.getCompilerStorage().get(Graph.class); - if (intrinsicGraph == null) { - // TODO remove once all intrinsics are available via compilerStorage - intrinsicGraph = runtime.intrinsicGraph(invoke.stateAfter().method(), invoke.bci(), target, invoke.callTarget().arguments()); - } - return intrinsicGraph; - } -} +/* + * 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.phases.common; + +import java.lang.reflect.*; +import java.util.*; +import java.util.concurrent.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType; +import com.oracle.graal.api.meta.ResolvedJavaType.Representation; +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.nodes.extended.*; +import com.oracle.graal.nodes.java.*; +import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; +import com.oracle.graal.nodes.util.*; +import com.oracle.graal.phases.*; + +public class InliningUtil { + private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication"); + private static final String inliningDecisionsScopeString = "InliningDecisions"; + + public interface InliningCallback { + StructuredGraph buildGraph(final ResolvedJavaMethod method); + } + + public interface InliningPolicy { + void initialize(StructuredGraph graph); + boolean continueInlining(StructuredGraph graph); + InlineInfo next(); + void scanInvokes(Iterable newNodes); + double inliningWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke); + boolean isWorthInlining(InlineInfo info); + } + + public interface WeightComputationPolicy { + double computeWeight(ResolvedJavaMethod caller, ResolvedJavaMethod method, Invoke invoke, boolean preferredInvoke); + } + + public static void logNotInlinedMethod(InlineInfo info, String msg, Object... args) { + logInliningDecision(info, false, msg, args); + } + + public static void logInliningDecision(InlineInfo info, boolean success, String msg, final Object... args) { + if (shouldLogInliningDecision()) { + logInliningDecision(methodName(info), success, msg, args); + } + } + + public static void logInliningDecision(final String msg, final Object... args) { + Debug.scope(inliningDecisionsScopeString, new Runnable() { + public void run() { + Debug.log(msg, args); + } + }); + } + + private static boolean logNotInlinedMethodAndReturnFalse(Invoke invoke, String msg) { + if (shouldLogInliningDecision()) { + String methodString = invoke.callTarget() == null ? "callTarget=null" : invoke.callTarget().targetName(); + logInliningDecision(methodString, false, msg, new Object[0]); + } + return false; + } + + private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, ResolvedJavaMethod method, String msg) { + if (shouldLogInliningDecision()) { + String methodString = methodName(method, invoke); + logInliningDecision(methodString, false, msg, new Object[0]); + } + return null; + } + + private static boolean logNotInlinedMethodAndReturnFalse(Invoke invoke, ResolvedJavaMethod method, String msg) { + if (shouldLogInliningDecision()) { + String methodString = methodName(method, invoke); + logInliningDecision(methodString, false, msg, new Object[0]); + } + return false; + } + + private static void logInliningDecision(final String methodString, final boolean success, final String msg, final Object... args) { + String inliningMsg = "inlining " + methodString + ": " + msg; + if (!success) { + inliningMsg = "not " + inliningMsg; + } + logInliningDecision(inliningMsg, args); + } + + private static boolean shouldLogInliningDecision() { + return Debug.scope(inliningDecisionsScopeString, new Callable() { + public Boolean call() { + return Debug.isLogEnabled(); + } + }); + } + + private static String methodName(ResolvedJavaMethod method, Invoke invoke) { + if (invoke != null && invoke.stateAfter() != null) { + return methodName(invoke.stateAfter(), invoke.bci()) + ": " + MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)"; + } else { + return MetaUtil.format("%H.%n(%p):%r", method) + " (" + method.getCodeSize() + " bytes)"; + } + } + + private static String methodName(InlineInfo info) { + if (info == null) { + return "null"; + } else if (info.invoke() != null && info.invoke().stateAfter() != null) { + return methodName(info.invoke().stateAfter(), info.invoke().bci()) + ": " + info.toString(); + } else { + return info.toString(); + } + } + + private static String methodName(FrameState frameState, int bci) { + StringBuilder sb = new StringBuilder(); + if (frameState.outerFrameState() != null) { + sb.append(methodName(frameState.outerFrameState(), frameState.outerFrameState().bci)); + sb.append("->"); + } + sb.append(MetaUtil.format("%h.%n", frameState.method())); + sb.append("@").append(bci); + return sb.toString(); + } + + /** + * Represents an opportunity for inlining at the given invoke, with the given weight and level. + * The weight is the amortized weight of the additional code - so smaller is better. + * The level is the number of nested inlinings that lead to this invoke. + */ + public interface InlineInfo extends Comparable { + Invoke invoke(); + double weight(); + int level(); + int compiledCodeSize(); + int compareTo(InlineInfo o); + + /** + * Performs the inlining described by this object and returns the node that represents the return value of the + * inlined method (or null for void methods and methods that have no non-exceptional exit). + */ + void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions); + } + + public abstract static class AbstractInlineInfo implements InlineInfo { + protected final Invoke invoke; + protected final double weight; + + public AbstractInlineInfo(Invoke invoke, double weight) { + this.invoke = invoke; + this.weight = weight; + } + + @Override + public int compareTo(InlineInfo o) { + return (weight < o.weight()) ? -1 : (weight > o.weight()) ? 1 : 0; + } + + public Invoke invoke() { + return invoke; + } + + public double weight() { + return weight; + } + + public int level() { + return computeInliningLevel(invoke); + } + + protected static StructuredGraph getGraph(final Invoke invoke, final ResolvedJavaMethod concrete, final GraalCodeCacheProvider runtime, final InliningCallback callback) { + return Debug.scope("GetInliningGraph", concrete, new Callable() { + @Override + public StructuredGraph call() throws Exception { + StructuredGraph result = getIntrinsicGraph(invoke, concrete, runtime); + if (result == null) { + assert !Modifier.isNative(concrete.getModifiers()); + result = callback.buildGraph(concrete); + } + return result; + } + }); + } + } + + /** + * Represents an inlining opportunity where the compiler can statically determine a monomorphic target method and + * therefore is able to determine the called method exactly. + */ + private static class ExactInlineInfo extends AbstractInlineInfo { + public final ResolvedJavaMethod concrete; + + public ExactInlineInfo(Invoke invoke, double weight, ResolvedJavaMethod concrete) { + super(invoke, weight); + this.concrete = concrete; + } + + @Override + public void inline(StructuredGraph compilerGraph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { + StructuredGraph graph = getGraph(invoke, concrete, runtime, callback); + assumptions.recordMethodContents(concrete); + InliningUtil.inline(invoke, graph, true); + } + + @Override + public int compiledCodeSize() { + return concrete.getCompiledCodeSize(); + } + + @Override + public String toString() { + return "exact " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + } + + /** + * Represents an inlining opportunity for which profiling information suggests a monomorphic receiver, but for which + * the receiver type cannot be proven. A type check guard will be generated if this inlining is performed. + */ + private static class TypeGuardInlineInfo extends AbstractInlineInfo { + public final ResolvedJavaMethod concrete; + public final ResolvedJavaType type; + + public TypeGuardInlineInfo(Invoke invoke, double weight, ResolvedJavaMethod concrete, ResolvedJavaType type) { + super(invoke, weight); + this.concrete = concrete; + this.type = type; + } + + @Override + public int compiledCodeSize() { + return concrete.getCompiledCodeSize(); + } + + @Override + public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { + // receiver null check must be before the type check + InliningUtil.receiverNullCheck(invoke); + ValueNode receiver = invoke.methodCallTarget().receiver(); + ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(Representation.ObjectHub), runtime, graph); + LoadHubNode receiverHub = graph.add(new LoadHubNode(receiver, typeHub.kind())); + CompareNode typeCheck = CompareNode.createCompareNode(Condition.EQ, receiverHub, typeHub); + FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile, invoke.leafGraphId())); + ValueAnchorNode anchor = graph.add(new ValueAnchorNode()); + assert invoke.predecessor() != null; + + ValueNode anchoredReceiver = createAnchoredReceiver(graph, anchor, type, receiver, true); + invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver); + + graph.addBeforeFixed(invoke.node(), receiverHub); + graph.addBeforeFixed(invoke.node(), guard); + graph.addBeforeFixed(invoke.node(), anchor); + + StructuredGraph calleeGraph = getGraph(invoke, concrete, runtime, callback); + assumptions.recordMethodContents(concrete); + InliningUtil.inline(invoke, calleeGraph, false); + } + + @Override + public String toString() { + return "type-checked " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + } + + /** + * Polymorphic inlining of m methods with n type checks (n >= m) in case that the profiling information suggests a reasonable + * amounts of different receiver types and different methods. If an unknown type is encountered a deoptimization is triggered. + */ + private static class MultiTypeGuardInlineInfo extends AbstractInlineInfo { + public final List concretes; + public final ProfiledType[] ptypes; + public final int[] typesToConcretes; + public final double notRecordedTypeProbability; + + public MultiTypeGuardInlineInfo(Invoke invoke, double weight, List concretes, ProfiledType[] ptypes, + int[] typesToConcretes, double notRecordedTypeProbability) { + super(invoke, weight); + assert concretes.size() > 0 && concretes.size() <= ptypes.length : "must have at least one method but no more than types methods"; + assert ptypes.length == typesToConcretes.length : "array lengths must match"; + + this.concretes = concretes; + this.ptypes = ptypes; + this.typesToConcretes = typesToConcretes; + this.notRecordedTypeProbability = notRecordedTypeProbability; + } + + @Override + public int compiledCodeSize() { + int result = 0; + for (ResolvedJavaMethod m: concretes) { + result += m.getCompiledCodeSize(); + } + return result; + } + + @Override + public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { + int numberOfMethods = concretes.size(); + boolean hasReturnValue = invoke.node().kind() != Kind.Void; + + // receiver null check must be the first node + InliningUtil.receiverNullCheck(invoke); + if (numberOfMethods > 1 || shouldFallbackToInvoke()) { + inlineMultipleMethods(graph, runtime, callback, assumptions, numberOfMethods, hasReturnValue); + } else { + inlineSingleMethod(graph, runtime, callback, assumptions); + } + } + + private boolean shouldFallbackToInvoke() { + return notRecordedTypeProbability > 0; + } + + private void inlineMultipleMethods(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions, int numberOfMethods, boolean hasReturnValue) { + FixedNode continuation = invoke.next(); + + ValueNode originalReceiver = invoke.methodCallTarget().receiver(); + // setup merge and phi nodes for results and exceptions + MergeNode returnMerge = graph.add(new MergeNode()); + returnMerge.setProbability(invoke.probability()); + returnMerge.setStateAfter(invoke.stateAfter().duplicate(invoke.stateAfter().bci)); + + PhiNode returnValuePhi = null; + if (hasReturnValue) { + returnValuePhi = graph.unique(new PhiNode(invoke.node().kind(), returnMerge)); + } + + MergeNode exceptionMerge = null; + PhiNode exceptionObjectPhi = null; + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + DispatchBeginNode exceptionEdge = invokeWithException.exceptionEdge(); + ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); + + exceptionMerge = graph.add(new MergeNode()); + exceptionMerge.setProbability(exceptionEdge.probability()); + + FixedNode exceptionSux = exceptionObject.next(); + graph.addBeforeFixed(exceptionSux, exceptionMerge); + exceptionObjectPhi = graph.unique(new PhiNode(Kind.Object, exceptionMerge)); + exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, Kind.Void, exceptionObjectPhi)); + } + + // create one separate block for each invoked method + BeginNode[] successors = new BeginNode[numberOfMethods + 1]; + for (int i = 0; i < numberOfMethods; i++) { + double probability = 0; + for (int j = 0; j < typesToConcretes.length; j++) { + if (typesToConcretes[j] == i) { + probability += ptypes[j].getProbability(); + } + } + + successors[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, invoke.probability() * probability, true); + } + + // create the successor for an unknown type + FixedNode unknownTypeSux; + if (shouldFallbackToInvoke()) { + unknownTypeSux = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, notRecordedTypeProbability, false); + } else { + unknownTypeSux = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated, invoke.leafGraphId())); + } + successors[successors.length - 1] = BeginNode.begin(unknownTypeSux); + + // replace the invoke exception edge + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke; + BeginNode exceptionEdge = invokeWithExceptionNode.exceptionEdge(); + ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); + exceptionObject.replaceAtUsages(exceptionObjectPhi); + exceptionObject.setNext(null); + GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge()); + } + + // get all graphs and record assumptions + assert invoke.node().isAlive(); + StructuredGraph[] calleeGraphs = new StructuredGraph[numberOfMethods]; + for (int i = 0; i < numberOfMethods; i++) { + ResolvedJavaMethod concrete = concretes.get(i); + calleeGraphs[i] = getGraph(invoke, concrete, runtime, callback); + assumptions.recordMethodContents(concrete); + } + + // replace the invoke with a switch on the type of the actual receiver + Kind hubKind = invoke.methodCallTarget().targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind(); + LoadHubNode receiverHub = graph.add(new LoadHubNode(invoke.methodCallTarget().receiver(), hubKind)); + graph.addBeforeFixed(invoke.node(), receiverHub); + FixedNode dispatchOnType = createDispatchOnType(graph, receiverHub, successors); + + assert invoke.next() == continuation; + invoke.setNext(null); + returnMerge.setNext(continuation); + invoke.node().replaceAtUsages(returnValuePhi); + invoke.node().replaceAndDelete(dispatchOnType); + + ArrayList replacements = new ArrayList<>(); + + // do the actual inlining for every invoke + for (int i = 0; i < numberOfMethods; i++) { + BeginNode node = successors[i]; + Invoke invokeForInlining = (Invoke) node.next(); + + ResolvedJavaType commonType = getLeastCommonType(i); + ValueNode receiver = invokeForInlining.methodCallTarget().receiver(); + boolean exact = getTypeCount(i) == 1; + PiNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact); + invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver); + + StructuredGraph calleeGraph = calleeGraphs[i]; + InliningUtil.inline(invokeForInlining, calleeGraph, false); + replacements.add(anchoredReceiver); + } + if (shouldFallbackToInvoke()) { + replacements.add(null); + } + if (GraalOptions.OptTailDuplication) { + /* + * We might want to perform tail duplication at the merge after a type switch, if there are invokes that would + * benefit from the improvement in type information. + */ + FixedNode current = returnMerge; + int opportunities = 0; + do { + if (current instanceof InvokeNode && ((InvokeNode) current).methodCallTarget().receiver() == originalReceiver) { + opportunities++; + } else if (current.inputs().contains(originalReceiver)) { + opportunities++; + } + current = ((FixedWithNextNode) current).next(); + } while (current instanceof FixedWithNextNode); + if (opportunities > 0) { + metricInliningTailDuplication.increment(); + Debug.log("MultiTypeGuardInlineInfo starting tail duplication (%d opportunities)", opportunities); + TailDuplicationPhase.tailDuplicate(returnMerge, TailDuplicationPhase.TRUE_DECISION, replacements); + } + } + } + + private int getTypeCount(int concreteMethodIndex) { + int count = 0; + for (int i = 0; i < typesToConcretes.length; i++) { + if (typesToConcretes[i] == concreteMethodIndex) { + count++; + } + } + return count; + } + + private ResolvedJavaType getLeastCommonType(int concreteMethodIndex) { + ResolvedJavaType commonType = null; + for (int i = 0; i < typesToConcretes.length; i++) { + if (typesToConcretes[i] == concreteMethodIndex) { + if (commonType == null) { + commonType = ptypes[i].getType(); + } else { + commonType = commonType.findLeastCommonAncestor(ptypes[i].getType()); + } + } + } + assert commonType != null; + return commonType; + } + + private void inlineSingleMethod(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { + assert concretes.size() == 1 && ptypes.length > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0; + + BeginNode calleeEntryNode = graph.add(new BeginNode()); + calleeEntryNode.setProbability(invoke.probability()); + Kind hubKind = invoke.methodCallTarget().targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind(); + LoadHubNode receiverHub = graph.add(new LoadHubNode(invoke.methodCallTarget().receiver(), hubKind)); + graph.addBeforeFixed(invoke.node(), receiverHub); + + BeginNode unknownTypeSux = BeginNode.begin(graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated, invoke.leafGraphId()))); + BeginNode[] successors = new BeginNode[] {calleeEntryNode, unknownTypeSux}; + FixedNode dispatchOnType = createDispatchOnType(graph, receiverHub, successors); + + FixedWithNextNode pred = (FixedWithNextNode) invoke.node().predecessor(); + pred.setNext(dispatchOnType); + calleeEntryNode.setNext(invoke.node()); + + ResolvedJavaMethod concrete = concretes.get(0); + StructuredGraph calleeGraph = getGraph(invoke, concrete, runtime, callback); + assumptions.recordMethodContents(concrete); + InliningUtil.inline(invoke, calleeGraph, false); + } + + private FixedNode createDispatchOnType(StructuredGraph graph, LoadHubNode hub, BeginNode[] successors) { + assert ptypes.length > 1; + + ResolvedJavaType[] keys = new ResolvedJavaType[ptypes.length]; + double[] keyProbabilities = new double[ptypes.length + 1]; + int[] keySuccessors = new int[ptypes.length + 1]; + for (int i = 0; i < ptypes.length; i++) { + keys[i] = ptypes[i].getType(); + keyProbabilities[i] = ptypes[i].getProbability(); + keySuccessors[i] = typesToConcretes[i]; + assert keySuccessors[i] < successors.length - 1 : "last successor is the unknownTypeSux"; + } + keyProbabilities[keyProbabilities.length - 1] = notRecordedTypeProbability; + keySuccessors[keySuccessors.length - 1] = successors.length - 1; + + double[] successorProbabilities = SwitchNode.successorProbabilites(successors.length, keySuccessors, keyProbabilities); + TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(hub, successors, successorProbabilities, keys, keyProbabilities, keySuccessors)); + + return typeSwitch; + } + + private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, + MergeNode exceptionMerge, PhiNode exceptionObjectPhi, double probability, boolean useForInlining) { + Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining, probability); + BeginNode calleeEntryNode = graph.add(new BeginNode()); + calleeEntryNode.setNext(duplicatedInvoke.node()); + calleeEntryNode.setProbability(probability); + + EndNode endNode = graph.add(new EndNode()); + endNode.setProbability(probability); + + duplicatedInvoke.setNext(endNode); + returnMerge.addForwardEnd(endNode); + + if (returnValuePhi != null) { + returnValuePhi.addInput(duplicatedInvoke.node()); + } + return calleeEntryNode; + } + + private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining, double probability) { + Invoke result = (Invoke) invoke.node().copyWithInputs(); + Node callTarget = result.callTarget().copyWithInputs(); + result.node().replaceFirstInput(result.callTarget(), callTarget); + result.setUseForInlining(useForInlining); + result.setProbability(probability); + + Kind kind = invoke.node().kind(); + if (!kind.isVoid()) { + FrameState stateAfter = invoke.stateAfter(); + stateAfter = stateAfter.duplicate(stateAfter.bci); + stateAfter.replaceFirstInput(invoke.node(), result.node()); + result.setStateAfter(stateAfter); + } + + if (invoke instanceof InvokeWithExceptionNode) { + assert exceptionMerge != null && exceptionObjectPhi != null; + + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + BeginNode exceptionEdge = invokeWithException.exceptionEdge(); + ExceptionObjectNode exceptionObject = (ExceptionObjectNode) exceptionEdge.next(); + FrameState stateAfterException = exceptionObject.stateAfter(); + + BeginNode newExceptionEdge = (BeginNode) exceptionEdge.copyWithInputs(); + ExceptionObjectNode newExceptionObject = (ExceptionObjectNode) exceptionObject.copyWithInputs(); + // set new state (pop old exception object, push new one) + newExceptionObject.setStateAfter(stateAfterException.duplicateModified(stateAfterException.bci, stateAfterException.rethrowException(), Kind.Object, newExceptionObject)); + newExceptionEdge.setNext(newExceptionObject); + + EndNode endNode = graph.add(new EndNode()); + newExceptionObject.setNext(endNode); + exceptionMerge.addForwardEnd(endNode); + exceptionObjectPhi.addInput(newExceptionObject); + + ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge); + } + return result; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(shouldFallbackToInvoke() ? "megamorphic" : "polymorphic"); + builder.append(String.format(", %d methods with %d type checks:", concretes.size(), ptypes.length)); + for (int i = 0; i < concretes.size(); i++) { + builder.append(MetaUtil.format(" %H.%n(%p):%r", concretes.get(i))); + } + return builder.toString(); + } + } + + + /** + * Represents an inlining opportunity where the current class hierarchy leads to a monomorphic target method, + * but for which an assumption has to be registered because of non-final classes. + */ + private static class AssumptionInlineInfo extends ExactInlineInfo { + public final ResolvedJavaType context; + + public AssumptionInlineInfo(Invoke invoke, double weight, ResolvedJavaType context, ResolvedJavaMethod concrete) { + super(invoke, weight, concrete); + this.context = context; + } + + @Override + public void inline(StructuredGraph graph, GraalCodeCacheProvider runtime, InliningCallback callback, Assumptions assumptions) { + if (Debug.isLogEnabled()) { + String targetName = MetaUtil.format("%H.%n(%p):%r", invoke.methodCallTarget().targetMethod()); + String concreteName = MetaUtil.format("%H.%n(%p):%r", concrete); + Debug.log("recording concrete method assumption: %s on receiver type %s -> %s", targetName, context, concreteName); + } + assumptions.recordConcreteMethod(invoke.methodCallTarget().targetMethod(), context, concrete); + + super.inline(graph, runtime, callback, assumptions); + } + + @Override + public String toString() { + return "assumption " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + } + + /** + * Determines if inlining is possible at the given invoke node. + * @param invoke the invoke that should be inlined + * @param runtime a GraalRuntime instance used to determine of the invoke can be inlined and/or should be intrinsified + * @param inliningPolicy used to determine the weight of a specific inlining + * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke + */ + public static InlineInfo getInlineInfo(Invoke invoke, GraalCodeCacheProvider runtime, Assumptions assumptions, InliningPolicy inliningPolicy, OptimisticOptimizations optimisticOpts) { + if (!checkInvokeConditions(invoke)) { + return null; + } + ResolvedJavaMethod caller = getCaller(invoke); + MethodCallTargetNode callTarget = invoke.methodCallTarget(); + ResolvedJavaMethod targetMethod = callTarget.targetMethod(); + + if (callTarget.invokeKind() == InvokeKind.Special || targetMethod.canBeStaticallyBound()) { + if (!checkTargetConditions(invoke, targetMethod, optimisticOpts, runtime)) { + return null; + } + double weight = inliningPolicy.inliningWeight(caller, targetMethod, invoke); + return new ExactInlineInfo(invoke, weight, targetMethod); + } + ObjectStamp receiverStamp = callTarget.receiver().objectStamp(); + ResolvedJavaType receiverType = receiverStamp.type(); + if (receiverStamp.isExactType()) { + assert receiverType.isSubtypeOf(targetMethod.getDeclaringClass()) : receiverType + " subtype of " + targetMethod.getDeclaringClass() + " for " + targetMethod; + ResolvedJavaMethod resolved = receiverType.resolveMethod(targetMethod); + if (!checkTargetConditions(invoke, resolved, optimisticOpts, runtime)) { + return null; + } + double weight = inliningPolicy.inliningWeight(caller, resolved, invoke); + return new ExactInlineInfo(invoke, weight, resolved); + } + ResolvedJavaType holder = targetMethod.getDeclaringClass(); + + if (receiverStamp.type() != null) { + // the invoke target might be more specific than the holder (happens after inlining: locals lose their declared type...) + // TODO (lstadler) fix this + if (receiverType != null && receiverType.isSubtypeOf(holder)) { + holder = receiverType; + } + } + // TODO (thomaswue) fix this + if (assumptions.useOptimisticAssumptions()) { + ResolvedJavaMethod concrete = holder.findUniqueConcreteMethod(targetMethod); + if (concrete != null) { + if (!checkTargetConditions(invoke, concrete, optimisticOpts, runtime)) { + return null; + } + double weight = inliningPolicy.inliningWeight(caller, concrete, invoke); + return new AssumptionInlineInfo(invoke, weight, holder, concrete); + } + } + + // type check based inlining + return getTypeCheckedInlineInfo(invoke, inliningPolicy, caller, targetMethod, optimisticOpts, runtime); + } + + private static InlineInfo getTypeCheckedInlineInfo(Invoke invoke, InliningPolicy inliningPolicy, ResolvedJavaMethod caller, + ResolvedJavaMethod targetMethod, OptimisticOptimizations optimisticOpts, GraalCodeCacheProvider runtime) { + ProfilingInfo profilingInfo = caller.getProfilingInfo(); + JavaTypeProfile typeProfile = profilingInfo.getTypeProfile(invoke.bci()); + if (typeProfile == null) { + return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no type profile exists"); + } + + ProfiledType[] ptypes = typeProfile.getTypes(); + if (ptypes == null || ptypes.length <= 0) { + return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "no types/probabilities were recorded"); + } + + double notRecordedTypeProbability = typeProfile.getNotRecordedProbability(); + if (ptypes.length == 1 && notRecordedTypeProbability == 0) { + if (!optimisticOpts.inlineMonomorphicCalls()) { + return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining monomorphic calls is disabled"); + } + + ResolvedJavaType type = ptypes[0].getType(); + ResolvedJavaMethod concrete = type.resolveMethod(targetMethod); + if (!checkTargetConditions(invoke, concrete, optimisticOpts, runtime)) { + return null; + } + double weight = inliningPolicy.inliningWeight(caller, concrete, invoke); + return new TypeGuardInlineInfo(invoke, weight, concrete, type); + } else { + invoke.setPolymorphic(true); + + + if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) { + return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining polymorphic calls is disabled"); + } + if (!optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) { + return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "inlining megamorphic calls is disabled"); + } + + // TODO (chaeubl) inlining of multiple methods should work differently + // 1. check which methods can be inlined + // 2. for those methods, use weight and probability to compute which of them should be inlined + // 3. do the inlining + // a) all seen methods can be inlined -> do so and guard with deopt + // b) some methods can be inlined -> inline them and fall back to invocation if violated + + // determine concrete methods and map type to specific method + ArrayList concreteMethods = new ArrayList<>(); + int[] typesToConcretes = new int[ptypes.length]; + for (int i = 0; i < ptypes.length; i++) { + ResolvedJavaMethod concrete = ptypes[i].getType().resolveMethod(targetMethod); + + int index = concreteMethods.indexOf(concrete); + if (index < 0) { + index = concreteMethods.size(); + concreteMethods.add(concrete); + } + typesToConcretes[i] = index; + } + + double totalWeight = 0; + for (ResolvedJavaMethod concrete: concreteMethods) { + if (!checkTargetConditions(invoke, concrete, optimisticOpts, runtime)) { + return logNotInlinedMethodAndReturnNull(invoke, targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined"); + } + totalWeight += inliningPolicy.inliningWeight(caller, concrete, invoke); + } + return new MultiTypeGuardInlineInfo(invoke, totalWeight, concreteMethods, ptypes, typesToConcretes, notRecordedTypeProbability); + } + } + + + private static ResolvedJavaMethod getCaller(Invoke invoke) { + return invoke.stateAfter().method(); + } + + private static PiNode createAnchoredReceiver(StructuredGraph graph, FixedNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) { + // to avoid that floating reads on receiver fields float above the type check + return graph.unique(new PiNode(receiver, anchor, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredNonNull(commonType))); + } + + private static boolean checkInvokeConditions(Invoke invoke) { + if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { + return logNotInlinedMethodAndReturnFalse(invoke, "the invoke has already been lowered, or has been created as a low-level node"); + } else if (invoke.methodCallTarget().targetMethod() == null) { + return logNotInlinedMethodAndReturnFalse(invoke, "target method is null"); + } else if (invoke.stateAfter() == null) { + return logNotInlinedMethodAndReturnFalse(invoke, "the invoke has no after state"); + } else if (invoke.predecessor() == null || !invoke.node().isAlive()) { + return logNotInlinedMethodAndReturnFalse(invoke, "the invoke is dead code"); + } else if (!invoke.useForInlining()) { + return logNotInlinedMethodAndReturnFalse(invoke, "the invoke is marked to be not used for inlining"); + } else { + return true; + } + } + + private static boolean checkTargetConditions(Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts, GraalCodeCacheProvider runtime) { + if (method == null) { + return logNotInlinedMethodAndReturnFalse(invoke, method, "the method is not resolved"); + } else if (Modifier.isNative(method.getModifiers()) && (!GraalOptions.Intrinsify || !InliningUtil.canIntrinsify(invoke, method, runtime))) { + return logNotInlinedMethodAndReturnFalse(invoke, method, "it is a non-intrinsic native method"); + } else if (Modifier.isAbstract(method.getModifiers())) { + return logNotInlinedMethodAndReturnFalse(invoke, method, "it is an abstract method"); + } else if (!method.getDeclaringClass().isInitialized()) { + return logNotInlinedMethodAndReturnFalse(invoke, method, "the method's class is not initialized"); + } else if (!method.canBeInlined()) { + return logNotInlinedMethodAndReturnFalse(invoke, method, "it is marked non-inlinable"); + } else if (computeInliningLevel(invoke) > GraalOptions.MaximumInlineLevel) { + return logNotInlinedMethodAndReturnFalse(invoke, method, "it exceeds the maximum inlining depth"); + } else if (computeRecursiveInliningLevel(invoke.stateAfter(), method) > GraalOptions.MaximumRecursiveInlining) { + return logNotInlinedMethodAndReturnFalse(invoke, method, "it exceeds the maximum recursive inlining depth"); + } else if (new OptimisticOptimizations(method).lessOptimisticThan(optimisticOpts)) { + return logNotInlinedMethodAndReturnFalse(invoke, method, "the callee uses less optimistic optimizations than caller"); + } else { + return true; + } + } + + private static int computeInliningLevel(Invoke invoke) { + int count = -1; + FrameState curState = invoke.stateAfter(); + while (curState != null) { + count++; + curState = curState.outerFrameState(); + } + return count; + } + + private static int computeRecursiveInliningLevel(FrameState state, ResolvedJavaMethod method) { + assert state != null; + + int count = 0; + FrameState curState = state; + while (curState != null) { + if (curState.method() == method) { + count++; + } + curState = curState.outerFrameState(); + } + return count; + } + + /** + * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph. + * + * @param invoke the invoke that will be replaced + * @param inlineGraph the graph that the invoke will be replaced with + * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings, false if no such check is required + */ + public static void inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) { + NodeInputList parameters = invoke.callTarget().arguments(); + StructuredGraph graph = (StructuredGraph) invoke.node().graph(); + + FrameState stateAfter = invoke.stateAfter(); + assert stateAfter.isAlive(); + + IdentityHashMap replacements = new IdentityHashMap<>(); + ArrayList nodes = new ArrayList<>(); + ReturnNode returnNode = null; + UnwindNode unwindNode = null; + StartNode entryPointNode = inlineGraph.start(); + FixedNode firstCFGNode = entryPointNode.next(); + for (Node node : inlineGraph.getNodes()) { + if (node == entryPointNode || node == entryPointNode.stateAfter()) { + // Do nothing. + } else if (node instanceof LocalNode) { + replacements.put(node, parameters.get(((LocalNode) node).index())); + } else { + nodes.add(node); + if (node instanceof ReturnNode) { + assert returnNode == null; + returnNode = (ReturnNode) node; + } else if (node instanceof UnwindNode) { + assert unwindNode == null; + unwindNode = (UnwindNode) node; + } + } + } + replacements.put(entryPointNode, BeginNode.prevBegin(invoke.node())); // ensure proper anchoring of things that where anchored to the StartNode + + assert invoke.node().successors().first() != null : invoke; + assert invoke.node().predecessor() != null; + + Map duplicates = graph.addDuplicates(nodes, replacements); + FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode); + if (receiverNullCheck) { + receiverNullCheck(invoke); + } + invoke.node().replaceAtPredecessor(firstCFGNodeDuplicate); + + FrameState stateAtExceptionEdge = null; + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = ((InvokeWithExceptionNode) invoke); + if (unwindNode != null) { + assert unwindNode.predecessor() != null; + assert invokeWithException.exceptionEdge().successors().count() == 1; + ExceptionObjectNode obj = (ExceptionObjectNode) invokeWithException.exceptionEdge().next(); + stateAtExceptionEdge = obj.stateAfter(); + UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); + obj.replaceAtUsages(unwindDuplicate.exception()); + unwindDuplicate.clearInputs(); + Node n = obj.next(); + obj.setNext(null); + unwindDuplicate.replaceAndDelete(n); + } else { + invokeWithException.killExceptionEdge(); + } + } else { + if (unwindNode != null) { + UnwindNode unwindDuplicate = (UnwindNode) duplicates.get(unwindNode); + DeoptimizeNode deoptimizeNode = new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler, invoke.leafGraphId()); + unwindDuplicate.replaceAndDelete(graph.add(deoptimizeNode)); + // move the deopt upwards if there is a monitor exit that tries to use the "after exception" frame state + // (because there is no "after exception" frame state!) + if (deoptimizeNode.predecessor() instanceof MonitorExitNode) { + MonitorExitNode monitorExit = (MonitorExitNode) deoptimizeNode.predecessor(); + if (monitorExit.stateAfter() != null && monitorExit.stateAfter().bci == FrameState.AFTER_EXCEPTION_BCI) { + FrameState monitorFrameState = monitorExit.stateAfter(); + graph.removeFixed(monitorExit); + monitorFrameState.safeDelete(); + } + } + } + } + + FrameState outerFrameState = null; + double invokeProbability = invoke.node().probability(); + for (Node node : duplicates.values()) { + if (GraalOptions.ProbabilityAnalysis) { + if (node instanceof FixedNode) { + FixedNode fixed = (FixedNode) node; + double newProbability = fixed.probability() * invokeProbability; + if (GraalOptions.LimitInlinedProbability) { + newProbability = Math.min(newProbability, invokeProbability); + } + fixed.setProbability(newProbability); + } + } + if (node instanceof FrameState) { + FrameState frameState = (FrameState) node; + assert frameState.bci != FrameState.BEFORE_BCI; + if (frameState.bci == FrameState.AFTER_BCI) { + frameState.replaceAndDelete(stateAfter); + } else if (frameState.bci == FrameState.AFTER_EXCEPTION_BCI) { + if (frameState.isAlive()) { + assert stateAtExceptionEdge != null; + frameState.replaceAndDelete(stateAtExceptionEdge); + } else { + assert stateAtExceptionEdge == null; + } + } else { + // only handle the outermost frame states + if (frameState.outerFrameState() == null) { + assert frameState.method() == inlineGraph.method(); + if (outerFrameState == null) { + outerFrameState = stateAfter.duplicateModified(invoke.bci(), stateAfter.rethrowException(), invoke.node().kind()); + outerFrameState.setDuringCall(true); + } + frameState.setOuterFrameState(outerFrameState); + } + } + } + } + + Node returnValue = null; + if (returnNode != null) { + if (returnNode.result() instanceof LocalNode) { + returnValue = replacements.get(returnNode.result()); + } else { + returnValue = duplicates.get(returnNode.result()); + } + invoke.node().replaceAtUsages(returnValue); + Node returnDuplicate = duplicates.get(returnNode); + returnDuplicate.clearInputs(); + Node n = invoke.next(); + invoke.setNext(null); + returnDuplicate.replaceAndDelete(n); + } + + invoke.node().replaceAtUsages(null); + GraphUtil.killCFG(invoke.node()); + } + + public static void receiverNullCheck(Invoke invoke) { + MethodCallTargetNode callTarget = invoke.methodCallTarget(); + StructuredGraph graph = (StructuredGraph) invoke.graph(); + NodeInputList parameters = callTarget.arguments(); + ValueNode firstParam = parameters.size() <= 0 ? null : parameters.get(0); + if (!callTarget.isStatic() && firstParam.kind() == Kind.Object && !firstParam.objectStamp().nonNull()) { + graph.addBeforeFixed(invoke.node(), graph.add(new FixedGuardNode(graph.unique(new IsNullNode(firstParam)), DeoptimizationReason.NullCheckException, DeoptimizationAction.InvalidateReprofile, true, invoke.leafGraphId()))); + } + } + + public static boolean canIntrinsify(Invoke invoke, ResolvedJavaMethod target, GraalCodeCacheProvider runtime) { + return getIntrinsicGraph(invoke, target, runtime) != null; + } + + private static StructuredGraph getIntrinsicGraph(Invoke invoke, ResolvedJavaMethod target, GraalCodeCacheProvider runtime) { + assert invoke.node().isAlive(); + + StructuredGraph intrinsicGraph = (StructuredGraph) target.getCompilerStorage().get(Graph.class); + if (intrinsicGraph == null) { + // TODO remove once all intrinsics are available via compilerStorage + intrinsicGraph = runtime.intrinsicGraph(invoke.stateAfter().method(), invoke.bci(), target, invoke.callTarget().arguments()); + } + return intrinsicGraph; + } +} diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java Mon Nov 26 10:57:02 2012 +0100 @@ -142,7 +142,6 @@ public static int PrintBinaryGraphPort = 4445; // Other printing settings - public static boolean PrintAddressMap = ____; public static boolean PrintQueue = ____; public static boolean PrintCompilation = ____; public static boolean PrintProfilingInformation = ____; diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetTemplate.java --- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetTemplate.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetTemplate.java Mon Nov 26 10:57:02 2012 +0100 @@ -389,8 +389,8 @@ private static boolean checkConstantArgument(final ResolvedJavaMethod method, Signature signature, int i, String name, Object arg, Kind kind) { if (kind.isObject()) { - Class type = signature.getParameterType(i, method.getDeclaringClass()).resolve(method.getDeclaringClass()).toJava(); - assert arg == null || type.isInstance(arg) : + ResolvedJavaType type = signature.getParameterType(i, method.getDeclaringClass()).resolve(method.getDeclaringClass()); + assert arg == null || type.isInstance(Constant.forObject(arg)) : method + ": wrong value type for " + name + ": expected " + type.getName() + ", got " + arg.getClass().getName(); } else { assert arg != null && kind.toBoxedJavaClass() == arg.getClass() : @@ -402,9 +402,8 @@ private static boolean checkVarargs(final ResolvedJavaMethod method, Signature signature, int i, String name, Varargs varargs) { Object arg = varargs.getArray(); ResolvedJavaType type = (ResolvedJavaType) signature.getParameterType(i, method.getDeclaringClass()); - Class< ? > javaType = type.toJava(); - assert javaType.isArray() : "varargs parameter must be an array type"; - assert javaType.isInstance(arg) : "value for " + name + " is not a " + javaType.getName() + " instance: " + arg; + assert type.isArrayClass() : "varargs parameter must be an array type"; + assert type.isInstance(Constant.forObject(arg)) : "value for " + name + " is not a " + MetaUtil.toJavaName(type) + " instance: " + arg; return true; } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetVerificationPhase.java --- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetVerificationPhase.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetVerificationPhase.java Mon Nov 26 10:57:02 2012 +0100 @@ -83,7 +83,7 @@ ValueNode argument = arguments.get(argc); if (argument == node) { ResolvedJavaType type = (ResolvedJavaType) signature.getParameterType(i, method.getDeclaringClass()); - verify((type.toJava() == Word.class) == isWord(argument), node, invoke.node(), "cannot pass word value to non-word parameter " + i + " or vice-versa"); + verify((type.isClass(Word.class)) == isWord(argument), node, invoke.node(), "cannot pass word value to non-word parameter " + i + " or vice-versa"); } argc++; } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/MaterializeObjectNode.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/MaterializeObjectNode.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/MaterializeObjectNode.java Mon Nov 26 10:57:02 2012 +0100 @@ -101,27 +101,11 @@ } @Override - public EscapeOp getEscapeOp() { - if (!shouldRevirtualize(this)) { - return null; + public ObjectDesc[] getAllocations(long nextVirtualId, MetaAccessProvider metaAccess) { + if (shouldRevirtualize(this)) { + return new ObjectDesc[] {new ObjectDesc(virtualObject, values.toArray(new ValueNode[values.size()]), lockCount)}; } - return new EscapeOp() { - - @Override - public ValueNode[] fieldState() { - return values.toArray(new ValueNode[values.size()]); - } - - @Override - public VirtualObjectNode virtualObject(long virtualId) { - return virtualObject; - } - - @Override - public int lockCount() { - return lockCount; - } - }; + return null; } private boolean shouldRevirtualize(MaterializeObjectNode materializeObjectNode) { diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java Mon Nov 26 10:57:02 2012 +0100 @@ -158,8 +158,10 @@ void addAndMarkAlias(VirtualObjectNode virtual, ValueNode node, NodeBitMap usages) { objectAliases.put(node, virtual); - for (Node usage : node.usages()) { - markVirtualUsages(usage, usages); + if (node.isAlive()) { + for (Node usage : node.usages()) { + markVirtualUsages(usage, usages); + } } } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java Mon Nov 26 10:57:02 2012 +0100 @@ -40,11 +40,13 @@ private final TargetDescription target; private final MetaAccessProvider runtime; private final Assumptions assumptions; + private final boolean iterative; - public PartialEscapeAnalysisPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions) { + public PartialEscapeAnalysisPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions, boolean iterative) { this.target = target; this.runtime = runtime; this.assumptions = assumptions; + this.iterative = iterative; } public static final void trace(String format, Object... obj) { @@ -87,6 +89,9 @@ assert noObsoleteNodes(graph, obsoleteNodes); new DeadCodeEliminationPhase().apply(graph); + if (!iterative) { + return; + } if (GraalOptions.OptCanonicalizer) { new CanonicalizerPhase(target, runtime, assumptions).apply(graph); } diff -r e5ec98288b91 -r c2a3b92c9e79 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java Mon Nov 26 10:51:39 2012 +0100 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java Mon Nov 26 10:57:02 2012 +0100 @@ -36,6 +36,7 @@ import com.oracle.graal.nodes.VirtualState.NodeClosure; import com.oracle.graal.nodes.cfg.*; import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.spi.EscapeAnalyzable.ObjectDesc; import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.graph.*; @@ -71,10 +72,12 @@ private int virtualIds = 0; private final VirtualizerToolImpl tool; + private final MetaAccessProvider metaAccess; public PartialEscapeClosure(NodeBitMap usages, SchedulePhase schedule, MetaAccessProvider metaAccess) { this.usages = usages; this.schedule = schedule; + this.metaAccess = metaAccess; tool = new VirtualizerToolImpl(effects, usages, metaAccess); } @@ -93,29 +96,32 @@ FixedWithNextNode lastFixedNode = null; for (Node node : nodeList) { - EscapeOp op = null; + ObjectDesc[] newAllocations = null; if (node instanceof EscapeAnalyzable) { - op = ((EscapeAnalyzable) node).getEscapeOp(); + newAllocations = ((EscapeAnalyzable) node).getAllocations(virtualIds, metaAccess); } - if (op != null) { + if (newAllocations != null) { trace("{{%s}} ", node); - VirtualObjectNode virtualObject = op.virtualObject(virtualIds); - if (virtualObject.isAlive()) { - reusedVirtualObjects.add(virtualObject); + for (ObjectDesc desc : newAllocations) { + VirtualObjectNode virtualObject = desc.virtualObject; + if (virtualObject.isAlive()) { + reusedVirtualObjects.add(virtualObject); + state.addAndMarkAlias(virtualObject, virtualObject, usages); + } else { + effects.addFloatingNode(virtualObject); + } + ValueNode[] fieldState = desc.entryState; + for (int i = 0; i < fieldState.length; i++) { + fieldState[i] = state.getScalarAlias(fieldState[i]); + } + state.addObject(virtualObject, new ObjectState(virtualObject, fieldState, desc.lockCount)); state.addAndMarkAlias(virtualObject, virtualObject, usages); - } else { - effects.addFloatingNode(virtualObject); } - ValueNode[] fieldState = op.fieldState(); - for (int i = 0; i < fieldState.length; i++) { - fieldState[i] = state.getScalarAlias(fieldState[i]); - } - state.addObject(virtualObject, new ObjectState(virtualObject, fieldState, op.lockCount())); - state.addAndMarkAlias(virtualObject, (ValueNode) node, usages); + state.addAndMarkAlias(newAllocations[0].virtualObject, (ValueNode) node, usages); effects.deleteFixedNode((FixedWithNextNode) node); - virtualIds++; - metricAllocationRemoved.increment(); + metricAllocationRemoved.add(newAllocations.length); + virtualIds += newAllocations.length; } else { if (usages.isMarked(node)) { trace("[[%s]] ", node); diff -r e5ec98288b91 -r c2a3b92c9e79 mx/projects --- a/mx/projects Mon Nov 26 10:51:39 2012 +0100 +++ b/mx/projects Mon Nov 26 10:57:02 2012 +0100 @@ -41,6 +41,13 @@ project@com.oracle.graal.api.meta@checkstyle=com.oracle.graal.graph project@com.oracle.graal.api.meta@javaCompliance=1.7 +# graal.api.meta.test +project@com.oracle.graal.api.meta.test@subDir=graal +project@com.oracle.graal.api.meta.test@sourceDirs=src +project@com.oracle.graal.api.meta.test@dependencies=JUNIT,com.oracle.graal.api.meta,com.oracle.graal.api.runtime +project@com.oracle.graal.api.meta.test@checkstyle=com.oracle.graal.graph +project@com.oracle.graal.api.meta.test@javaCompliance=1.7 + # graal.api.code project@com.oracle.graal.api.code@subDir=graal project@com.oracle.graal.api.code@sourceDirs=src diff -r e5ec98288b91 -r c2a3b92c9e79 src/share/vm/graal/graalCodeInstaller.cpp --- a/src/share/vm/graal/graalCodeInstaller.cpp Mon Nov 26 10:51:39 2012 +0100 +++ b/src/share/vm/graal/graalCodeInstaller.cpp Mon Nov 26 10:57:02 2012 +0100 @@ -625,14 +625,13 @@ TRACE_graal_3("relocating (stub) at %p", inst); } else { // method != NULL assert(hotspot_method != NULL, "unexpected JavaMethod"); - assert(debug_info != NULL, "debug info expected"); - +#ifdef ASSERT Method* method = NULL; // we need to check, this might also be an unresolved method if (hotspot_method->is_a(HotSpotResolvedJavaMethod::klass())) { method = getMethodFromHotSpotMethod(hotspot_method); } - +#endif assert(debug_info != NULL, "debug info expected"); TRACE_graal_3("method call");