# HG changeset patch # User Michael Van De Vanter # Date 1400545290 25200 # Node ID bdf260d8e1639c9e0b8da7e6d444b988defc91b2 # Parent 8c34e2cc4add5e203601764f1a226066540202cb# Parent 9ae1d2f3bda60f9d91243c883c5aa7812e2ab256 Merge with 9ae1d2f3bda60f9d91243c883c5aa7812e2ab256 diff -r 8c34e2cc4add -r bdf260d8e163 CHANGELOG.md --- a/CHANGELOG.md Mon May 19 17:14:36 2014 -0700 +++ b/CHANGELOG.md Mon May 19 17:21:30 2014 -0700 @@ -3,6 +3,8 @@ ## `tip` ### Graal * Made initialization of Graal runtime lazy in hosted mode. +* Added supported for new 'jrelibrary' dependency type in mx/projects. +* Java projects with compliance level higher than the JDKs specified by JAVA_HOME and EXTRA_JAVA_HOMES are ignored once mx/projects has been processed. ### Truffle * `truffle.jar`: strip out build-time only dependency into a seperated JAR file (`truffle-dsl-processor.jar`) diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java --- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java Mon May 19 17:21:30 2014 -0700 @@ -96,7 +96,8 @@ public ConcreteSubtype(ResolvedJavaType context, ResolvedJavaType subtype) { this.context = context; this.subtype = subtype; - assert !subtype.isInterface() : subtype.toString() + " : " + context.toString(); + assert !subtype.isAbstract() : subtype.toString() + " : " + context.toString(); + assert !subtype.isArray() || getElementalType(subtype).isFinal() : subtype.toString() + " : " + context.toString(); } @Override @@ -264,7 +265,7 @@ /** * Returns whether any assumptions have been registered. - * + * * @return {@code true} if at least one assumption has been registered, {@code false} otherwise. */ public boolean isEmpty() { @@ -303,7 +304,7 @@ /** * Records an assumption that the specified type has no finalizable subclasses. - * + * * @param receiverType the type that is assumed to have no finalizable subclasses */ public void recordNoFinalizableSubclassAssumption(ResolvedJavaType receiverType) { @@ -314,7 +315,7 @@ /** * Records that {@code subtype} is the only concrete subtype in the class hierarchy below * {@code context}. - * + * * @param context the root of the subtree of the class hierarchy that this assumptions is about * @param subtype the one concrete subtype */ @@ -326,7 +327,7 @@ /** * Records that {@code impl} is the only possible concrete target for a virtual call to * {@code method} with a receiver of type {@code context}. - * + * * @param method a method that is the target of a virtual call * @param context the receiver type of a call to {@code method} * @param impl the concrete method that is the only possible target for the virtual call @@ -338,7 +339,7 @@ /** * Records that {@code method} was used during the compilation. - * + * * @param method a method whose contents were used */ public void recordMethodContents(ResolvedJavaMethod method) { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/ResolvedJavaTypeResolveMethodTest.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/ResolvedJavaTypeResolveMethodTest.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.api.meta.test; + +import static org.junit.Assert.*; + +import org.junit.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.runtime.*; +import com.oracle.graal.phases.util.*; +import com.oracle.graal.runtime.*; + +public class ResolvedJavaTypeResolveMethodTest { + public final MetaAccessProvider metaAccess; + + public ResolvedJavaTypeResolveMethodTest() { + Providers providers = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend().getProviders(); + metaAccess = providers.getMetaAccess(); + } + + protected static abstract class A { + @SuppressWarnings("unused") + private void priv() { + } + + public void v1() { + } + + public void v2() { + } + + public abstract void abs(); + } + + protected static class B extends A implements I { + public void i() { + } + + @Override + public void v2() { + } + + @Override + public void abs() { + + } + } + + protected static class C extends B { + public void d() { + } + } + + protected static abstract class D extends A { + + } + + protected static class E extends D { + @Override + public void abs() { + } + } + + protected interface I { + void i(); + + default void d() { + } + } + + @Test + public void testDefaultMethod() { + ResolvedJavaType i = getType(I.class); + ResolvedJavaType b = getType(B.class); + ResolvedJavaType c = getType(C.class); + ResolvedJavaMethod di = getMethod(i, "d"); + ResolvedJavaMethod dc = getMethod(c, "d"); + + assertEquals(di, i.resolveMethod(di, c)); + assertEquals(di, b.resolveMethod(di, c)); + assertEquals(dc, c.resolveMethod(di, c)); + } + + @Test + public void testPrivateMethod() { + ResolvedJavaType a = getType(A.class); + ResolvedJavaType b = getType(B.class); + ResolvedJavaType c = getType(C.class); + ResolvedJavaMethod priv = getMethod(a, "priv"); + + assertNull(a.resolveMethod(priv, c)); + assertNull(b.resolveMethod(priv, c)); + } + + @Test + public void testAbstractMethod() { + ResolvedJavaType a = getType(A.class); + ResolvedJavaType b = getType(B.class); + ResolvedJavaType c = getType(C.class); + ResolvedJavaType d = getType(D.class); + ResolvedJavaType e = getType(E.class); + ResolvedJavaMethod absa = getMethod(a, "abs"); + ResolvedJavaMethod absb = getMethod(b, "abs"); + ResolvedJavaMethod abse = getMethod(e, "abs"); + + assertNull(a.resolveMethod(absa, c)); + assertNull(d.resolveMethod(absa, c)); + + assertEquals(absb, b.resolveMethod(absa, c)); + assertEquals(absb, b.resolveMethod(absb, c)); + assertEquals(absb, c.resolveMethod(absa, c)); + assertEquals(absb, c.resolveMethod(absb, c)); + assertEquals(abse, e.resolveMethod(absa, c)); + assertNull(e.resolveMethod(absb, c)); + assertEquals(abse, e.resolveMethod(abse, c)); + } + + @Test + public void testVirtualMethod() { + ResolvedJavaType a = getType(A.class); + ResolvedJavaType b = getType(B.class); + ResolvedJavaType c = getType(C.class); + ResolvedJavaMethod v1a = getMethod(a, "v1"); + ResolvedJavaMethod v2a = getMethod(a, "v2"); + ResolvedJavaMethod v2b = getMethod(b, "v2"); + + assertEquals(v1a, a.resolveMethod(v1a, c)); + assertEquals(v1a, b.resolveMethod(v1a, c)); + assertEquals(v1a, c.resolveMethod(v1a, c)); + assertEquals(v2a, a.resolveMethod(v2a, c)); + assertEquals(v2b, b.resolveMethod(v2a, c)); + assertEquals(v2b, b.resolveMethod(v2b, c)); + assertEquals(v2b, c.resolveMethod(v2a, c)); + assertEquals(v2b, c.resolveMethod(v2b, c)); + + } + + private static ResolvedJavaMethod getMethod(ResolvedJavaType type, String methodName) { + for (ResolvedJavaMethod method : type.getDeclaredMethods()) { + if (method.getName().equals(methodName)) { + return method; + } + } + throw new IllegalArgumentException(); + } + + protected ResolvedJavaType getType(Class clazz) { + ResolvedJavaType type = metaAccess.lookupJavaType(clazz); + type.initialize(); + return type; + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaType.java --- a/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaType.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaType.java Mon May 19 17:21:30 2014 -0700 @@ -265,10 +265,13 @@ static class Concrete3 extends Concrete2 { } + static final class Final1 extends Abstract1 { + } + abstract static class Abstract4 extends Concrete3 { } - void checkConcreteSubtype(ResolvedJavaType type, Class expected) { + void checkConcreteSubtype(ResolvedJavaType type, ResolvedJavaType expected) { ResolvedJavaType subtype = type.findUniqueConcreteSubtype(); if (subtype == null) { // findUniqueConcreteSubtype() is conservative @@ -276,7 +279,7 @@ if (expected == null) { assertNull(subtype); } else { - assertTrue(subtype.equals(metaAccess.lookupJavaType(expected))); + assertTrue(subtype.equals(expected)); } } @@ -294,31 +297,44 @@ @Test public void findUniqueConcreteSubtypeTest() { ResolvedJavaType base = metaAccess.lookupJavaType(Base.class); - checkConcreteSubtype(base, Base.class); + checkConcreteSubtype(base, base); ResolvedJavaType a1 = metaAccess.lookupJavaType(Abstract1.class); ResolvedJavaType c1 = metaAccess.lookupJavaType(Concrete1.class); checkConcreteSubtype(base, null); - checkConcreteSubtype(a1, Concrete1.class); - checkConcreteSubtype(c1, Concrete1.class); + checkConcreteSubtype(a1, c1); + checkConcreteSubtype(c1, c1); ResolvedJavaType i1 = metaAccess.lookupJavaType(Interface1.class); ResolvedJavaType c2 = metaAccess.lookupJavaType(Concrete2.class); checkConcreteSubtype(base, null); checkConcreteSubtype(a1, null); - checkConcreteSubtype(c1, Concrete1.class); - checkConcreteSubtype(i1, Concrete2.class); - checkConcreteSubtype(c2, Concrete2.class); + checkConcreteSubtype(c1, c1); + checkConcreteSubtype(i1, c2); + checkConcreteSubtype(c2, c2); ResolvedJavaType c3 = metaAccess.lookupJavaType(Concrete3.class); checkConcreteSubtype(c2, null); - checkConcreteSubtype(c3, Concrete3.class); + checkConcreteSubtype(c3, c3); ResolvedJavaType a4 = metaAccess.lookupJavaType(Abstract4.class); checkConcreteSubtype(c3, null); checkConcreteSubtype(a4, null); + + ResolvedJavaType a1a = metaAccess.lookupJavaType(Abstract1[].class); + checkConcreteSubtype(a1a, null); + ResolvedJavaType c1a = metaAccess.lookupJavaType(Concrete1[].class); + checkConcreteSubtype(c1a, null); + ResolvedJavaType f1a = metaAccess.lookupJavaType(Final1[].class); + checkConcreteSubtype(f1a, f1a); + + ResolvedJavaType obja = metaAccess.lookupJavaType(Object[].class); + checkConcreteSubtype(obja, null); + + ResolvedJavaType inta = metaAccess.lookupJavaType(int[].class); + checkConcreteSubtype(inta, inta); } @Test @@ -431,20 +447,21 @@ return declarations; } - private static void checkResolveMethod(ResolvedJavaType type, ResolvedJavaMethod decl, ResolvedJavaMethod expected) { - ResolvedJavaMethod impl = type.resolveMethod(decl); + private static void checkResolveMethod(ResolvedJavaType type, ResolvedJavaType context, ResolvedJavaMethod decl, ResolvedJavaMethod expected) { + ResolvedJavaMethod impl = type.resolveMethod(decl, context); assertEquals(expected, impl); } @Test public void resolveMethodTest() { + ResolvedJavaType context = metaAccess.lookupJavaType(TestResolvedJavaType.class); for (Class c : classes) { if (c.isInterface() || c.isPrimitive()) { ResolvedJavaType type = metaAccess.lookupJavaType(c); for (Method m : c.getDeclaredMethods()) { if (JAVA_VERSION <= 1.7D || (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers()))) { ResolvedJavaMethod resolved = metaAccess.lookupJavaMethod(m); - ResolvedJavaMethod impl = type.resolveMethod(resolved); + ResolvedJavaMethod impl = type.resolveMethod(resolved, context); ResolvedJavaMethod expected = resolved.isDefault() ? resolved : null; assertEquals(m.toString(), expected, impl); } else { @@ -458,12 +475,14 @@ Set decls = findDeclarations(impl, c); for (Method decl : decls) { ResolvedJavaMethod m = metaAccess.lookupJavaMethod(decl); - ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); - checkResolveMethod(type, m, i); + if (m.isPublic()) { + ResolvedJavaMethod i = metaAccess.lookupJavaMethod(impl); + checkResolveMethod(type, context, m, i); + } } } for (Method m : c.getDeclaredMethods()) { - ResolvedJavaMethod impl = type.resolveMethod(metaAccess.lookupJavaMethod(m)); + ResolvedJavaMethod impl = type.resolveMethod(metaAccess.lookupJavaMethod(m), context); ResolvedJavaMethod expected = isAbstract(m.getModifiers()) ? null : impl; assertEquals(type + " " + m.toString(), expected, impl); } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java --- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java Mon May 19 17:21:30 2014 -0700 @@ -218,9 +218,10 @@ Constant getEncoding(); /** - * Checks if this method is present in the virtual table. + * Checks if this method is present in the virtual table for subtypes of the specified + * {@linkplain ResolvedJavaType type}. * - * @return true is this method is present in the virtual table + * @return true is this method is present in the virtual table for subtypes of this type. */ - boolean isInVirtualMethodTable(); + boolean isInVirtualMethodTable(ResolvedJavaType resolved); } diff -r 8c34e2cc4add -r bdf260d8e163 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 May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaType.java Mon May 19 17:21:30 2014 -0700 @@ -214,7 +214,7 @@ * @return the concrete method that would be selected at runtime, or {@code null} if there is no * concrete implementation of {@code method} in this type or any of its superclasses */ - ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method); + ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType); /** * Given a {@link ResolvedJavaMethod} A, returns a concrete {@link ResolvedJavaMethod} B that is diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java --- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java Mon May 19 17:21:30 2014 -0700 @@ -784,15 +784,39 @@ } public final void idivl(Register src) { - int encode = prefixAndEncode(src.encoding); + int encode = prefixAndEncode(7, src.encoding); emitByte(0xF7); - emitByte(0xF8 | encode); + emitByte(0xC0 | encode); } public final void divl(Register src) { - int encode = prefixAndEncode(src.encoding); + int encode = prefixAndEncode(6, src.encoding); + emitByte(0xF7); + emitByte(0xC0 | encode); + } + + public final void mull(Register src) { + int encode = prefixAndEncode(4, src.encoding); + emitByte(0xF7); + emitByte(0xC0 | encode); + } + + public final void mull(AMD64Address src) { + prefix(src); emitByte(0xF7); - emitByte(0xF0 | encode); + emitOperandHelper(4, src); + } + + public final void imull(Register src) { + int encode = prefixAndEncode(5, src.encoding); + emitByte(0xF7); + emitByte(0xC0 | encode); + } + + public final void imull(AMD64Address src) { + prefix(src); + emitByte(0xF7); + emitOperandHelper(5, src); } public final void imull(Register dst, Register src) { @@ -2346,15 +2370,39 @@ } public final void divq(Register src) { - int encode = prefixqAndEncode(src.encoding); + int encode = prefixqAndEncode(6, src.encoding); emitByte(0xF7); - emitByte(0xF0 | encode); + emitByte(0xC0 | encode); } public final void idivq(Register src) { - int encode = prefixqAndEncode(src.encoding); + int encode = prefixqAndEncode(7, src.encoding); + emitByte(0xF7); + emitByte(0xC0 | encode); + } + + public final void mulq(Register src) { + int encode = prefixqAndEncode(4, src.encoding); + emitByte(0xF7); + emitByte(0xC0 | encode); + } + + public final void mulq(AMD64Address src) { + prefixq(src); emitByte(0xF7); - emitByte(0xF8 | encode); + emitOperandHelper(4, src); + } + + public final void imulq(Register src) { + int encode = prefixqAndEncode(5, src.encoding); + emitByte(0xF7); + emitByte(0xC0 | encode); + } + + public final void imulq(AMD64Address src) { + prefixq(src); + emitByte(0xF7); + emitOperandHelper(5, src); } public final void imulq(Register dst, Register src) { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java --- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java Mon May 19 17:21:30 2014 -0700 @@ -601,6 +601,37 @@ } } + private Value emitMulHigh(AMD64Arithmetic opcode, Value a, Value b) { + MulHighOp mulHigh = new MulHighOp(opcode, asAllocatable(b)); + emitMove(mulHigh.x, a); + append(mulHigh); + return emitMove(mulHigh.highResult); + } + + @Override + public Value emitMulHigh(Value a, Value b) { + switch (a.getKind().getStackKind()) { + case Int: + return emitMulHigh(IMUL, a, b); + case Long: + return emitMulHigh(LMUL, a, b); + default: + throw GraalInternalError.shouldNotReachHere(); + } + } + + @Override + public Value emitUMulHigh(Value a, Value b) { + switch (a.getKind().getStackKind()) { + case Int: + return emitMulHigh(IUMUL, a, b); + case Long: + return emitMulHigh(LUMUL, a, b); + default: + throw GraalInternalError.shouldNotReachHere(); + } + } + public Value emitBinaryMemory(AMD64Arithmetic op, Kind kind, AllocatableValue a, AMD64AddressValue location, LIRFrameState state) { Variable result = newVariable(a.getKind()); append(new BinaryMemory(op, kind, result, a, location, state)); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/ObjectStamp.java --- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/ObjectStamp.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/ObjectStamp.java Mon May 19 17:21:30 2014 -0700 @@ -107,6 +107,11 @@ return StampFactory.illegal(Kind.Illegal); } ObjectStamp other = (ObjectStamp) otherStamp; + if (!isLegal()) { + return other; + } else if (!other.isLegal()) { + return this; + } ResolvedJavaType meetType; boolean meetExactType; boolean meetNonNull; diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java --- a/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java Mon May 19 17:21:30 2014 -0700 @@ -131,11 +131,18 @@ } /** + * Determines if the JVM supports the required typeProfileWidth. + */ + public boolean typeProfileWidthAtLeast(int val) { + return (getHSAILBackend().getRuntime().getConfig().typeProfileWidth >= val); + } + + /** * Determines if the runtime supports {@link VirtualObject}s in {@link DebugInfo} associated * with HSAIL code. */ public boolean canHandleDeoptVirtualObjects() { - return false; + return true; } /** diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VecmathNBodyDeoptTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VecmathNBodyDeoptTest.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test.lambda; + +import java.util.*; +import org.junit.*; +import com.oracle.graal.compiler.hsail.test.infra.GraalKernelTester; +import javax.vecmath.*; + +/** + * Tests NBody algorithm using the javax.vecmath package (all objects non-escaping). + */ +public class VecmathNBodyDeoptTest extends GraalKernelTester { + static final int bodies = 1024; + static final float delT = .005f; + static final float espSqr = 1.0f; + static final float mass = 5f; + static final int width = 768; + static final int height = 768; + + static class Body extends Vector3f { + + /** + * + */ + private static final long serialVersionUID = 1L; + + public Body(float _x, float _y, float _z, float _m) { + super(_x, _y, _z); + m = _m; + v = new Vector3f(0, 0, 0); + } + + float m; + Vector3f v; + + public float getM() { + return m; + } + + public Vector3f computeAcc(Body[] in_bodies, float espSqr1, float delT1) { + Vector3f acc = new Vector3f(); + + for (Body b : in_bodies) { + Vector3f d = new Vector3f(); + d.sub(b, this); + float invDist = 1.0f / (float) Math.sqrt(d.lengthSquared() + espSqr1); + float s = b.getM() * invDist * invDist * invDist; + acc.scaleAdd(s, d, acc); + } + + // now return acc scaled by delT + acc.scale(delT1); + return acc; + } + } + + @Result Body[] in_bodies = new Body[bodies]; + @Result Body[] out_bodies = new Body[bodies]; + + static Body[] seed_bodies = new Body[bodies]; + + static { + java.util.Random randgen = new Random(0); + final float maxDist = width / 4; + for (int body = 0; body < bodies; body++) { + final float theta = (float) (randgen.nextFloat() * Math.PI * 2); + final float phi = (float) (randgen.nextFloat() * Math.PI * 2); + final float radius = randgen.nextFloat() * maxDist; + float x = (float) (radius * Math.cos(theta) * Math.sin(phi)) + width / 2; + float y = (float) (radius * Math.sin(theta) * Math.sin(phi)) + height / 2; + float z = (float) (radius * Math.cos(phi)); + seed_bodies[body] = new Body(x, y, z, mass); + } + } + + @Override + public void runTest() { + System.arraycopy(seed_bodies, 0, in_bodies, 0, seed_bodies.length); + for (int b = 0; b < bodies; b++) { + out_bodies[b] = new Body(0, 0, 0, mass); + } + // no local copies of arrays so we make it an instance lambda + + dispatchLambdaKernel(bodies, (gid) -> { + Body inb = in_bodies[gid]; + Body outb = out_bodies[gid]; + Vector3f acc = inb.computeAcc(in_bodies, espSqr, delT); + + Vector3f tmpPos = new Vector3f(); + tmpPos.scaleAdd(delT, inb.v, inb); + if (gid == bodies / 2) { + tmpPos.x += forceDeopt(gid); + } + tmpPos.scaleAdd(0.5f * delT, acc, tmpPos); + outb.set(tmpPos); + + outb.v.add(inb.v, acc); + }); + } + + @Override + protected boolean supportsRequiredCapabilities() { + return (canHandleDeoptVirtualObjects()); + } + + @Test + public void test() { + testGeneratedHsail(); + } + + @Test + public void testUsingLambdaMethod() { + testGeneratedHsailUsingLambdaMethod(); + } + +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCall3Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCall3Test.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test.lambda; + +import org.junit.Test; + +/** + * Tests a true virtual method call with 3 targets. + */ +public class VirtualCall3Test extends VirtualCallBase { + + void setupArrays() { + for (int i = 0; i < NUM; i++) { + outArray[i] = -i; + inShapeArray[i] = createShape(i % 3, i + 1); + } + } + + // although runTest is the same in each class derived from VirtualCallBase + // we duplicate the logic in each derived test so as to have different lambda call sites + @Override + public void runTest() { + setupArrays(); + + dispatchLambdaKernel(NUM, (gid) -> { + Shape shape = inShapeArray[gid]; + outArray[gid] = shape.getArea(); + }); + } + + @Override + protected boolean supportsRequiredCapabilities() { + return typeProfileWidthAtLeast(3); + } + + @Test + public void test() { + testGeneratedHsail(); + } + + @Test + public void testUsingLambdaMethod() { + testGeneratedHsailUsingLambdaMethod(); + } + +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCall4Test.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCall4Test.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test.lambda; + +import org.junit.Test; + +/** + * Tests a true virtual method call with 4 targets. + */ +public class VirtualCall4Test extends VirtualCallBase { + + void setupArrays() { + for (int i = 0; i < NUM; i++) { + outArray[i] = -i; + inShapeArray[i] = createShape(i % 4, i + 1); + } + } + + // although runTest is the same in each class derived from VirtualCallBase + // we duplicate the logic in each derived test so as to have different lambda call sites + @Override + public void runTest() { + setupArrays(); + + dispatchLambdaKernel(NUM, (gid) -> { + Shape shape = inShapeArray[gid]; + outArray[gid] = shape.getArea(); + }); + } + + @Override + protected boolean supportsRequiredCapabilities() { + return typeProfileWidthAtLeast(4); + } + + @Test + public void test() { + testGeneratedHsail(); + } + + @Test + public void testUsingLambdaMethod() { + testGeneratedHsailUsingLambdaMethod(); + } + +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCallBase.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCallBase.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.graal.compiler.hsail.test.lambda; + +import com.oracle.graal.compiler.hsail.test.infra.GraalKernelTester; + +/** + * Base class for testing virtual method calls. + */ +abstract public class VirtualCallBase extends GraalKernelTester { + + static final int NUM = 20000; + + @Result public float[] outArray = new float[NUM]; + public Shape[] inShapeArray = new Shape[NUM]; + + static abstract class Shape { + + abstract public float getArea(); + } + + static class Circle extends Shape { + + private float radius; + + Circle(float r) { + radius = r; + } + + @Override + public float getArea() { + return (float) (Math.PI * radius * radius); + } + } + + static class Square extends Shape { + + private float len; + + Square(float _len) { + len = _len; + } + + @Override + public float getArea() { + return len * len; + } + } + + static class Triangle extends Shape { + + private float base; + private float height; + + Triangle(float base, float height) { + this.base = base; + this.height = height; + } + + @Override + public float getArea() { + return (base * height / 2.0f); + } + } + + static class Rectangle extends Shape { + + private float base; + private float height; + + Rectangle(float base, float height) { + this.base = base; + this.height = height; + } + + @Override + public float getArea() { + return (base * height); + } + } + + Shape createShape(int kind, int size) { + switch (kind) { + case 0: + return new Circle(size); + case 1: + return new Square(size); + case 2: + return new Triangle(size, size + 1); + case 3: + return new Rectangle(size, size + 1); + default: + return null; + } + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCallTest.java --- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCallTest.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCallTest.java Mon May 19 17:21:30 2014 -0700 @@ -23,66 +23,23 @@ package com.oracle.graal.compiler.hsail.test.lambda; -import static com.oracle.graal.debug.Debug.*; - -import com.oracle.graal.compiler.hsail.test.infra.GraalKernelTester; -import com.oracle.graal.debug.*; - import org.junit.Test; /** - * Tests a true virtual method call. + * Tests a true virtual method call with 2 targets. */ -public class VirtualCallTest extends GraalKernelTester { - - static final int NUM = 20; - - static abstract class Shape { - - abstract public float getArea(); - } +public class VirtualCallTest extends VirtualCallBase { - static class Circle extends Shape { - - private float radius; - - Circle(float r) { - radius = r; - } - - @Override - public float getArea() { - return (float) (Math.PI * radius * radius); + void setupArrays() { + for (int i = 0; i < NUM; i++) { + outArray[i] = -i; + int kind = i % 3 == 0 ? 0 : 1; + inShapeArray[i] = createShape(kind, i + 1); } } - static class Square extends Shape { - - private float len; - - Square(float _len) { - len = _len; - } - - @Override - public float getArea() { - return len * len; - } - } - - @Result public float[] outArray = new float[NUM]; - public Shape[] inShapeArray = new Shape[NUM]; - - void setupArrays() { - for (int i = 0; i < NUM; i++) { - if (i % 2 == 0) - inShapeArray[i] = new Circle(i + 1); - else - inShapeArray[i] = new Square(i + 1); - outArray[i] = -i; - } - } - + // although runTest is the same in each class derived from VirtualCallBase + // we duplicate the logic in each derived test so as to have different lambda call sites @Override public void runTest() { setupArrays(); @@ -93,19 +50,19 @@ }); } - // graal says not inlining getArea():float (0 bytes): no type profile exists - @Test(expected = com.oracle.graal.compiler.common.GraalInternalError.class) - public void test() { - try (DebugConfigScope s = disableIntercept()) { - testGeneratedHsail(); - } + @Override + protected boolean supportsRequiredCapabilities() { + return typeProfileWidthAtLeast(2); } - @Test(expected = com.oracle.graal.compiler.common.GraalInternalError.class) + @Test + public void test() { + testGeneratedHsail(); + } + + @Test public void testUsingLambdaMethod() { - try (DebugConfigScope s = disableIntercept()) { - testGeneratedHsailUsingLambdaMethod(); - } + testGeneratedHsailUsingLambdaMethod(); } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java --- a/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java Mon May 19 17:21:30 2014 -0700 @@ -404,6 +404,16 @@ } @Override + public Value emitMulHigh(Value a, Value b) { + throw GraalInternalError.unimplemented(); + } + + @Override + public Value emitUMulHigh(Value a, Value b) { + throw GraalInternalError.unimplemented(); + } + + @Override public Value emitDiv(Value a, Value b, LIRFrameState state) { Variable result = newVariable(a.getKind()); switch (a.getKind()) { @@ -828,16 +838,6 @@ * series of cascading compare and branch instructions. This is currently the recommended way of * generating performant HSAIL code for switch constructs. * - * In Java bytecode the keys for switch statements are always ints. - * - * The x86 backend also adds support for handling keys of type long or Object but these two - * special cases are for handling the TypeSwitchNode, which is a node that the JVM produces for - * handling operations related to method dispatch. We haven't yet added support for the - * TypeSwitchNode, so for the time being we have added a check to ensure that the keys are of - * type int. This also allows us to flag any test cases/execution paths that may trigger the - * creation of a TypeSwitchNode which we don't support yet. - * - * * @param strategy the strategy used for this switch. * @param keyTargets array of branch targets for each of the cases. * @param defaultTarget the branch target for the default case. @@ -845,12 +845,16 @@ */ @Override public void emitStrategySwitch(SwitchStrategy strategy, Variable key, LabelRef[] keyTargets, LabelRef defaultTarget) { - if ((key.getKind() == Kind.Int) || (key.getKind() == Kind.Long)) { - // Append the LIR instruction for generating compare and branch instructions. - append(new StrategySwitchOp(strategy, keyTargets, defaultTarget, key)); - } else { - // Throw an exception if the keys aren't ints. - throw GraalInternalError.unimplemented("Switch statements are only supported for keys of type int or long, not " + key.getKind()); + switch (key.getKind()) { + case Int: + case Long: + case Object: + // Append the LIR instruction for generating compare and branch instructions. + append(new StrategySwitchOp(strategy, keyTargets, defaultTarget, key)); + break; + default: + // Throw an exception if the key kind is anything else. + throw GraalInternalError.unimplemented("Switch statements not supported for keys of type " + key.getKind()); } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java --- a/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java Mon May 19 17:21:30 2014 -0700 @@ -485,6 +485,16 @@ } @Override + public Value emitMulHigh(Value a, Value b) { + throw GraalInternalError.unimplemented(); + } + + @Override + public Value emitUMulHigh(Value a, Value b) { + throw GraalInternalError.unimplemented(); + } + + @Override public Value emitDiv(Value a, Value b, LIRFrameState state) { Variable result = newVariable(a.getKind()); switch (a.getKind()) { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java --- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java Mon May 19 17:21:30 2014 -0700 @@ -580,6 +580,16 @@ } @Override + public Value emitMulHigh(Value a, Value b) { + throw GraalInternalError.unimplemented(); + } + + @Override + public Value emitUMulHigh(Value a, Value b) { + throw GraalInternalError.unimplemented(); + } + + @Override public Value emitDiv(Value a, Value b, LIRFrameState state) { Variable result = newVariable(a.getKind()); switch (a.getKind().getStackKind()) { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java Mon May 19 17:21:30 2014 -0700 @@ -30,6 +30,8 @@ import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.phases.*; import com.oracle.graal.phases.common.*; import com.oracle.graal.phases.tiers.*; @@ -137,6 +139,59 @@ return 1; } + @Test + public void test6() { + testCombinedIf("test6Snippet", 3); + test("test6Snippet", new int[]{0}); + } + + public static int test6Snippet(int[] a) { + int i = a[0]; + if (i >= 0 && i < a.length) { + return a[i]; + } + return 1; + } + + @Test + public void test7() { + testCombinedIf("test7Snippet", 1); + test("test7Snippet", -1); + } + + public static int test7Snippet(int v) { + if (v >= 0 && v < 1024) { + return v + 1; + } + return v - 1; + } + + @Test + public void test8() { + testCombinedIf("test8Snippet", 1); + test("test8Snippet", -1); + } + + public static int test8Snippet(int v) { + if (v >= 0 && v <= 1024) { + return v + 1; + } + return v - 1; + } + + private void testCombinedIf(String snippet, int count) { + StructuredGraph graph = parse(snippet); + PhaseContext context = new PhaseContext(getProviders(), new Assumptions(false)); + new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, context); + new FloatingReadPhase().apply(graph); + MidTierContext midContext = new MidTierContext(getProviders(), new Assumptions(false), getCodeCache().getTarget(), OptimisticOptimizations.ALL, graph.method().getProfilingInfo(), null); + new GuardLoweringPhase().apply(graph, midContext); + new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext); + new ValueAnchorCleanupPhase().apply(graph); + new CanonicalizerPhase(true).apply(graph, context); + assertDeepEquals(count, graph.getNodes().filter(IfNode.class).count()); + } + private void test(String snippet) { StructuredGraph graph = parse(snippet); ParameterNode param = graph.getNodes(ParameterNode.class).iterator().next(); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchGenerator.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchGenerator.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchGenerator.java Mon May 19 17:21:30 2014 -0700 @@ -32,5 +32,10 @@ * @returns null if the match can't be generated or a {@link ComplexMatchResult} that can be * evaluated during LIR generation to produce the final LIR value. */ - ComplexMatchResult match(NodeLIRBuilder gen); + ComplexMatchResult match(NodeLIRBuilder gen, Object... args); + + /** + * @return a descriptive name meaningful to the user. + */ + String getName(); } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java Mon May 19 17:21:30 2014 -0700 @@ -22,7 +22,6 @@ */ package com.oracle.graal.compiler.match; -import com.oracle.graal.compiler.common.*; import com.oracle.graal.debug.*; import com.oracle.graal.graph.Node.Verbosity; import com.oracle.graal.graph.*; @@ -197,36 +196,6 @@ return result; } - /** - * Convert a list of field names into {@link com.oracle.graal.graph.NodeClass.Position} objects - * that can be used to read them during a match. The names should already have been confirmed to - * exist in the type. - * - * @param theClass - * @param names - * @return an array of Position objects corresponding to the named fields. - */ - public static NodeClass.Position[] findPositions(Class theClass, String[] names) { - NodeClass.Position[] result = new NodeClass.Position[names.length]; - NodeClass nodeClass = NodeClass.get(theClass); - for (int i = 0; i < names.length; i++) { - for (NodeClass.Position position : nodeClass.getFirstLevelInputPositions()) { - String name = nodeClass.getName(position); - if (name.endsWith("#NDF")) { - name = name.substring(0, name.length() - 4); - } - if (name.equals(names[i])) { - result[i] = position; - break; - } - } - if (result[i] == null) { - throw new GraalInternalError("unknown field \"%s\" in class %s", names[i], theClass); - } - } - return result; - } - private Result matchUsage(ValueNode node, MatchContext context, boolean atRoot) { Result result = matchType(node); if (result != Result.OK) { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java Mon May 19 17:21:30 2014 -0700 @@ -331,7 +331,8 @@ List requiredPackages = new ArrayList<>(); /** - * The java.lang.reflect.Method for invoking a method based MatchRule. + * The mapping between elements with MatchRules and the wrapper class used invoke the code + * generation after the match. */ private Map invokers = new LinkedHashMap<>(); @@ -455,7 +456,7 @@ } String generatePositionDeclaration() { - return String.format("private static final NodeClass.Position[] %s_positions = MatchPattern.findPositions(%s.class, new String[]{\"%s\"});", nodeType.nodeClass, nodeType.nodeClass, + return String.format("NodeClass.Position[] %s_positions = MatchRuleRegistry.findPositions(lookup, %s.class, new String[]{\"%s\"});", nodeType.nodeClass, nodeType.nodeClass, String.join("\", \"", nodeType.inputs)); } } @@ -486,9 +487,7 @@ out.println("package " + pkg + ";"); out.println(""); out.println("import java.util.*;"); - out.println("import java.lang.reflect.*;"); out.println("import " + MatchStatementSet.class.getPackage().getName() + ".*;"); - out.println("import " + GraalInternalError.class.getName() + ";"); out.println("import " + NodeLIRBuilder.class.getName() + ";"); out.println("import " + NodeClass.class.getName() + ";"); for (String p : requiredPackages) { @@ -498,64 +497,67 @@ out.println("public class " + matchStatementClassName + " implements " + MatchStatementSet.class.getSimpleName() + " {"); out.println(); - out.println(" private static Method lookupMethod(Class theClass, String name, Class... args) {"); - out.println(" try {"); - out.println(" return theClass.getDeclaredMethod(name, args);"); - out.println(" } catch (Exception e) {"); - out.println(" throw new GraalInternalError(e);"); - out.println(" }"); - out.println(" }"); - out.println(); - // Generate declarations for the reflective invocation of the code generation methods. + // Generate declarations for the wrapper class to invoke the code generation methods. for (MethodInvokerItem invoker : invokers.values()) { StringBuilder args = new StringBuilder(); StringBuilder types = new StringBuilder(); int count = invoker.fields.size(); + int index = 0; for (VariableElement arg : invoker.fields) { args.append('"'); args.append(arg.getSimpleName()); args.append('"'); - types.append(fullClassName(typeUtils.asElement(arg.asType()))); - types.append(".class"); + types.append(String.format("(%s) args[%s]", fullClassName(typeUtils.asElement(arg.asType())), index++)); if (count-- > 1) { args.append(", "); types.append(", "); } } out.printf(" private static final String[] %s = new String[] {%s};\n", invoker.argumentsListName(), args); - out.printf(" private static final Method %s = lookupMethod(%s.class, \"%s\", %s);\n", invoker.reflectiveMethodName(), invoker.nodeLIRBuilderClass, invoker.methodName, types); + out.printf(" private static final class %s implements MatchGenerator {\n", invoker.wrapperClass()); + out.printf(" static MatchGenerator instance = new %s();\n", invoker.wrapperClass()); + out.printf(" public ComplexMatchResult match(NodeLIRBuilder builder, Object...args) {\n"); + out.printf(" return ((%s) builder).%s(%s);\n", invoker.nodeLIRBuilderClass, invoker.methodName, types); + out.printf(" }\n"); + out.printf(" public String getName() {\n"); + out.printf(" return \"%s\";\n", invoker.methodName); + out.printf(" }\n"); + out.printf(" }\n"); out.println(); } - for (String positionDeclaration : info.positionDeclarations) { - out.println(" " + positionDeclaration); - } - out.println(); - String desc = MatchStatement.class.getSimpleName(); - out.println(" // CheckStyle: stop line length check"); - out.println(" private static final List<" + desc + "> statements = Collections.unmodifiableList(Arrays.asList("); - - int i = 0; - for (MatchRuleItem matchRule : info.matchRules) { - String comma = i == info.matchRules.size() - 1 ? "" : ","; - out.printf(" %s%s\n", matchRule.ruleBuilder(), comma); - i++; - } - out.println(" ));"); - out.println(" // CheckStyle: resume line length check"); - out.println(); out.println(" public Class forClass() {"); out.println(" return " + topDeclaringClass + ".class;"); out.println(" }"); out.println(); out.println(" @Override"); - out.println(" public List<" + desc + "> statements() {"); + out.println(" public List<" + desc + "> statements(MatchRuleRegistry.NodeClassLookup lookup) {"); + + for (String positionDeclaration : info.positionDeclarations) { + out.println(" " + positionDeclaration); + } + out.println(); + + out.println(" // CheckStyle: stop line length check"); + out.println(" List<" + desc + "> statements = Collections.unmodifiableList(Arrays.asList("); + + int i = 0; + for (MatchRuleItem matchRule : info.matchRules) { + String comma = i == info.matchRules.size() - 1 ? "" : ","; + out.printf(" %s%s\n", matchRule.ruleBuilder(), comma); + i++; + } + out.println(" ));"); + out.println(" // CheckStyle: resume line length check"); out.println(" return statements;"); out.println(" }"); + + out.println(); + out.println("}"); } @@ -606,13 +608,12 @@ * @return a string which will construct the MatchStatement instance to match this pattern. */ public String ruleBuilder() { - return String.format("new MatchStatement(\"%s\", %s, %s, %s)", invoker.name, matchPattern, invoker.reflectiveMethodName(), invoker.argumentsListName()); + return String.format("new MatchStatement(\"%s\", %s, %s.instance, %s)", invoker.name, matchPattern, invoker.wrapperClass(), invoker.argumentsListName()); } } /** - * Used to generate the declarations needed for reflective invocation of the code generation - * method. + * Used to generate the wrapper class to invoke the code generation method. */ static class MethodInvokerItem { final String name; @@ -627,8 +628,8 @@ this.fields = fields; } - String reflectiveMethodName() { - return methodName + "_invoke"; + String wrapperClass() { + return "MatchGenerator_" + methodName; } String argumentsListName() { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRuleRegistry.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRuleRegistry.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRuleRegistry.java Mon May 19 17:21:30 2014 -0700 @@ -27,13 +27,61 @@ import java.util.*; import java.util.Map.Entry; +import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.gen.*; import com.oracle.graal.debug.*; import com.oracle.graal.debug.Debug.Scope; +import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; public class MatchRuleRegistry { + /** + * Helper interface for mapping between Class and NodeClass. In static compilation environments, + * the current NodeClass might not be the same NodeClass used in the target so this provides a + * level of indirection. + */ + public static interface NodeClassLookup { + NodeClass get(Class theClass); + + } + + static class DefaultNodeClassLookup implements NodeClassLookup { + public NodeClass get(Class theClass) { + return NodeClass.get(theClass); + } + } + + /** + * Convert a list of field names into {@link com.oracle.graal.graph.NodeClass.Position} objects + * that can be used to read them during a match. The names should already have been confirmed to + * exist in the type. + * + * @param theClass + * @param names + * @return an array of Position objects corresponding to the named fields. + */ + public static NodeClass.Position[] findPositions(NodeClassLookup lookup, Class theClass, String[] names) { + NodeClass.Position[] result = new NodeClass.Position[names.length]; + NodeClass nodeClass = lookup.get(theClass); + for (int i = 0; i < names.length; i++) { + for (NodeClass.Position position : nodeClass.getFirstLevelInputPositions()) { + String name = nodeClass.getName(position); + if (name.endsWith("#NDF")) { + name = name.substring(0, name.length() - 4); + } + if (name.equals(names[i])) { + result[i] = position; + break; + } + } + if (result[i] == null) { + throw new GraalInternalError("unknown field \"%s\" in class %s", names[i], theClass); + } + } + return result; + } + private static final HashMap, Map, List>> registry = new HashMap<>(); /** @@ -46,10 +94,11 @@ Map, List> result = registry.get(theClass); if (result == null) { + NodeClassLookup lookup = new DefaultNodeClassLookup(); HashMap, List> localRules = new HashMap<>(); ServiceLoader sl = ServiceLoader.loadInstalled(MatchStatementSet.class); for (MatchStatementSet rules : sl) { - localRules.put(rules.forClass(), rules.statements()); + localRules.put(rules.forClass(), rules.statements(lookup)); } // Walk the class hierarchy collecting lists and merge them together. The subclass diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatement.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatement.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatement.java Mon May 19 17:21:30 2014 -0700 @@ -24,11 +24,9 @@ import static com.oracle.graal.compiler.GraalDebugConfig.*; -import java.lang.reflect.*; import java.util.*; import com.oracle.graal.api.meta.*; -import com.oracle.graal.compiler.common.*; import com.oracle.graal.compiler.gen.*; import com.oracle.graal.compiler.match.MatchPattern.MatchResultCode; import com.oracle.graal.compiler.match.MatchPattern.Result; @@ -59,14 +57,14 @@ /** * The method in the {@link NodeLIRBuilder} subclass that will actually do the code emission. */ - private Method generatorMethod; + private MatchGenerator generatorMethod; /** * The name of arguments in the order they are expected to be passed to the generator method. */ private String[] arguments; - public MatchStatement(String name, MatchPattern pattern, Method generator, String[] arguments) { + public MatchStatement(String name, MatchPattern pattern, MatchGenerator generator, String[] arguments) { this.name = name; this.pattern = pattern; this.generatorMethod = generator; @@ -93,23 +91,19 @@ MatchContext context = new MatchContext(builder, this, index, node, nodes); result = pattern.matchUsage(node, context); if (result == Result.OK) { - try { - // Invoke the generator method and set the result if it's non null. - ComplexMatchResult value = (ComplexMatchResult) generatorMethod.invoke(builder, buildArgList(context)); - if (value != null) { - context.setResult(value); - MatchStatementSuccess.increment(); - Debug.metric("MatchStatement[%s]", getName()).increment(); - return true; - } - // The pattern matched but some other code generation constraint disallowed code - // generation for the pattern. - if (LogVerbose.getValue()) { - Debug.log("while matching %s|%s %s %s returned null", context.getRoot().toString(Verbosity.Id), context.getRoot().getClass().getSimpleName(), getName(), generatorMethod.getName()); - Debug.log("with nodes %s", formatMatch(node)); - } - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - throw new GraalInternalError(e); + // Invoke the generator method and set the result if it's non null. + ComplexMatchResult value = generatorMethod.match(builder, buildArgList(context)); + if (value != null) { + context.setResult(value); + MatchStatementSuccess.increment(); + Debug.metric("MatchStatement[%s]", getName()).increment(); + return true; + } + // The pattern matched but some other code generation constraint disallowed code + // generation for the pattern. + if (LogVerbose.getValue()) { + Debug.log("while matching %s|%s %s %s returned null", context.getRoot().toString(Verbosity.Id), context.getRoot().getClass().getSimpleName(), getName(), generatorMethod.getName()); + Debug.log("with nodes %s", formatMatch(node)); } } else { if (LogVerbose.getValue() && result.code != MatchResultCode.WRONG_CLASS) { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatementSet.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatementSet.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatementSet.java Mon May 19 17:21:30 2014 -0700 @@ -36,5 +36,5 @@ /** * @return the {@link MatchStatement}s available for this {@link NodeLIRBuilder} subclass. */ - public List statements(); + public List statements(MatchRuleRegistry.NodeClassLookup lookup); } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.debug/src/com/oracle/graal/debug/AnsiColor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/AnsiColor.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.debug; + +/** + * Ansi terminal color escape codes. + */ +public final class AnsiColor { + /** Foreground black */ + public static final String BLACK = "\u001b[30m"; + /** Foreground red */ + public static final String RED = "\u001b[31m"; + /** Foreground green */ + public static final String GREEN = "\u001b[32m"; + /** Foreground yellow */ + public static final String YELLOW = "\u001b[33m"; + /** Foreground blue */ + public static final String BLUE = "\u001b[34m"; + /** Foreground magenta */ + public static final String MAGENTA = "\u001b[35m"; + /** Foreground cyan */ + public static final String CYAN = "\u001b[36m"; + /** Foreground white */ + public static final String WHITE = "\u001b[37m"; + + /** Foreground bold black */ + public static final String BOLD_BLACK = "\u001b[30;1m"; + /** Foreground bold red */ + public static final String BOLD_RED = "\u001b[31;1m"; + /** Foreground bold green */ + public static final String BOLD_GREEN = "\u001b[32;1m"; + /** Foreground bold yellow */ + public static final String BOLD_YELLOW = "\u001b[33;1m"; + /** Foreground bold blue */ + public static final String BOLD_BLUE = "\u001b[34;1m"; + /** Foreground bold magenta */ + public static final String BOLD_MAGENTA = "\u001b[35;1m"; + /** Foreground bold cyan */ + public static final String BOLD_CYAN = "\u001b[36;1m"; + /** Foreground bold white */ + public static final String BOLD_WHITE = "\u001b[37;1m"; + + /** Background black */ + public static final String BG_BLACK = "\u001b[40m"; + /** Background red */ + public static final String BG_RED = "\u001b[41m"; + /** Background green */ + public static final String BG_GREEN = "\u001b[42m"; + /** Background yellow */ + public static final String BG_YELLOW = "\u001b[43m"; + /** Background blue */ + public static final String BG_BLUE = "\u001b[44m"; + /** Background magenta */ + public static final String BG_MAGENTA = "\u001b[45m"; + /** Background cyan */ + public static final String BG_CYAN = "\u001b[46m"; + /** Background white */ + public static final String BG_WHITE = "\u001b[47m"; + + /** Reset */ + public static final String RESET = "\u001b[0m"; + /** Underline */ + public static final String UNDERLINED = "\u001b[4m"; + + /** Prevent instantiation */ + private AnsiColor() { + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java --- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java Mon May 19 17:21:30 2014 -0700 @@ -741,7 +741,7 @@ private static DebugMetric createMetric(String format, Object arg1, Object arg2) { String name = formatDebugName(format, arg1, arg2); - boolean conditional = enabledMetrics != null && enabledMetrics.contains(name); + boolean conditional = enabledMetrics == null || !enabledMetrics.contains(name); return new MetricImpl(name, conditional); } @@ -981,7 +981,7 @@ private static DebugTimer createTimer(String format, Object arg1, Object arg2) { String name = formatDebugName(format, arg1, arg2); - boolean conditional = enabledTimers != null && enabledTimers.contains(name); + boolean conditional = enabledTimers == null || !enabledTimers.contains(name); return new TimerImpl(name, conditional); } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java --- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java Mon May 19 17:21:30 2014 -0700 @@ -197,4 +197,9 @@ public boolean contains(Node node) { return isMarked(node); } + + @Override + public String toString() { + return snapshot().toString(); + } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java --- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java Mon May 19 17:21:30 2014 -0700 @@ -32,6 +32,9 @@ import java.lang.reflect.*; import java.util.*; +import java.util.Map.Entry; +import java.util.function.*; +import java.util.stream.*; import com.amd.okra.*; import com.oracle.graal.api.code.*; @@ -69,9 +72,11 @@ import com.oracle.graal.nodes.extended.*; import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.virtual.*; import com.oracle.graal.options.*; import com.oracle.graal.phases.*; import com.oracle.graal.phases.tiers.*; +import com.oracle.graal.virtual.nodes.*; /** * HSAIL specific backend. @@ -266,7 +271,7 @@ StructuredGraph hostGraph = hsailCode.getHostGraph(); if (hostGraph != null) { // TODO get rid of the unverified entry point in the host code - try (Scope ds = Debug.scope("GeneratingHostGraph")) { + try (Scope ds = Debug.scope("GeneratingHostGraph", new DebugDumpScope("HostGraph"))) { HotSpotBackend hostBackend = getRuntime().getHostBackend(); JavaType[] parameterTypes = new JavaType[hostGraph.getNodes(ParameterNode.class).count()]; Debug.log("Param count: %d", parameterTypes.length); @@ -726,6 +731,8 @@ asm.emitString(spillsegStringFinal, spillsegDeclarationPosition); // Emit the epilogue. + HSAILHotSpotLIRGenerationResult lirGenRes = ((HSAILCompilationResultBuilder) crb).lirGenRes; + int numSRegs = 0; int numDRegs = 0; int numStackSlotBytes = 0; @@ -736,31 +743,39 @@ Set infoUsedRegs = new TreeSet<>(); Set infoUsedStackSlots = new HashSet<>(); List infoList = crb.compilationResult.getInfopoints(); + Queue workList = new LinkedList<>(); for (Infopoint info : infoList) { BytecodeFrame frame = info.debugInfo.frame(); while (frame != null) { - for (int i = 0; i < frame.numLocals + frame.numStack; i++) { - Value val = frame.values[i]; - if (isLegal(val)) { - if (isRegister(val)) { - Register reg = asRegister(val); - infoUsedRegs.add(reg); - if (hsailRegConfig.isAllocatableSReg(reg)) { - numSRegs = Math.max(numSRegs, reg.encoding + 1); - } else if (hsailRegConfig.isAllocatableDReg(reg)) { - numDRegs = Math.max(numDRegs, reg.encoding + 1); - } - } else if (isStackSlot(val)) { - StackSlot slot = asStackSlot(val); - Kind slotKind = slot.getKind(); - int slotSizeBytes = (slotKind.isObject() ? 8 : slotKind.getByteCount()); - int slotOffsetMax = HSAIL.getStackOffsetStart(slot, slotSizeBytes * 8) + slotSizeBytes; - numStackSlotBytes = Math.max(numStackSlotBytes, slotOffsetMax); - infoUsedStackSlots.add(slot); + workList.add(frame.values); + frame = frame.caller(); + } + } + while (!workList.isEmpty()) { + Value[] values = workList.poll(); + for (Value val : values) { + if (isLegal(val)) { + if (isRegister(val)) { + Register reg = asRegister(val); + infoUsedRegs.add(reg); + if (hsailRegConfig.isAllocatableSReg(reg)) { + numSRegs = Math.max(numSRegs, reg.encoding + 1); + } else if (hsailRegConfig.isAllocatableDReg(reg)) { + numDRegs = Math.max(numDRegs, reg.encoding + 1); } + } else if (isStackSlot(val)) { + StackSlot slot = asStackSlot(val); + Kind slotKind = slot.getKind(); + int slotSizeBytes = (slotKind.isObject() ? 8 : slotKind.getByteCount()); + int slotOffsetMax = HSAIL.getStackOffsetStart(slot, slotSizeBytes * 8) + slotSizeBytes; + numStackSlotBytes = Math.max(numStackSlotBytes, slotOffsetMax); + infoUsedStackSlots.add(slot); + } else if (isVirtualObject(val)) { + workList.add(((VirtualObject) val).getValues()); + } else { + assert isConstant(val) : "Unsupported value: " + val; } } - frame = frame.caller(); } } @@ -923,8 +938,9 @@ asm.emitString0("}; \n"); ExternalCompilationResult compilationResult = (ExternalCompilationResult) crb.compilationResult; - HSAILHotSpotLIRGenerationResult lirGenRes = ((HSAILCompilationResultBuilder) crb).lirGenRes; - compilationResult.setHostGraph(prepareHostGraph(method, lirGenRes.getDeopts(), getProviders(), config, numSRegs, numDRegs)); + if (useHSAILDeoptimization) { + compilationResult.setHostGraph(prepareHostGraph(method, lirGenRes.getDeopts(), getProviders(), config, numSRegs, numDRegs)); + } } private static class OopMapArrayBuilder { @@ -1090,26 +1106,53 @@ } private static FrameState createFrameState(BytecodeFrame lowLevelFrame, ParameterNode hsailFrame, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs, int numDRegs) { + return createFrameState(lowLevelFrame, hsailFrame, providers, config, numSRegs, numDRegs, new HashMap()); + } + + private static FrameState createFrameState(BytecodeFrame lowLevelFrame, ParameterNode hsailFrame, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs, int numDRegs, + Map virtualObjects) { + FrameState outterFrameState = null; + if (lowLevelFrame.caller() != null) { + outterFrameState = createFrameState(lowLevelFrame.caller(), hsailFrame, providers, config, numSRegs, numDRegs, virtualObjects); + } StructuredGraph hostGraph = hsailFrame.graph(); + Function lirValueToHirNode = v -> getNodeForValueFromFrame(v, hsailFrame, hostGraph, providers, config, numSRegs, numDRegs, virtualObjects); ValueNode[] locals = new ValueNode[lowLevelFrame.numLocals]; for (int i = 0; i < lowLevelFrame.numLocals; i++) { - locals[i] = getNodeForValueFromFrame(lowLevelFrame.getLocalValue(i), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs); + locals[i] = lirValueToHirNode.apply(lowLevelFrame.getLocalValue(i)); } List stack = new ArrayList<>(lowLevelFrame.numStack); for (int i = 0; i < lowLevelFrame.numStack; i++) { - stack.add(getNodeForValueFromFrame(lowLevelFrame.getStackValue(i), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs)); + stack.add(lirValueToHirNode.apply(lowLevelFrame.getStackValue(i))); } ValueNode[] locks = new ValueNode[lowLevelFrame.numLocks]; MonitorIdNode[] monitorIds = new MonitorIdNode[lowLevelFrame.numLocks]; for (int i = 0; i < lowLevelFrame.numLocks; i++) { HotSpotMonitorValue lockValue = (HotSpotMonitorValue) lowLevelFrame.getLockValue(i); - locks[i] = getNodeForValueFromFrame(lockValue, hsailFrame, hostGraph, providers, config, numSRegs, numDRegs); + locks[i] = lirValueToHirNode.apply(lockValue); monitorIds[i] = getMonitorIdForHotSpotMonitorValueFromFrame(lockValue, hsailFrame, hostGraph); } FrameState frameState = hostGraph.add(new FrameState(lowLevelFrame.getMethod(), lowLevelFrame.getBCI(), locals, stack, locks, monitorIds, lowLevelFrame.rethrowException, false)); - if (lowLevelFrame.caller() != null) { - frameState.setOuterFrameState(createFrameState(lowLevelFrame.caller(), hsailFrame, providers, config, numSRegs, numDRegs)); + if (outterFrameState != null) { + frameState.setOuterFrameState(outterFrameState); } + Map virtualObjectsCopy; + // TODO this could be implemented more efficiently with a mark into the map + // unfortunately LinkedHashMap doesn't seem to provide that. + List virtualStates = new ArrayList<>(virtualObjects.size()); + do { + virtualObjectsCopy = new HashMap<>(virtualObjects); + virtualStates.clear(); + for (Entry entry : virtualObjectsCopy.entrySet()) { + VirtualObject virtualObject = entry.getKey(); + VirtualObjectNode virtualObjectNode = entry.getValue(); + List fieldValues = Arrays.stream(virtualObject.getValues()).map(lirValueToHirNode).collect(Collectors.toList()); + virtualStates.add(new VirtualObjectState(virtualObjectNode, fieldValues)); + } + // New virtual objects may have been discovered while processing the previous set. + // Wait until a fixed point is reached + } while (virtualObjectsCopy.size() < virtualObjects.size()); + virtualStates.forEach(vos -> frameState.addVirtualObjectMapping(hostGraph.unique(vos))); return frameState; } @@ -1122,18 +1165,18 @@ } private static ValueNode getNodeForValueFromFrame(Value localValue, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs, - int numDRegs) { + int numDRegs, Map virtualObjects) { ValueNode valueNode; if (localValue instanceof Constant) { valueNode = ConstantNode.forConstant((Constant) localValue, providers.getMetaAccess(), hostGraph); } else if (localValue instanceof VirtualObject) { - throw GraalInternalError.unimplemented(); + valueNode = getNodeForVirtualObjectFromFrame((VirtualObject) localValue, virtualObjects, hostGraph); } else if (localValue instanceof StackSlot) { StackSlot slot = (StackSlot) localValue; valueNode = getNodeForStackSlotFromFrame(slot, localValue.getKind(), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs); } else if (localValue instanceof HotSpotMonitorValue) { HotSpotMonitorValue hotSpotMonitorValue = (HotSpotMonitorValue) localValue; - return getNodeForValueFromFrame(hotSpotMonitorValue.getOwner(), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs); + return getNodeForValueFromFrame(hotSpotMonitorValue.getOwner(), hsailFrame, hostGraph, providers, config, numSRegs, numDRegs, virtualObjects); } else if (localValue instanceof RegisterValue) { RegisterValue registerValue = (RegisterValue) localValue; int regNumber = registerValue.getRegister().number; @@ -1146,6 +1189,16 @@ return valueNode; } + private static ValueNode getNodeForVirtualObjectFromFrame(VirtualObject virtualObject, Map virtualObjects, StructuredGraph hostGraph) { + return virtualObjects.computeIfAbsent(virtualObject, vo -> { + if (vo.getType().isArray()) { + return hostGraph.add(new VirtualArrayNode(vo.getType().getComponentType(), vo.getValues().length)); + } else { + return hostGraph.add(new VirtualInstanceNode(vo.getType(), true)); + } + }); + } + private static ValueNode getNodeForRegisterFromFrame(int regNumber, Kind valueKind, ParameterNode hsailFrame, StructuredGraph hostGraph, HotSpotProviders providers, HotSpotVMConfig config, int numSRegs) { ValueNode valueNode; diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot.jfr/src/com/oracle/graal/hotspot/jfr/events/JFREventProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot.jfr/src/com/oracle/graal/hotspot/jfr/events/JFREventProvider.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.hotspot.jfr.events; + +import java.net.*; + +import com.oracle.graal.api.runtime.*; +import com.oracle.graal.hotspot.events.*; +import com.oracle.jrockit.jfr.*; + +/** + * A JFR implementation for {@link EventProvider}. This implementation is used when Flight Recorder + * is turned on. + */ +@ServiceProvider(EventProvider.class) +public final class JFREventProvider implements EventProvider { + + @SuppressWarnings("deprecation") private final Producer producer; + + @SuppressWarnings("deprecation") + public JFREventProvider() { + try { + /* + * The "HotSpot JVM" producer is a native producer and we cannot use it. So we create + * our own. This has the downside that Mission Control is confused and doesn't show + * Graal's events in the "Code" tab. There are plans to revise the JFR code for JDK 9. + */ + producer = new Producer("HotSpot JVM", "Oracle Hotspot JVM", "http://www.oracle.com/hotspot/jvm/"); + producer.register(); + } catch (URISyntaxException e) { + throw new InternalError(e); + } + + // Register event classes with Producer. + for (Class c : JFREventProvider.class.getDeclaredClasses()) { + if (c.isAnnotationPresent(EventDefinition.class)) { + assert com.oracle.jrockit.jfr.InstantEvent.class.isAssignableFrom(c) : c; + registerEvent(c); + } + } + } + + /** + * Register an event class with the {@link Producer}. + * + * @param c event class + * @return the {@link EventToken event token} + */ + @SuppressWarnings({"deprecation", "javadoc", "unchecked"}) + private final EventToken registerEvent(Class c) { + try { + return producer.addEvent((Class) c); + } catch (InvalidEventDefinitionException | InvalidValueException e) { + throw new InternalError(e); + } + } + + public CompilationEvent newCompilationEvent() { + return new JFRCompilationEvent(); + } + + /** + * A JFR compilation event. + * + *

+ * See: event {@code Compilation} in {@code src/share/vm/trace/trace.xml} + */ + @SuppressWarnings("deprecation") + @EventDefinition(name = "Compilation", path = "vm/compiler/compilation") + public static class JFRCompilationEvent extends com.oracle.jrockit.jfr.DurationEvent implements CompilationEvent { + + /* + * FIXME method should be a Method* but we can't express that in Java. + */ + @ValueDefinition(name = "Java Method") public String method; + @ValueDefinition(name = "Compilation ID", relationKey = "COMP_ID") public int compileId; + @ValueDefinition(name = "Compilation Level") public short compileLevel; + @ValueDefinition(name = "Succeeded") public boolean succeeded; + @ValueDefinition(name = "On Stack Replacement") public boolean isOsr; + @ValueDefinition(name = "Compiled Code Size", contentType = ContentType.Bytes) public int codeSize; + @ValueDefinition(name = "Inlined Code Size", contentType = ContentType.Bytes) public int inlinedBytes; + + public void setMethod(String method) { + this.method = method; + } + + public void setCompileId(int id) { + this.compileId = id; + } + + public void setCompileLevel(int compileLevel) { + this.compileLevel = (short) compileLevel; + } + + public void setSucceeded(boolean succeeded) { + this.succeeded = succeeded; + } + + public void setIsOsr(boolean isOsr) { + this.isOsr = isOsr; + } + + public void setCodeSize(int codeSize) { + this.codeSize = codeSize; + } + + public void setInlinedBytes(int inlinedBytes) { + this.inlinedBytes = inlinedBytes; + } + } + + public CompilerFailureEvent newCompilerFailureEvent() { + return new JFRCompilerFailureEvent(); + } + + /** + * A JFR compiler failure event. + * + *

+ * See: event {@code CompilerFailure} in {@code src/share/vm/trace/trace.xml} + */ + @SuppressWarnings("deprecation") + @EventDefinition(name = "Compilation Failure", path = "vm/compiler/failure") + public static class JFRCompilerFailureEvent extends com.oracle.jrockit.jfr.InstantEvent implements CompilerFailureEvent { + + @ValueDefinition(name = "Compilation ID", relationKey = "COMP_ID") public int compileId; + @ValueDefinition(name = "Message", description = "The failure message") public String failure; + + public void setCompileId(int id) { + this.compileId = id; + } + + public void setMessage(String message) { + this.failure = message; + } + } + +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java Mon May 19 17:21:30 2014 -0700 @@ -26,6 +26,7 @@ import java.lang.ref.*; import java.lang.reflect.*; +import com.oracle.graal.phases.common.inlining.policy.InlineEverythingPolicy; import org.junit.*; import com.oracle.graal.api.code.*; @@ -249,7 +250,7 @@ StructuredGraph graph = parse(snippet); HighTierContext highContext = new HighTierContext(getProviders(), new Assumptions(false), null, getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL); MidTierContext midContext = new MidTierContext(getProviders(), new Assumptions(false), getCodeCache().getTarget(), OptimisticOptimizations.ALL, graph.method().getProfilingInfo(), null); - new InliningPhase(new InliningPhase.InlineEverythingPolicy(), new CanonicalizerPhase(true)).apply(graph, highContext); + new InliningPhase(new InlineEverythingPolicy(), new CanonicalizerPhase(true)).apply(graph, highContext); new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.HIGH_TIER).apply(graph, highContext); new GuardLoweringPhase().apply(graph, midContext); new LoweringPhase(new CanonicalizerPhase(true), LoweringTool.StandardLoweringStage.MID_TIER).apply(graph, midContext); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Mon May 19 17:21:30 2014 -0700 @@ -40,6 +40,7 @@ import com.oracle.graal.api.code.*; import com.oracle.graal.api.code.CallingConvention.Type; import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.runtime.*; import com.oracle.graal.baseline.*; import com.oracle.graal.compiler.*; import com.oracle.graal.compiler.common.*; @@ -47,6 +48,9 @@ import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.debug.internal.*; import com.oracle.graal.hotspot.bridge.*; +import com.oracle.graal.hotspot.events.*; +import com.oracle.graal.hotspot.events.EventProvider.CompilationEvent; +import com.oracle.graal.hotspot.events.EventProvider.CompilerFailureEvent; import com.oracle.graal.hotspot.meta.*; import com.oracle.graal.hotspot.phases.*; import com.oracle.graal.java.*; @@ -103,6 +107,13 @@ private final int id; private final AtomicReference status; + /** + * The executor processing the Graal compilation queue this task was placed on. This will be + * null for blocking compilations or if compilations are scheduled as native HotSpot + * {@linkplain #ctask CompileTask}s. + */ + private final ExecutorService executor; + private StructuredGraph graph; /** @@ -116,7 +127,7 @@ * A {@link com.sun.management.ThreadMXBean} to be able to query some information about the * current compiler thread, e.g. total allocated bytes. */ - private final com.sun.management.ThreadMXBean threadMXBean = (com.sun.management.ThreadMXBean) ManagementFactory.getThreadMXBean(); + private static final com.sun.management.ThreadMXBean threadMXBean = (com.sun.management.ThreadMXBean) ManagementFactory.getThreadMXBean(); /** * The address of the native CompileTask associated with this compilation. If 0L, then this @@ -125,7 +136,8 @@ */ private final long ctask; - public CompilationTask(HotSpotBackend backend, HotSpotResolvedJavaMethod method, int entryBCI, long ctask, boolean blocking) { + public CompilationTask(ExecutorService executor, HotSpotBackend backend, HotSpotResolvedJavaMethod method, int entryBCI, long ctask, boolean blocking) { + this.executor = executor; this.backend = backend; this.method = method; this.entryBCI = entryBCI; @@ -144,6 +156,11 @@ return method; } + /** + * Returns the compilation id of this task. + * + * @return compile id + */ public int getId() { return id; } @@ -156,7 +173,7 @@ public void run() { withinEnqueue.set(Boolean.FALSE); try { - runCompilation(true); + runCompilation(); } finally { withinEnqueue.set(Boolean.TRUE); status.set(CompilationStatus.Finished); @@ -226,7 +243,14 @@ return method.getCompilationProfilingInfo(osrCompilation); } - public void runCompilation(boolean clearFromCompilationQueue) { + public void runCompilation() { + if (executor != null && executor.isShutdown()) { + // We don't want to do any unnecessary compilation if the Graal compilation + // queue has been shutdown. Note that we leave the JVM_ACC_QUEUED bit set + // for the method so that it won't be re-scheduled for compilation. + return; + } + /* * no code must be outside this try/finally because it could happen otherwise that * clearQueuedForCompilation() is not executed @@ -237,12 +261,16 @@ long previousInlinedBytecodes = InlinedBytecodes.getCurrentValue(); long previousCompilationTime = CompilationTime.getCurrentValue(); HotSpotInstalledCode installedCode = null; + final boolean isOSR = entryBCI != StructuredGraph.INVOCATION_ENTRY_BCI; + + // Log a compilation event. + EventProvider eventProvider = Graal.getRequiredCapability(EventProvider.class); + CompilationEvent compilationEvent = eventProvider.newCompilationEvent(); try (TimerCloseable a = CompilationTime.start()) { if (!tryToChangeStatus(CompilationStatus.Queued, CompilationStatus.Running)) { return; } - boolean isOSR = entryBCI != StructuredGraph.INVOCATION_ENTRY_BCI; // If there is already compiled code for this method on our level we simply return. // Graal compiles are always at the highest compile level, even in non-tiered mode so we @@ -266,6 +294,8 @@ final long allocatedBytesBefore = threadMXBean.getThreadAllocatedBytes(threadId); try (Scope s = Debug.scope("Compiling", new DebugDumpScope(String.valueOf(id), true))) { + // Begin the compilation event. + compilationEvent.begin(); if (UseBaselineCompiler.getValue() == true) { HotSpotProviders providers = backend.getProviders(); @@ -307,6 +337,9 @@ } catch (Throwable e) { throw Debug.handle(e); } finally { + // End the compilation event. + compilationEvent.end(); + filter.remove(); final boolean printAfterCompilation = PrintAfterCompilation.getValue() && !TTY.isSuppressed(); @@ -346,16 +379,37 @@ if (PrintStackTraceOnException.getValue() || ExitVMOnException.getValue()) { t.printStackTrace(TTY.cachedOut); } + + // Log a failure event. + CompilerFailureEvent event = eventProvider.newCompilerFailureEvent(); + if (event.shouldWrite()) { + event.setCompileId(getId()); + event.setMessage(t.getMessage()); + event.commit(); + } + if (ExitVMOnException.getValue()) { System.exit(-1); } } finally { - int processedBytes = (int) (InlinedBytecodes.getCurrentValue() - previousInlinedBytecodes); + final int processedBytes = (int) (InlinedBytecodes.getCurrentValue() - previousInlinedBytecodes); + + // Log a compilation event. + if (compilationEvent.shouldWrite()) { + compilationEvent.setMethod(MetaUtil.format("%H.%n(%p)", method)); + compilationEvent.setCompileId(getId()); + compilationEvent.setCompileLevel(config.compilationLevelFullOptimization); + compilationEvent.setSucceeded(true); + compilationEvent.setIsOsr(isOSR); + compilationEvent.setCodeSize(installedCode.getSize()); + compilationEvent.setInlinedBytes(processedBytes); + compilationEvent.commit(); + } + if (ctask != 0L) { unsafe.putInt(ctask + config.compileTaskNumInlinedBytecodesOffset, processedBytes); } if ((config.ciTime || config.ciTimeEach || PrintCompRate.getValue() != 0) && installedCode != null) { - long time = CompilationTime.getCurrentValue() - previousCompilationTime; TimeUnit timeUnit = CompilationTime.getTimeUnit(); long timeUnitsPerSecond = timeUnit.convert(1, TimeUnit.SECONDS); @@ -363,7 +417,7 @@ c2vm.notifyCompilationStatistics(id, method, entryBCI != INVOCATION_ENTRY_BCI, processedBytes, time, timeUnitsPerSecond, installedCode); } - if (clearFromCompilationQueue) { + if (executor != null) { assert method.isQueuedForCompilation(); method.clearQueuedForCompilation(); } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java Mon May 19 17:21:30 2014 -0700 @@ -317,7 +317,7 @@ class CTWCompilationTask extends CompilationTask { CTWCompilationTask(HotSpotBackend backend, HotSpotResolvedJavaMethod method) { - super(backend, method, INVOCATION_ENTRY_BCI, 0L, false); + super(null, backend, method, INVOCATION_ENTRY_BCI, 0L, false); } /** @@ -349,7 +349,7 @@ HotSpotBackend backend = runtime.getHostBackend(); CompilationTask task = new CTWCompilationTask(backend, method); - task.runCompilation(false); + task.runCompilation(); compileTime += (System.currentTimeMillis() - start); compiledMethodsCounter++; diff -r 8c34e2cc4add -r bdf260d8e163 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 May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Mon May 19 17:21:30 2014 -0700 @@ -43,6 +43,7 @@ import com.oracle.graal.compiler.target.*; import com.oracle.graal.graph.*; import com.oracle.graal.hotspot.bridge.*; +import com.oracle.graal.hotspot.events.*; import com.oracle.graal.hotspot.logging.*; import com.oracle.graal.hotspot.meta.*; import com.oracle.graal.options.*; @@ -274,6 +275,8 @@ } registerBackend(factory.createBackend(this, hostBackend)); } + + eventProvider = createEventProvider(); } private HotSpotBackend registerBackend(HotSpotBackend backend) { @@ -379,6 +382,22 @@ private final NodeCollectionsProvider nodeCollectionsProvider = new DefaultNodeCollectionsProvider(); + private final EventProvider eventProvider; + + private EventProvider createEventProvider() { + if (config.flightRecorder) { + ServiceLoader sl = ServiceLoader.loadInstalled(EventProvider.class); + EventProvider singleProvider = null; + for (EventProvider ep : sl) { + assert singleProvider == null : String.format("multiple %s service implementations found: %s and %s", EventProvider.class.getName(), singleProvider.getClass().getName(), + ep.getClass().getName()); + singleProvider = ep; + } + return singleProvider; + } + return new EmptyEventProvider(); + } + @SuppressWarnings("unchecked") @Override public T getCapability(Class clazz) { @@ -392,6 +411,8 @@ return (T) getHostProviders().getSnippetReflection(); } else if (clazz == MethodHandleAccessProvider.class) { return (T) getHostProviders().getMethodHandleAccess(); + } else if (clazz == EventProvider.class) { + return (T) eventProvider; } return null; } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Mon May 19 17:21:30 2014 -0700 @@ -737,6 +737,8 @@ @HotSpotVMFlag(name = "AllocatePrefetchStepSize") @Stable public int allocatePrefetchStepSize; @HotSpotVMFlag(name = "AllocatePrefetchDistance") @Stable public int allocatePrefetchDistance; + @HotSpotVMFlag(name = "FlightRecorder", optional = true) @Stable public boolean flightRecorder; + @HotSpotVMField(name = "Universe::_collectedHeap", type = "CollectedHeap*", get = HotSpotVMField.Type.VALUE) @Stable private long universeCollectedHeap; @HotSpotVMField(name = "CollectedHeap::_total_collections", type = "unsigned int", get = HotSpotVMField.Type.OFFSET) @Stable private int collectedHeapTotalCollectionsOffset; @@ -1064,6 +1066,7 @@ @HotSpotVMConstant(name = "Method::_dont_inline") @Stable public int methodFlagsDontInline; @HotSpotVMConstant(name = "Method::_hidden") @Stable public int methodFlagsHidden; @HotSpotVMConstant(name = "Method::nonvirtual_vtable_index") @Stable public int nonvirtualVtableIndex; + @HotSpotVMConstant(name = "Method::invalid_vtable_index") @Stable public int invalidVtableIndex; @HotSpotVMConstant(name = "JVM_ACC_MONITOR_MATCH") @Stable public int jvmAccMonitorMatch; @HotSpotVMConstant(name = "JVM_ACC_HAS_MONITOR_BYTECODES") @Stable public int jvmAccHasMonitorBytecodes; diff -r 8c34e2cc4add -r bdf260d8e163 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 May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java Mon May 19 17:21:30 2014 -0700 @@ -248,7 +248,7 @@ void initializeConfiguration(HotSpotVMConfig config); - long resolveMethod(long metaspaceKlass, String name, String signature); + long resolveMethod(long metaspaceKlassExactReceiver, long metaspaceMethod, long metaspaceKlassCaller); long getClassInitializer(long metaspaceKlass); @@ -353,4 +353,6 @@ void materializeVirtualObjects(HotSpotStackFrameReference stackFrame, boolean invalidate); void resolveInvokeDynamic(long metaspaceConstantPool, int index); + + int getVtableIndexForInterface(long metaspaceKlass, long metaspaceMethod); } diff -r 8c34e2cc4add -r bdf260d8e163 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 May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java Mon May 19 17:21:30 2014 -0700 @@ -99,7 +99,7 @@ public native void initializeConfiguration(HotSpotVMConfig config); @Override - public native long resolveMethod(long metaspaceKlass, String name, String signature); + public native long resolveMethod(long metaspaceKlassExactReceiver, long metaspaceMethod, long metaspaceKlassCaller); @Override public native boolean hasFinalizableSubclass(long metaspaceKlass); @@ -182,4 +182,6 @@ public native long getTimeStamp(); public native void resolveInvokeDynamic(long metaspaceConstantPool, int index); + + public native int getVtableIndexForInterface(long metaspaceKlass, long metaspaceMethod); } diff -r 8c34e2cc4add -r bdf260d8e163 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 May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Mon May 19 17:21:30 2014 -0700 @@ -91,7 +91,7 @@ * is in the proper state. */ static class Queue { - private ThreadPoolExecutor executor; + private final ThreadPoolExecutor executor; Queue(CompilerThreadFactory factory) { executor = new ThreadPoolExecutor(Threads.getValue(), Threads.getValue(), 0L, TimeUnit.MILLISECONDS, new PriorityBlockingQueue(), factory); @@ -117,11 +117,19 @@ executor.execute(task); } + /** + * @see ExecutorService#isShutdown() + */ + public boolean isShutdown() { + return executor.isShutdown(); + } + public void shutdown() throws InterruptedException { assert CompilationTask.isWithinEnqueue(); - executor.shutdown(); - if (Debug.isEnabled() && Dump.getValue() != null) { - // Wait 2 seconds to flush out all graph dumps that may be of interest + executor.shutdownNow(); + if (Debug.isEnabled() && (Dump.getValue() != null || areMetricsOrTimersEnabled())) { + // Wait up to 2 seconds to flush out all graph dumps and stop metrics/timers + // being updated. executor.awaitTermination(2, TimeUnit.SECONDS); } } @@ -472,18 +480,6 @@ printMap(new DebugValueScope(null, result), debugValues); } - static long collectTotal(DebugValue value) { - List maps = DebugValueMap.getTopLevelMaps(); - long total = 0; - for (int i = 0; i < maps.size(); i++) { - DebugValueMap map = maps.get(i); - int index = value.getIndex(); - total += map.getCurrentValue(index); - total += collectTotal(map.getChildren(), index); - } - return total; - } - private static long collectTotal(List maps, int index) { long total = 0; for (int i = 0; i < maps.size(); i++) { @@ -570,8 +566,8 @@ void compileMethod(final HotSpotResolvedJavaMethod method, final int entryBCI, long ctask, final boolean blocking) { if (ctask != 0L) { HotSpotBackend backend = runtime.getHostBackend(); - CompilationTask task = new CompilationTask(backend, method, entryBCI, ctask, false); - task.runCompilation(false); + CompilationTask task = new CompilationTask(null, backend, method, entryBCI, ctask, false); + task.runCompilation(); return; } @@ -596,13 +592,14 @@ if (method.tryToQueueForCompilation()) { assert method.isQueuedForCompilation(); - HotSpotBackend backend = runtime.getHostBackend(); - CompilationTask task = new CompilationTask(backend, method, entryBCI, ctask, block); - try { - compileQueue.execute(task); - if (block) { - task.block(); + if (!compileQueue.executor.isShutdown()) { + HotSpotBackend backend = runtime.getHostBackend(); + CompilationTask task = new CompilationTask(compileQueue.executor, backend, method, entryBCI, ctask, block); + compileQueue.execute(task); + if (block) { + task.block(); + } } } catch (RejectedExecutionException e) { // The compile queue was already shut down. diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/events/EmptyEventProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/events/EmptyEventProvider.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.hotspot.events; + +import com.oracle.graal.compiler.common.*; + +/** + * An empty implementation for {@link EventProvider}. This implementation is used when no logging is + * requested. + */ +public final class EmptyEventProvider implements EventProvider { + + public CompilationEvent newCompilationEvent() { + return new EmptyCompilationEvent(); + } + + class EmptyCompilationEvent implements CompilationEvent { + public void commit() { + throw GraalInternalError.shouldNotReachHere(); + } + + public boolean shouldWrite() { + // Events of this class should never been written. + return false; + } + + public void begin() { + } + + public void end() { + } + + public void setMethod(String method) { + throw GraalInternalError.shouldNotReachHere(); + } + + public void setCompileId(int compileId) { + throw GraalInternalError.shouldNotReachHere(); + } + + public void setCompileLevel(int compileLevel) { + throw GraalInternalError.shouldNotReachHere(); + } + + public void setSucceeded(boolean succeeded) { + throw GraalInternalError.shouldNotReachHere(); + } + + public void setIsOsr(boolean isOsr) { + throw GraalInternalError.shouldNotReachHere(); + } + + public void setCodeSize(int codeSize) { + throw GraalInternalError.shouldNotReachHere(); + } + + public void setInlinedBytes(int inlinedBytes) { + throw GraalInternalError.shouldNotReachHere(); + } + } + + public CompilerFailureEvent newCompilerFailureEvent() { + return new EmptyCompilerFailureEvent(); + } + + class EmptyCompilerFailureEvent implements CompilerFailureEvent { + public void commit() { + throw GraalInternalError.shouldNotReachHere(); + } + + public boolean shouldWrite() { + // Events of this class should never been written. + return false; + } + + public void setCompileId(int compileId) { + throw GraalInternalError.shouldNotReachHere(); + } + + public void setMessage(String message) { + throw GraalInternalError.shouldNotReachHere(); + } + } + +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/events/EventProvider.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/events/EventProvider.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.hotspot.events; + +/** + * A provider that provides a specific implementation for events that can be logged in the compiler. + */ +public interface EventProvider { + + /** + * An instant event is an event that is not considered to have taken any time. + */ + interface InstantEvent { + /** + * Commits the event. + */ + void commit(); + + /** + * Determines if this particular event instance would be committed to the data stream right + * now if application called {@link #commit()}. This in turn depends on whether the event is + * enabled and possible other factors. + * + * @return if this event would be committed on a call to {@link #commit()}. + */ + boolean shouldWrite(); + } + + /** + * Timed events describe an operation that somehow consumes time. + */ + interface TimedEvent extends InstantEvent { + /** + * Starts the timing for this event. + */ + void begin(); + + /** + * Ends the timing period for this event. + */ + void end(); + } + + /** + * Creates a new {@link CompilationEvent}. + * + * @return a compilation event + */ + CompilationEvent newCompilationEvent(); + + /** + * A compilation event. + */ + interface CompilationEvent extends TimedEvent { + void setMethod(String method); + + void setCompileId(int compileId); + + void setCompileLevel(int compileLevel); + + void setSucceeded(boolean succeeded); + + void setIsOsr(boolean isOsr); + + void setCodeSize(int codeSize); + + void setInlinedBytes(int inlinedBytes); + } + + /** + * Creates a new {@link CompilerFailureEvent}. + * + * @return a compiler failure event + */ + CompilerFailureEvent newCompilerFailureEvent(); + + /** + * A compiler failure event. + */ + interface CompilerFailureEvent extends InstantEvent { + void setCompileId(int compileId); + + void setMessage(String message); + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java Mon May 19 17:21:30 2014 -0700 @@ -230,29 +230,26 @@ JavaType[] signature = MetaUtil.signatureToTypes(callTarget.targetMethod().getSignature(), callTarget.isStatic() ? null : callTarget.targetMethod().getDeclaringClass()); LoweredCallTargetNode loweredCallTarget = null; - if (callTarget.invokeKind() == InvokeKind.Virtual && InlineVTableStubs.getValue() && (AlwaysInlineVTableStubs.getValue() || invoke.isPolymorphic())) { - + boolean isVirtualOrInterface = callTarget.invokeKind() == InvokeKind.Virtual || callTarget.invokeKind() == InvokeKind.Interface; + if (InlineVTableStubs.getValue() && isVirtualOrInterface && (AlwaysInlineVTableStubs.getValue() || invoke.isPolymorphic())) { HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) callTarget.targetMethod(); - if (!hsMethod.getDeclaringClass().isInterface()) { - if (hsMethod.isInVirtualMethodTable()) { - int vtableEntryOffset = hsMethod.vtableEntryOffset(); - assert vtableEntryOffset > 0; - Kind wordKind = runtime.getTarget().wordKind; - ValueNode hub = createReadHub(graph, wordKind, receiver, receiverNullCheck); + ResolvedJavaType receiverType = invoke.getReceiverType(); + if (hsMethod.isInVirtualMethodTable(receiverType)) { + Kind wordKind = runtime.getTarget().wordKind; + ValueNode hub = createReadHub(graph, wordKind, receiver, receiverNullCheck); - ReadNode metaspaceMethod = createReadVirtualMethod(graph, wordKind, hub, hsMethod); - // We use LocationNode.ANY_LOCATION for the reads that access the - // compiled code entry as HotSpot does not guarantee they are final - // values. - ReadNode compiledEntry = graph.add(new ReadNode(metaspaceMethod, ConstantLocationNode.create(ANY_LOCATION, wordKind, runtime.getConfig().methodCompiledEntryOffset, graph), - StampFactory.forKind(wordKind), BarrierType.NONE, false)); + ReadNode metaspaceMethod = createReadVirtualMethod(graph, wordKind, hub, hsMethod, receiverType); + // We use LocationNode.ANY_LOCATION for the reads that access the + // compiled code entry as HotSpot does not guarantee they are final + // values. + ReadNode compiledEntry = graph.add(new ReadNode(metaspaceMethod, ConstantLocationNode.create(ANY_LOCATION, wordKind, runtime.getConfig().methodCompiledEntryOffset, graph), + StampFactory.forKind(wordKind), BarrierType.NONE, false)); - loweredCallTarget = graph.add(new HotSpotIndirectCallTargetNode(metaspaceMethod, compiledEntry, parameters, invoke.asNode().stamp(), signature, callTarget.targetMethod(), - CallingConvention.Type.JavaCall)); + loweredCallTarget = graph.add(new HotSpotIndirectCallTargetNode(metaspaceMethod, compiledEntry, parameters, invoke.asNode().stamp(), signature, callTarget.targetMethod(), + CallingConvention.Type.JavaCall)); - graph.addBeforeFixed(invoke.asNode(), metaspaceMethod); - graph.addAfterFixed(metaspaceMethod, compiledEntry); - } + graph.addBeforeFixed(invoke.asNode(), metaspaceMethod); + graph.addAfterFixed(metaspaceMethod, compiledEntry); } } @@ -554,8 +551,8 @@ private void lowerLoadMethodNode(LoadMethodNode loadMethodNode) { StructuredGraph graph = loadMethodNode.graph(); - ResolvedJavaMethod method = loadMethodNode.getMethod(); - ReadNode metaspaceMethod = createReadVirtualMethod(graph, runtime.getTarget().wordKind, loadMethodNode.getHub(), method); + HotSpotResolvedJavaMethod method = (HotSpotResolvedJavaMethod) loadMethodNode.getMethod(); + ReadNode metaspaceMethod = createReadVirtualMethod(graph, runtime.getTarget().wordKind, loadMethodNode.getHub(), method, loadMethodNode.getReceiverType()); graph.replaceFixed(loadMethodNode, metaspaceMethod); } @@ -816,12 +813,11 @@ return false; } - private static ReadNode createReadVirtualMethod(StructuredGraph graph, Kind wordKind, ValueNode hub, ResolvedJavaMethod method) { - HotSpotResolvedJavaMethod hsMethod = (HotSpotResolvedJavaMethod) method; - assert !hsMethod.getDeclaringClass().isInterface(); - assert hsMethod.isInVirtualMethodTable(); + private static ReadNode createReadVirtualMethod(StructuredGraph graph, Kind wordKind, ValueNode hub, HotSpotResolvedJavaMethod method, ResolvedJavaType receiverType) { + return createReadVirtualMethod(graph, wordKind, hub, method.vtableEntryOffset(receiverType)); + } - int vtableEntryOffset = hsMethod.vtableEntryOffset(); + private static ReadNode createReadVirtualMethod(StructuredGraph graph, Kind wordKind, ValueNode hub, int vtableEntryOffset) { assert vtableEntryOffset > 0; // We use LocationNode.ANY_LOCATION for the reads that access the vtable // entry as HotSpot does not guarantee that this is a final value. diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantPool.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantPool.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantPool.java Mon May 19 17:21:30 2014 -0700 @@ -416,7 +416,7 @@ if ((metaspacePointer & config.compilerToVMSymbolTag) != 0) { final long metaspaceSymbol = metaspacePointer & ~config.compilerToVMSymbolTag; String name = new HotSpotSymbol(metaspaceSymbol).asString(); - return HotSpotUnresolvedJavaType.create(name); + return HotSpotUnresolvedJavaType.create("L" + name + ";"); } else { assert (metaspacePointer & config.compilerToVMKlassTag) == 0; return HotSpotResolvedObjectType.fromMetaspaceKlass(metaspacePointer); diff -r 8c34e2cc4add -r bdf260d8e163 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 May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java Mon May 19 17:21:30 2014 -0700 @@ -22,6 +22,7 @@ */ package com.oracle.graal.hotspot.meta; +import static com.oracle.graal.compiler.common.GraalInternalError.*; import static com.oracle.graal.compiler.common.GraalOptions.*; import static com.oracle.graal.compiler.common.UnsafeAccess.*; import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; @@ -33,7 +34,6 @@ import com.oracle.graal.api.code.*; import com.oracle.graal.api.meta.*; import com.oracle.graal.api.meta.ProfilingInfo.TriState; -import com.oracle.graal.compiler.common.*; import com.oracle.graal.debug.*; import com.oracle.graal.hotspot.*; import com.oracle.graal.hotspot.debug.*; @@ -589,22 +589,34 @@ /** * Returns the offset of this method into the v-table. The method must have a v-table entry as - * indicated by {@link #isInVirtualMethodTable()}, otherwise an exception is thrown. + * indicated by {@link #isInVirtualMethodTable(ResolvedJavaType)}, otherwise an exception is + * thrown. * * @return the offset of this method into the v-table */ - public int vtableEntryOffset() { - if (!isInVirtualMethodTable() || !holder.isInitialized()) { - throw new GraalInternalError("%s does not have a vtable entry", this); - } + public int vtableEntryOffset(ResolvedJavaType resolved) { + guarantee(isInVirtualMethodTable(resolved), "%s does not have a vtable entry", this); HotSpotVMConfig config = runtime().getConfig(); - final int vtableIndex = getVtableIndex(); + final int vtableIndex = getVtableIndex(resolved); return config.instanceKlassVtableStartOffset + vtableIndex * config.vtableEntrySize + config.vtableEntryMethodOffset; } @Override - public boolean isInVirtualMethodTable() { - return getVtableIndex() >= 0; + public boolean isInVirtualMethodTable(ResolvedJavaType resolved) { + return getVtableIndex(resolved) >= 0; + } + + private int getVtableIndex(ResolvedJavaType resolved) { + if (!holder.isLinked()) { + return runtime().getConfig().invalidVtableIndex; + } + if (holder.isInterface()) { + if (resolved.isArray() || resolved.isInterface()) { + return runtime().getConfig().invalidVtableIndex; + } + return getVtableIndexForInterface(resolved); + } + return getVtableIndex(); } /** @@ -620,6 +632,11 @@ return result; } + private int getVtableIndexForInterface(ResolvedJavaType resolved) { + HotSpotResolvedObjectType hotspotType = (HotSpotResolvedObjectType) resolved; + return runtime().getCompilerToVM().getVtableIndexForInterface(hotspotType.getMetaspaceKlass(), getMetaspaceMethod()); + } + /** * The {@link SpeculationLog} for methods compiled by Graal hang off this per-declaring-type * {@link ClassValue}. The raw Method* value is safe to use as a key in the map as a) it is diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java Mon May 19 17:21:30 2014 -0700 @@ -25,6 +25,7 @@ import static com.oracle.graal.api.meta.MetaUtil.*; import static com.oracle.graal.compiler.common.UnsafeAccess.*; import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; + import java.lang.annotation.*; import java.lang.reflect.*; import java.net.*; @@ -107,7 +108,7 @@ /** * Gets the metaspace Klass for this type. */ - private long metaspaceKlass() { + public long getMetaspaceKlass() { return HotSpotGraalRuntime.unsafeReadWord(javaClass, runtime().getConfig().klassOffset); } @@ -118,7 +119,7 @@ public int getAccessFlags() { HotSpotVMConfig config = runtime().getConfig(); - return unsafe.getInt(metaspaceKlass() + config.klassAccessFlagsOffset); + return unsafe.getInt(getMetaspaceKlass() + config.klassAccessFlagsOffset); } @Override @@ -141,7 +142,7 @@ if (isArray()) { return getElementalType(this).isFinal() ? this : null; } else if (isInterface()) { - final long implementorMetaspaceKlass = runtime().getCompilerToVM().getKlassImplementor(metaspaceKlass()); + final long implementorMetaspaceKlass = runtime().getCompilerToVM().getKlassImplementor(getMetaspaceKlass()); // No implementor. if (implementorMetaspaceKlass == 0) { @@ -192,7 +193,7 @@ * @return value of the subklass field as metaspace klass pointer */ private long getSubklass() { - return unsafeReadWord(metaspaceKlass() + runtime().getConfig().subklassOffset); + return unsafeReadWord(getMetaspaceKlass() + runtime().getConfig().subklassOffset); } @Override @@ -271,7 +272,7 @@ @Override public boolean hasFinalizableSubclass() { assert !isArray(); - return runtime().getCompilerToVM().hasFinalizableSubclass(metaspaceKlass()); + return runtime().getCompilerToVM().hasFinalizableSubclass(getMetaspaceKlass()); } @Override @@ -309,7 +310,7 @@ * @return state field value of this type */ private int getState() { - return unsafe.getByte(metaspaceKlass() + runtime().getConfig().klassStateOffset) & 0xFF; + return unsafe.getByte(getMetaspaceKlass() + runtime().getConfig().klassStateOffset) & 0xFF; } @Override @@ -354,13 +355,17 @@ } @Override - public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method) { - assert method instanceof HotSpotMethod; - if (!method.isAbstract() && method.getDeclaringClass().equals(this)) { + public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { + assert !callerType.isArray(); + if (!method.isAbstract() && method.getDeclaringClass().equals(this) && method.isPublic()) { return method; } - - final long resolvedMetaspaceMethod = runtime().getCompilerToVM().resolveMethod(metaspaceKlass(), method.getName(), ((HotSpotSignature) method.getSignature()).getMethodDescriptor()); + if (!method.getDeclaringClass().isAssignableFrom(this)) { + return null; + } + HotSpotResolvedJavaMethod hotSpotMethod = (HotSpotResolvedJavaMethod) method; + HotSpotResolvedObjectType hotSpotCallerType = (HotSpotResolvedObjectType) callerType; + final long resolvedMetaspaceMethod = runtime().getCompilerToVM().resolveMethod(getMetaspaceKlass(), hotSpotMethod.getMetaspaceMethod(), hotSpotCallerType.getMetaspaceKlass()); if (resolvedMetaspaceMethod == 0) { return null; } @@ -373,7 +378,7 @@ public ConstantPool constantPool() { if (constantPool == null) { - final long metaspaceConstantPool = unsafe.getAddress(metaspaceKlass() + runtime().getConfig().instanceKlassConstantsOffset); + final long metaspaceConstantPool = unsafe.getAddress(getMetaspaceKlass() + runtime().getConfig().instanceKlassConstantsOffset); constantPool = new HotSpotConstantPool(metaspaceConstantPool); } return constantPool; @@ -389,7 +394,7 @@ assert !isInterface(); HotSpotVMConfig config = runtime().getConfig(); - final int layoutHelper = unsafe.getInt(metaspaceKlass() + config.klassLayoutHelperOffset); + final int layoutHelper = unsafe.getInt(getMetaspaceKlass() + config.klassLayoutHelperOffset); assert layoutHelper > config.klassLayoutHelperNeutralValue : "must be instance"; // See: Klass::layout_helper_size_in_bytes @@ -475,7 +480,7 @@ public FieldInfo(int index) { HotSpotVMConfig config = runtime().getConfig(); // Get Klass::_fields - final long metaspaceFields = unsafe.getAddress(metaspaceKlass() + config.instanceKlassFieldsOffset); + final long metaspaceFields = unsafe.getAddress(getMetaspaceKlass() + config.instanceKlassFieldsOffset); assert config.fieldInfoFieldSlots == 6 : "revisit the field parsing code"; metaspaceData = metaspaceFields + config.arrayU2DataOffset + config.fieldInfoFieldSlots * Short.BYTES * index; } @@ -611,7 +616,7 @@ */ private int getFieldCount() { HotSpotVMConfig config = runtime().getConfig(); - final long metaspaceFields = unsafe.getAddress(metaspaceKlass() + config.instanceKlassFieldsOffset); + final long metaspaceFields = unsafe.getAddress(getMetaspaceKlass() + config.instanceKlassFieldsOffset); int metaspaceFieldsLength = unsafe.getInt(metaspaceFields + config.arrayU1LengthOffset); int fieldCount = 0; @@ -633,7 +638,7 @@ @Override public String getSourceFileName() { HotSpotVMConfig config = runtime().getConfig(); - final int sourceFileNameIndex = unsafe.getChar(metaspaceKlass() + config.klassSourceFileNameIndexOffset); + final int sourceFileNameIndex = unsafe.getChar(getMetaspaceKlass() + config.klassSourceFileNameIndexOffset); if (sourceFileNameIndex == 0) { return null; } @@ -654,7 +659,7 @@ * Gets the metaspace Klass boxed in a {@link Constant}. */ public Constant klass() { - return HotSpotMetaspaceConstant.forMetaspaceObject(runtime().getTarget().wordKind, metaspaceKlass(), this); + return HotSpotMetaspaceConstant.forMetaspaceObject(runtime().getTarget().wordKind, getMetaspaceKlass(), this); } public boolean isPrimaryType() { @@ -663,7 +668,7 @@ public int superCheckOffset() { HotSpotVMConfig config = runtime().getConfig(); - return unsafe.getInt(metaspaceKlass() + config.superCheckOffsetOffset); + return unsafe.getInt(getMetaspaceKlass() + config.superCheckOffsetOffset); } public long prototypeMarkWord() { @@ -671,7 +676,7 @@ if (isArray()) { return config.arrayPrototypeMarkWord(); } else { - return unsafeReadWord(metaspaceKlass() + config.prototypeMarkWordOffset); + return unsafeReadWord(getMetaspaceKlass() + config.prototypeMarkWordOffset); } } @@ -731,7 +736,7 @@ } public ResolvedJavaMethod getClassInitializer() { - final long metaspaceMethod = runtime().getCompilerToVM().getClassInitializer(metaspaceKlass()); + final long metaspaceMethod = runtime().getCompilerToVM().getClassInitializer(getMetaspaceKlass()); if (metaspaceMethod != 0L) { return createMethod(metaspaceMethod); } @@ -745,12 +750,6 @@ @Override public String toString() { - String simpleName; - if (isArray() || isInterface()) { - simpleName = getName(); - } else { - simpleName = getName().substring(1, getName().length() - 1); - } - return "HotSpotType<" + simpleName + ", resolved>"; + return "HotSpotType<" + getName() + ", resolved>"; } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedPrimitiveType.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedPrimitiveType.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedPrimitiveType.java Mon May 19 17:21:30 2014 -0700 @@ -163,7 +163,7 @@ } @Override - public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method) { + public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { return null; } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotUnresolvedJavaType.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotUnresolvedJavaType.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotUnresolvedJavaType.java Mon May 19 17:21:30 2014 -0700 @@ -32,56 +32,28 @@ public class HotSpotUnresolvedJavaType extends HotSpotJavaType { private static final long serialVersionUID = -2320936267633521314L; - public final String simpleName; - public final int dimensions; - public HotSpotUnresolvedJavaType(String name, String simpleName, int dimensions) { + public HotSpotUnresolvedJavaType(String name) { super(name); - assert dimensions >= 0; - this.simpleName = simpleName; - this.dimensions = dimensions; } /** * Creates an unresolved type for a valid {@link JavaType#getName() type name}. */ public static HotSpotUnresolvedJavaType create(String name) { - int dims = 0; - int startIndex = 0; - while (name.charAt(startIndex) == '[') { - startIndex++; - dims++; - } - - // Decode name if necessary. - if (name.charAt(name.length() - 1) == ';') { - assert name.charAt(startIndex) == 'L'; - return new HotSpotUnresolvedJavaType(name, name.substring(startIndex + 1, name.length() - 1), dims); - } else { - return new HotSpotUnresolvedJavaType(HotSpotUnresolvedJavaType.getFullName(name, dims), name, dims); - } - } - - public static String getFullName(String name, int dimensions) { - StringBuilder str = new StringBuilder(name.length() + dimensions + 2); - for (int i = 0; i < dimensions; i++) { - str.append('['); - } - str.append('L').append(name).append(';'); - return str.toString(); + assert name.charAt(name.length() - 1) == ';' : name; + return new HotSpotUnresolvedJavaType(name); } @Override public JavaType getComponentType() { - assert dimensions > 0 : "no array class" + getName(); - String name = getFullName(getName(), dimensions - 1); - return new HotSpotUnresolvedJavaType(name, simpleName, dimensions - 1); + assert getName().charAt(0) == '[' : "no array class" + getName(); + return new HotSpotUnresolvedJavaType(getName().substring(1)); } @Override public JavaType getArrayClass() { - String name = getFullName(getName(), dimensions + 1); - return new HotSpotUnresolvedJavaType(name, simpleName, dimensions + 1); + return new HotSpotUnresolvedJavaType('[' + getName()); } @Override @@ -91,7 +63,7 @@ @Override public int hashCode() { - return simpleName.hashCode(); + return getName().hashCode(); } @Override @@ -103,12 +75,12 @@ return false; } HotSpotUnresolvedJavaType that = (HotSpotUnresolvedJavaType) obj; - return this.simpleName.equals(that.simpleName) && this.dimensions == that.dimensions; + return this.getName().equals(that.getName()); } @Override public String toString() { - return "HotSpotType<" + simpleName + ", unresolved>"; + return "HotSpotType<" + getName() + ", unresolved>"; } @Override diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java Mon May 19 17:21:30 2014 -0700 @@ -179,15 +179,11 @@ @Snippet public static Object allocateArray(Word hub, int length, Word prototypeMarkWord, @ConstantParameter int headerSize, @ConstantParameter int log2ElementSize, @ConstantParameter boolean fillContents, @ConstantParameter Register threadRegister, @ConstantParameter boolean maybeUnroll, @ConstantParameter String typeContext) { - if (!belowThan(length, MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH)) { - // This handles both negative array sizes and very large array sizes - DeoptimizeNode.deopt(DeoptimizationAction.None, DeoptimizationReason.RuntimeConstraint); - } - return allocateArrayImpl(hub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, maybeUnroll, typeContext); + return allocateArrayImpl(hub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, maybeUnroll, typeContext, false); } private static Object allocateArrayImpl(Word hub, int length, Word prototypeMarkWord, int headerSize, int log2ElementSize, boolean fillContents, @ConstantParameter Register threadRegister, - @ConstantParameter boolean maybeUnroll, String typeContext) { + @ConstantParameter boolean maybeUnroll, String typeContext, boolean skipNegativeCheck) { Object result; int alignment = wordSize(); int allocationSize = computeArrayAllocationSize(length, alignment, headerSize, log2ElementSize); @@ -195,7 +191,7 @@ Word top = readTlabTop(thread); Word end = readTlabEnd(thread); Word newTop = top.add(allocationSize); - if (useTLAB() && probability(FAST_PATH_PROBABILITY, newTop.belowOrEqual(end))) { + if ((skipNegativeCheck || belowThan(length, MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH)) && useTLAB() && probability(FAST_PATH_PROBABILITY, newTop.belowOrEqual(end))) { writeTlabTop(thread, newTop); emitPrefetchAllocate(newTop, true); newarray_loopInit.inc(); @@ -246,7 +242,7 @@ int log2ElementSize = (layoutHelper >> layoutHelperLog2ElementSizeShift()) & layoutHelperLog2ElementSizeMask(); Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION); - return allocateArrayImpl(hub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, false, "dynamic type"); + return allocateArrayImpl(hub, length, prototypeMarkWord, headerSize, log2ElementSize, fillContents, threadRegister, false, "dynamic type", true); } /** diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java Mon May 19 17:21:30 2014 -0700 @@ -97,7 +97,7 @@ // check that array length is small enough for fast path. Word thread = registerAsWord(threadRegister); - if (length <= MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH) { + if (length >= 0 && length <= MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH) { Word memory = refillAllocate(thread, intArrayHub, sizeInBytes, logging()); if (memory.notEqual(0)) { if (logging()) { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java Mon May 19 17:21:30 2014 -0700 @@ -22,7 +22,6 @@ */ package com.oracle.graal.hotspot.stubs; -import static com.oracle.graal.api.meta.DeoptimizationAction.*; import static com.oracle.graal.api.meta.DeoptimizationReason.*; import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*; import static com.oracle.graal.hotspot.nodes.CStringNode.*; @@ -78,7 +77,7 @@ if (isObjectResult) { getAndClearObjectResult(thread); } - DeoptimizeCallerNode.deopt(InvalidateReprofile, RuntimeConstraint); + DeoptimizeCallerNode.deopt(DeoptimizationAction.None, RuntimeConstraint); } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java --- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java Mon May 19 17:21:30 2014 -0700 @@ -39,8 +39,8 @@ // @formatter:off - IADD, ISUB, IMUL, IDIV, IDIVREM, IREM, IUDIV, IUREM, IAND, IOR, IXOR, ISHL, ISHR, IUSHR, IROL, IROR, - LADD, LSUB, LMUL, LDIV, LDIVREM, LREM, LUDIV, LUREM, LAND, LOR, LXOR, LSHL, LSHR, LUSHR, LROL, LROR, + IADD, ISUB, IMUL, IUMUL, IDIV, IDIVREM, IREM, IUDIV, IUREM, IAND, IOR, IXOR, ISHL, ISHR, IUSHR, IROL, IROR, + LADD, LSUB, LMUL, LUMUL, LDIV, LDIVREM, LREM, LUDIV, LUREM, LAND, LOR, LXOR, LSHL, LSHR, LUSHR, LROL, LROR, FADD, FSUB, FMUL, FDIV, FREM, FAND, FOR, FXOR, DADD, DSUB, DMUL, DDIV, DREM, DAND, DOR, DXOR, INEG, LNEG, INOT, LNOT, @@ -342,6 +342,64 @@ } } + public static class MulHighOp extends AMD64LIRInstruction { + + @Opcode private final AMD64Arithmetic opcode; + @Def({REG}) public AllocatableValue lowResult; + @Def({REG}) public AllocatableValue highResult; + @Use({REG}) public AllocatableValue x; + @Use({REG, STACK}) public AllocatableValue y; + + public MulHighOp(AMD64Arithmetic opcode, AllocatableValue y) { + PlatformKind kind = y.getPlatformKind(); + + this.opcode = opcode; + this.x = AMD64.rax.asValue(kind); + this.y = y; + this.lowResult = AMD64.rax.asValue(kind); + this.highResult = AMD64.rdx.asValue(kind); + } + + @Override + public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { + if (isRegister(y)) { + switch (opcode) { + case IMUL: + masm.imull(asRegister(y)); + break; + case IUMUL: + masm.mull(asRegister(y)); + break; + case LMUL: + masm.imulq(asRegister(y)); + break; + case LUMUL: + masm.mulq(asRegister(y)); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + } else { + switch (opcode) { + case IMUL: + masm.imull((AMD64Address) crb.asAddress(y)); + break; + case IUMUL: + masm.mull((AMD64Address) crb.asAddress(y)); + break; + case LMUL: + masm.imulq((AMD64Address) crb.asAddress(y)); + break; + case LUMUL: + masm.mulq((AMD64Address) crb.asAddress(y)); + break; + default: + throw GraalInternalError.shouldNotReachHere(); + } + } + } + } + public static class DivRemOp extends AMD64LIRInstruction { @Opcode private final AMD64Arithmetic opcode; diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java --- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java Mon May 19 17:21:30 2014 -0700 @@ -142,6 +142,7 @@ protected void verify() { super.verify(); assert y instanceof Variable || y instanceof Constant; + assert kind != Kind.Long || !(y instanceof Constant) || NumUtil.isInt(((Constant) y).asLong()); } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java --- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java Mon May 19 17:21:30 2014 -0700 @@ -84,11 +84,6 @@ /** * Generates the code for this switch op. * - * The keys for switch statements in Java bytecode for of type int. However, Graal also - * generates a TypeSwitchNode (for method dispatch) which triggers the invocation of these - * routines with keys of type Long or Object. Currently we only support the - * IntegerSwitchNode so we throw an exception if the key isn't of type int. - * * @param crb the CompilationResultBuilder * @param masm the HSAIL assembler */ @@ -100,13 +95,13 @@ switch (key.getKind()) { case Int: case Long: + case Object: // Generate cascading compare and branches for each case. masm.emitCompare(key.getKind(), key, keyConstants[index], HSAILCompare.conditionToString(condition), false, false); masm.cbr(masm.nameOf(target)); break; - case Object: default: - throw new GraalInternalError("switch only supported for int"); + throw new GraalInternalError("switch not supported for kind " + key.getKind()); } } }; diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/ArithmeticLIRGenerator.java --- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/ArithmeticLIRGenerator.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/ArithmeticLIRGenerator.java Mon May 19 17:21:30 2014 -0700 @@ -42,6 +42,10 @@ Value emitMul(Value a, Value b); + Value emitMulHigh(Value a, Value b); + + Value emitUMulHigh(Value a, Value b); + Value emitDiv(Value a, Value b, LIRFrameState state); Value emitRem(Value a, Value b, LIRFrameState state); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampJoinTest.java --- a/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampJoinTest.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampJoinTest.java Mon May 19 17:21:30 2014 -0700 @@ -26,34 +26,16 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.compiler.common.type.*; -import com.oracle.graal.compiler.test.*; import com.oracle.graal.nodes.type.*; -public class ObjectStampJoinTest extends GraalCompilerTest { - - private static class A { - - } - - private static class B extends A { - - } - - private static class C extends B implements I { +public class ObjectStampJoinTest extends ObjectStampTest { - } - - private static class D extends A { - - } - - private abstract static class E extends A { - - } - - private interface I { - - } + // class A + // class B extends A + // class C extends B implements I + // class D extends A + // abstract class E extends A + // interface I @Test public void testJoin0() { @@ -142,8 +124,8 @@ @Test public void testJoinInterface0() { Stamp a = StampFactory.declared(getType(A.class)); - Stamp b = StampFactory.declared(getType(I.class)); - Assert.assertNotSame(StampFactory.illegal(Kind.Object), join(a, b)); + Stamp i = StampFactory.declared(getType(I.class)); + Assert.assertNotSame(StampFactory.illegal(Kind.Object), join(a, i)); } @Test @@ -163,14 +145,4 @@ Assert.assertEquals(StampFactory.illegal(Kind.Object), join); } - private static Stamp join(Stamp a, Stamp b) { - Stamp ab = a.join(b); - Stamp ba = b.join(a); - Assert.assertEquals(ab, ba); - return ab; - } - - private ResolvedJavaType getType(Class clazz) { - return getMetaAccess().lookupJavaType(clazz); - } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampMeetTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampMeetTest.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.nodes.test; + +import org.junit.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.type.*; + +public class ObjectStampMeetTest extends ObjectStampTest { + + // class A + // class B extends A + // class C extends B implements I + // class D extends A + // abstract class E extends A + // interface I + + @Test + public void testMeet0() { + Stamp a = StampFactory.declared(getType(A.class)); + Stamp b = StampFactory.declared(getType(B.class)); + Assert.assertEquals(a, meet(a, b)); + } + + @Test + public void testMeet1() { + Stamp a = StampFactory.declared(getType(A.class)); + Stamp aNonNull = StampFactory.declaredNonNull(getType(A.class)); + Stamp b = StampFactory.declared(getType(B.class)); + Stamp bNonNull = StampFactory.declaredNonNull(getType(B.class)); + Assert.assertEquals(a, meet(aNonNull, b)); + Assert.assertEquals(aNonNull, meet(aNonNull, bNonNull)); + } + + @Test + public void testMeet2() { + Stamp a = StampFactory.declared(getType(A.class)); + Stamp aExact = StampFactory.exactNonNull(getType(A.class)); + Stamp b = StampFactory.declared(getType(B.class)); + Assert.assertEquals(a, meet(aExact, b)); + } + + @Test + public void testMeet3() { + Stamp a = StampFactory.declared(getType(A.class)); + Stamp d = StampFactory.declared(getType(D.class)); + Stamp c = StampFactory.declared(getType(C.class)); + Assert.assertEquals(a, meet(c, d)); + } + + @Test + public void testMeet4() { + Stamp dExactNonNull = StampFactory.exactNonNull(getType(D.class)); + Stamp cExactNonNull = StampFactory.exactNonNull(getType(C.class)); + Stamp aNonNull = StampFactory.declaredNonNull(getType(A.class)); + Assert.assertEquals(aNonNull, meet(cExactNonNull, dExactNonNull)); + } + + @Test + public void testMeet() { + Stamp dExact = StampFactory.exact(getType(D.class)); + Stamp c = StampFactory.declared(getType(C.class)); + Stamp a = StampFactory.declared(getType(A.class)); + Assert.assertEquals(a, meet(dExact, c)); + } + + @Test + public void testMeet6() { + Stamp dExactNonNull = StampFactory.exactNonNull(getType(D.class)); + Stamp alwaysNull = StampFactory.alwaysNull(); + Stamp dExact = StampFactory.exact(getType(D.class)); + Assert.assertEquals(dExact, meet(dExactNonNull, alwaysNull)); + } + + @Test + public void testMeet7() { + Stamp aExact = StampFactory.exact(getType(A.class)); + Stamp e = StampFactory.declared(getType(E.class)); + Stamp a = StampFactory.declared(getType(A.class)); + Assert.assertEquals(a, meet(aExact, e)); + } + + @Test + public void testMeetInterface0() { + Stamp a = StampFactory.declared(getType(A.class)); + Stamp i = StampFactory.declared(getType(I.class)); + Assert.assertNotSame(StampFactory.object(), meet(a, i)); + } + + @Test + public void testMeetIllegal1() { + for (Class clazz : new Class[]{A.class, B.class, C.class, D.class, E.class, I.class, Object.class}) { + ResolvedJavaType type = getType(clazz); + for (Stamp test : new Stamp[]{StampFactory.declared(type), StampFactory.declaredNonNull(type), StampFactory.exact(type), StampFactory.exactNonNull(type)}) { + if (!type.isAbstract() || !((ObjectStamp) test).isExactType()) { + Assert.assertEquals("meeting illegal and " + test, test, meet(StampFactory.illegal(Kind.Object), test)); + } + } + } + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampTest.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.nodes.test; + +import org.junit.*; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.compiler.test.*; + +public class ObjectStampTest extends GraalCompilerTest { + + protected static class A { + + } + + protected static class B extends A { + + } + + protected static class C extends B implements I { + + } + + protected static class D extends A { + + } + + protected abstract static class E extends A { + + } + + protected interface I { + + } + + protected static Stamp join(Stamp a, Stamp b) { + Stamp ab = a.join(b); + Stamp ba = b.join(a); + Assert.assertEquals(ab, ba); + return ab; + } + + protected static Stamp meet(Stamp a, Stamp b) { + Stamp ab = a.meet(b); + Stamp ba = b.meet(a); + Assert.assertEquals(ab, ba); + return ab; + } + + protected ResolvedJavaType getType(Class clazz) { + return getMetaAccess().lookupJavaType(clazz); + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardedValueNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardedValueNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardedValueNode.java Mon May 19 17:21:30 2014 -0700 @@ -38,10 +38,12 @@ public class GuardedValueNode extends FloatingGuardedNode implements LIRLowerable, Virtualizable, IterableNodeType, Canonicalizable, ValueProxy { @Input private ValueNode object; + private final Stamp piStamp; public GuardedValueNode(ValueNode object, GuardingNode guard, Stamp stamp) { super(stamp, guard); this.object = object; + this.piStamp = stamp; } public GuardedValueNode(ValueNode object, GuardingNode guard) { @@ -61,10 +63,10 @@ @Override public boolean inferStamp() { - if (stamp() instanceof ObjectStamp && object().stamp() instanceof ObjectStamp) { - return updateStamp(((ObjectStamp) object().stamp()).castTo((ObjectStamp) stamp())); + if (piStamp instanceof ObjectStamp && object().stamp() instanceof ObjectStamp) { + return updateStamp(((ObjectStamp) object().stamp()).castTo((ObjectStamp) piStamp)); } - return updateStamp(object().stamp().join(stamp())); + return updateStamp(object().stamp().join(piStamp)); } @Override diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java Mon May 19 17:21:30 2014 -0700 @@ -41,6 +41,7 @@ @Input(InputType.Condition) private LogicNode condition; private final DeoptimizationReason reason; private final DeoptimizationAction action; + private final Stamp piStamp; private boolean negated; public ValueNode object() { @@ -81,6 +82,7 @@ public GuardingPiNode(ValueNode object, ValueNode condition, boolean negateCondition, DeoptimizationReason reason, DeoptimizationAction action, Stamp stamp) { super(stamp); assert stamp != null; + this.piStamp = stamp; this.object = object; this.condition = (LogicNode) condition; this.reason = reason; @@ -107,7 +109,7 @@ @Override public boolean inferStamp() { - return updateStamp(stamp().join(object().stamp())); + return updateStamp(piStamp.join(object().stamp())); } @Override diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java Mon May 19 17:21:30 2014 -0700 @@ -187,6 +187,10 @@ } while (false); } + if (checkForUnsignedCompare(tool)) { + return; + } + if (condition() instanceof LogicConstantNode) { LogicConstantNode c = (LogicConstantNode) condition(); if (c.getValue()) { @@ -219,7 +223,7 @@ // Reordering of those two if statements is beneficial from the point of view of // their probabilities. if (prepareForSwap(tool.getConstantReflection(), condition(), nextIf.condition(), this.trueSuccessorProbability, probabilityB)) { - // Reording is allowed from (if1 => begin => if2) to (if2 => begin => if1). + // Reordering is allowed from (if1 => begin => if2) to (if2 => begin => if1). assert intermediateBegin.next() == nextIf; BeginNode bothFalseBegin = nextIf.falseSuccessor(); nextIf.setFalseSuccessor(null); @@ -243,6 +247,106 @@ } } + /** + * Recognize a couple patterns that can be merged into an unsigned compare. + * + * @param tool + * @return true if a replacement was done. + */ + private boolean checkForUnsignedCompare(SimplifierTool tool) { + if (condition() instanceof IntegerLessThanNode && trueSuccessor().usages().isEmpty() && falseSuccessor().usages().isEmpty()) { + IntegerLessThanNode lessThan = (IntegerLessThanNode) condition(); + Constant y = lessThan.y().stamp().asConstant(); + if (y != null && y.asLong() == 0 && falseSuccessor().next() instanceof IfNode) { + IfNode ifNode2 = (IfNode) falseSuccessor().next(); + if (ifNode2.condition() instanceof IntegerLessThanNode) { + IntegerLessThanNode lessThan2 = (IntegerLessThanNode) ifNode2.condition(); + BeginNode falseSucc = ifNode2.falseSuccessor(); + BeginNode trueSucc = ifNode2.trueSuccessor(); + IntegerBelowThanNode below = null; + /* + * Convert x >= 0 && x < positive which is represented as !(x < 0) && x < + * into an unsigned compare. + */ + if (lessThan2.x() == lessThan.x() && lessThan2.y().stamp() instanceof IntegerStamp && ((IntegerStamp) lessThan2.y().stamp()).isPositive() && + sameDestination(trueSuccessor(), ifNode2.falseSuccessor)) { + below = graph().unique(new IntegerBelowThanNode(lessThan2.x(), lessThan2.y())); + // swap direction + BeginNode tmp = falseSucc; + falseSucc = trueSucc; + trueSucc = tmp; + } else if (lessThan2.y() == lessThan.x() && sameDestination(trueSuccessor(), ifNode2.trueSuccessor)) { + /* + * Convert x >= 0 && x <= positive which is represented as !(x < 0) && + * !( > x), into x <| positive + 1. This can only be done for + * constants since there isn't a IntegerBelowEqualThanNode but that doesn't + * appear to be interesting. + */ + Constant positive = lessThan2.x().asConstant(); + if (positive != null && positive.asLong() > 0 && positive.asLong() < positive.getKind().getMaxValue()) { + ConstantNode newLimit = ConstantNode.forIntegerKind(positive.getKind(), positive.asLong() + 1, graph()); + below = graph().unique(new IntegerBelowThanNode(lessThan.x(), newLimit)); + } + } + if (below != null) { + ifNode2.setTrueSuccessor(null); + ifNode2.setFalseSuccessor(null); + + IfNode newIfNode = graph().add(new IfNode(below, falseSucc, trueSucc, 1 - trueSuccessorProbability)); + // Remove the < 0 test. + tool.deleteBranch(trueSuccessor); + graph().removeSplit(this, falseSuccessor); + + // Replace the second test with the new one. + ifNode2.predecessor().replaceFirstSuccessor(ifNode2, newIfNode); + ifNode2.safeDelete(); + return true; + } + } + } + } + return false; + } + + /** + * Check it these two blocks end up at the same place. Meeting at the same merge, or + * deoptimizing in the same way. + */ + private static boolean sameDestination(BeginNode succ1, BeginNode succ2) { + Node next1 = succ1.next(); + Node next2 = succ2.next(); + if (next1 instanceof EndNode && next2 instanceof EndNode) { + EndNode end1 = (EndNode) next1; + EndNode end2 = (EndNode) next2; + if (end1.merge() == end2.merge()) { + // They go to the same MergeNode + return true; + } + } else if (next1 instanceof DeoptimizeNode && next2 instanceof DeoptimizeNode) { + DeoptimizeNode deopt1 = (DeoptimizeNode) next1; + DeoptimizeNode deopt2 = (DeoptimizeNode) next2; + if (deopt1.reason() == deopt2.reason() && deopt1.action() == deopt2.action()) { + // Same deoptimization reason and action. + return true; + } + } else if (next1 instanceof LoopExitNode && next2 instanceof LoopExitNode) { + LoopExitNode exit1 = (LoopExitNode) next1; + LoopExitNode exit2 = (LoopExitNode) next2; + if (exit1.loopBegin() == exit2.loopBegin() && exit1.stateAfter() == exit2.stateAfter() && exit1.stateAfter() == null && sameDestination(exit1, exit2)) { + // Exit the same loop and end up at the same place. + return true; + } + } else if (next1 instanceof ReturnNode && next2 instanceof ReturnNode) { + ReturnNode exit1 = (ReturnNode) next1; + ReturnNode exit2 = (ReturnNode) next2; + if (exit1.result() == exit2.result()) { + // Exit the same loop and end up at the same place. + return true; + } + } + return false; + } + private static boolean prepareForSwap(ConstantReflectionProvider constantReflection, LogicNode a, LogicNode b, double probabilityA, double probabilityB) { if (a instanceof InstanceOfNode) { InstanceOfNode instanceOfA = (InstanceOfNode) a; diff -r 8c34e2cc4add -r bdf260d8e163 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 May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java Mon May 19 17:21:30 2014 -0700 @@ -25,7 +25,9 @@ import com.oracle.graal.api.meta.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.extended.*; +import com.oracle.graal.nodes.java.*; import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; public interface Invoke extends StateSplit, Lowerable, DeoptimizingNode.DeoptDuring, GuardedNode { @@ -85,4 +87,16 @@ newStateDuring.setDuringCall(true); setStateDuring(newStateDuring); } + + default ValueNode getReceiver() { + return callTarget().arguments().get(0); + } + + default ResolvedJavaType getReceiverType() { + ResolvedJavaType receiverType = StampTool.typeOrNull(getReceiver()); + if (receiverType == null) { + receiverType = ((MethodCallTargetNode) callTarget()).targetMethod().getDeclaringClass(); + } + return receiverType; + } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java Mon May 19 17:21:30 2014 -0700 @@ -43,6 +43,7 @@ public class PiNode extends FloatingGuardedNode implements LIRLowerable, Virtualizable, IterableNodeType, Canonicalizable, ValueProxy { @Input private ValueNode object; + private final Stamp piStamp; public ValueNode object() { return object; @@ -50,12 +51,14 @@ public PiNode(ValueNode object, Stamp stamp) { super(stamp); + this.piStamp = stamp; this.object = object; } public PiNode(ValueNode object, Stamp stamp, ValueNode anchor) { super(stamp, (GuardingNode) anchor); this.object = object; + this.piStamp = stamp; } public PiNode(ValueNode object, ResolvedJavaType toType, boolean exactType, boolean nonNull) { @@ -71,13 +74,13 @@ @Override public boolean inferStamp() { - if (stamp() == StampFactory.forNodeIntrinsic()) { + if (piStamp == StampFactory.forNodeIntrinsic()) { return false; } - if (stamp() instanceof ObjectStamp && object.stamp() instanceof ObjectStamp) { - return updateStamp(((ObjectStamp) object.stamp()).castTo((ObjectStamp) stamp())); + if (piStamp instanceof ObjectStamp && object.stamp() instanceof ObjectStamp) { + return updateStamp(((ObjectStamp) object.stamp()).castTo((ObjectStamp) piStamp)); } - return updateStamp(stamp().join(object().stamp())); + return updateStamp(piStamp.join(object().stamp())); } @Override diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValuePhiNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValuePhiNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValuePhiNode.java Mon May 19 17:21:30 2014 -0700 @@ -54,10 +54,6 @@ @Override public boolean inferStamp() { - return inferPhiStamp(); - } - - public boolean inferPhiStamp() { return updateStamp(StampTool.meet(values())); } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConditionalNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConditionalNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConditionalNode.java Mon May 19 17:21:30 2014 -0700 @@ -82,7 +82,7 @@ IntegerStamp equalsXStamp = (IntegerStamp) equals.x().stamp(); if (equalsXStamp.upMask() == 1) { if (x().asConstant().equals(Constant.INT_0) && y().asConstant().equals(Constant.INT_1)) { - return equals.x(); + return IntegerConvertNode.convertUnsigned(equals.x(), stamp()); } } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java Mon May 19 17:21:30 2014 -0700 @@ -75,6 +75,14 @@ } public static ValueNode convert(ValueNode input, Stamp stamp) { + return convert(input, stamp, false); + } + + public static ValueNode convertUnsigned(ValueNode input, Stamp stamp) { + return convert(input, stamp, true); + } + + public static ValueNode convert(ValueNode input, Stamp stamp, boolean zeroExtend) { StructuredGraph graph = input.graph(); IntegerStamp fromStamp = (IntegerStamp) input.stamp(); IntegerStamp toStamp = (IntegerStamp) stamp; @@ -84,6 +92,9 @@ result = input; } else if (toStamp.getBits() < fromStamp.getBits()) { result = graph.unique(new NarrowNode(input, toStamp.getBits())); + } else if (zeroExtend) { + // toStamp.getBits() > fromStamp.getBits() + result = graph.unique(new ZeroExtendNode(input, toStamp.getBits())); } else { // toStamp.getBits() > fromStamp.getBits() result = graph.unique(new SignExtendNode(input, toStamp.getBits())); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java Mon May 19 17:21:30 2014 -0700 @@ -34,18 +34,20 @@ @Input private ValueNode hub; private final ResolvedJavaMethod method; + private final ResolvedJavaType receiverType; public ValueNode getHub() { return hub; } - public LoadMethodNode(ResolvedJavaMethod method, ValueNode hub, Kind kind) { + public LoadMethodNode(ResolvedJavaMethod method, ResolvedJavaType receiverType, ValueNode hub, Kind kind) { super(kind == Kind.Object ? StampFactory.objectNonNull() : StampFactory.forKind(kind)); + this.receiverType = receiverType; this.hub = hub; this.method = method; assert !method.isAbstract() : "Cannot load abstract method from a hub"; assert !method.isStatic() : "Cannot load a static method from a hub"; - assert method.isInVirtualMethodTable(); + assert method.isInVirtualMethodTable(receiverType); } @Override @@ -56,4 +58,8 @@ public ResolvedJavaMethod getMethod() { return method; } + + public ResolvedJavaType getReceiverType() { + return receiverType; + } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java Mon May 19 17:21:30 2014 -0700 @@ -23,10 +23,12 @@ package com.oracle.graal.nodes.extended; import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; -public class NullCheckNode extends DeoptimizingFixedWithNextNode implements LIRLowerable { +@NodeInfo(allowedUsageTypes = {InputType.Guard}) +public class NullCheckNode extends DeoptimizingFixedWithNextNode implements LIRLowerable, GuardingNode { @Input private ValueNode object; diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java Mon May 19 17:21:30 2014 -0700 @@ -132,8 +132,9 @@ @Override public boolean inferStamp() { - if (stamp() instanceof ObjectStamp && object().stamp() instanceof ObjectStamp) { - return updateStamp(((ObjectStamp) object().stamp()).castTo((ObjectStamp) stamp())); + if (object().stamp() instanceof ObjectStamp) { + ObjectStamp castStamp = (ObjectStamp) StampFactory.declared(type); + return updateStamp(((ObjectStamp) object().stamp()).castTo(castStamp)); } return false; } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java Mon May 19 17:21:30 2014 -0700 @@ -144,12 +144,32 @@ ResolvedJavaType type = StampTool.typeOrNull(receiver); if (type != null) { // either the holder class is exact, or the receiver object has an exact type - ResolvedJavaMethod resolvedMethod = type.resolveMethod(targetMethod); + ResolvedJavaMethod resolvedMethod = type.resolveMethod(targetMethod, invoke().getContextType()); if (resolvedMethod != null && (resolvedMethod.canBeStaticallyBound() || StampTool.isExactType(receiver))) { invokeKind = InvokeKind.Special; targetMethod = resolvedMethod; return this; } + if (tool.assumptions() != null && tool.assumptions().useOptimisticAssumptions()) { + ResolvedJavaType uniqueConcreteType = type.findUniqueConcreteSubtype(); + if (uniqueConcreteType != null) { + ResolvedJavaMethod methodFromUniqueType = uniqueConcreteType.resolveMethod(targetMethod, invoke().getContextType()); + if (methodFromUniqueType != null) { + tool.assumptions().recordConcreteSubtype(type, uniqueConcreteType); + invokeKind = InvokeKind.Special; + targetMethod = methodFromUniqueType; + return this; + } + } + + ResolvedJavaMethod uniqueConcreteMethod = type.findUniqueConcreteMethod(targetMethod); + if (uniqueConcreteMethod != null) { + tool.assumptions().recordConcreteMethod(targetMethod, type, uniqueConcreteMethod); + invokeKind = InvokeKind.Special; + targetMethod = uniqueConcreteMethod; + return this; + } + } } } return this; diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConditionalEliminationPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConditionalEliminationPhase.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConditionalEliminationPhase.java Mon May 19 17:21:30 2014 -0700 @@ -815,7 +815,7 @@ if (receiver != null && (callTarget.invokeKind() == InvokeKind.Interface || callTarget.invokeKind() == InvokeKind.Virtual)) { ResolvedJavaType type = state.getNodeType(receiver); if (!Objects.equals(type, StampTool.typeOrNull(receiver))) { - ResolvedJavaMethod method = type.resolveMethod(callTarget.targetMethod()); + ResolvedJavaMethod method = type.resolveMethod(callTarget.targetMethod(), invoke.getContextType()); if (method != null) { if (method.canBeStaticallyBound() || type.isFinal()) { callTarget.setInvokeKind(InvokeKind.Special); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeoptimizationGroupingPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeoptimizationGroupingPhase.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeoptimizationGroupingPhase.java Mon May 19 17:21:30 2014 -0700 @@ -62,7 +62,7 @@ merge.addForwardEnd(firstEnd); reasonActionPhi.addInput(((AbstractDeoptimizeNode) target).getActionAndReason(context.getMetaAccess())); speculationPhi.addInput(((AbstractDeoptimizeNode) target).getSpeculation(context.getMetaAccess())); - target.predecessor().replaceFirstSuccessor(target, firstEnd); + target.replaceAtPredecessor(firstEnd); exitLoops((AbstractDeoptimizeNode) target, firstEnd, cfg); @@ -77,7 +77,7 @@ merge.addForwardEnd(newEnd); reasonActionPhi.addInput(deopt.getActionAndReason(context.getMetaAccess())); speculationPhi.addInput(deopt.getSpeculation(context.getMetaAccess())); - deopt.predecessor().replaceFirstSuccessor(deopt, newEnd); + deopt.replaceAtPredecessor(newEnd); exitLoops(deopt, newEnd, cfg); obsoletes.add(deopt); } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java Mon May 19 17:21:30 2014 -0700 @@ -22,7 +22,10 @@ */ package com.oracle.graal.phases.common; +import java.util.*; + import com.oracle.graal.api.meta.*; +import com.oracle.graal.debug.*; import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.StructuredGraph.GuardsStage; @@ -34,6 +37,10 @@ public class UseTrappingNullChecksPhase extends BasePhase { + private static final DebugMetric metricTrappingNullCheck = Debug.metric("TrappingNullCheck"); + private static final DebugMetric metricTrappingNullCheckUnreached = Debug.metric("TrappingNullCheckUnreached"); + private static final DebugMetric metricTrappingNullCheckDynamicDeoptimize = Debug.metric("TrappingNullCheckDynamicDeoptimize"); + @Override protected void run(StructuredGraph graph, LowTierContext context) { if (context.getTarget().implicitNullCheckLimit <= 0) { @@ -42,36 +49,85 @@ assert graph.getGuardsStage().ordinal() >= GuardsStage.AFTER_FSA.ordinal(); for (DeoptimizeNode deopt : graph.getNodes(DeoptimizeNode.class)) { - tryUseTrappingNullCheck(deopt); + tryUseTrappingNullCheck(deopt, deopt.predecessor(), deopt.reason(), deopt.getSpeculation()); + } + for (DynamicDeoptimizeNode deopt : graph.getNodes(DynamicDeoptimizeNode.class)) { + tryUseTrappingNullCheck(context.getMetaAccess(), deopt); } } - private static void tryUseTrappingNullCheck(DeoptimizeNode deopt) { - if (deopt.reason() != DeoptimizationReason.NullCheckException) { - return; - } - if (deopt.getSpeculation() != null && !deopt.getSpeculation().equals(Constant.NULL_OBJECT)) { + private static void tryUseTrappingNullCheck(MetaAccessProvider metaAccessProvider, DynamicDeoptimizeNode deopt) { + ValueNode speculation = deopt.getSpeculation(); + if (!speculation.isConstant() || !speculation.asConstant().equals(Constant.NULL_OBJECT)) { return; } Node predecessor = deopt.predecessor(); + if (predecessor instanceof MergeNode) { + MergeNode merge = (MergeNode) predecessor; + + if (merge.phis().isEmpty()) { + // Process each predecessor at the merge, unpacking the reasons as needed. + ValueNode reason = deopt.getActionAndReason(); + List values = reason instanceof ValuePhiNode ? ((ValuePhiNode) reason).values().snapshot() : null; + + int index = 0; + for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) { + ValueNode thisReason = values != null ? values.get(index++) : reason; + if (thisReason.isConstant()) { + DeoptimizationReason deoptimizationReason = metaAccessProvider.decodeDeoptReason(thisReason.asConstant()); + tryUseTrappingNullCheck(deopt, end.predecessor(), deoptimizationReason, null); + } + } + } + } + } + + private static void tryUseTrappingNullCheck(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason, Constant speculation) { + if (deoptimizationReason != DeoptimizationReason.NullCheckException && deoptimizationReason != DeoptimizationReason.UnreachedCode) { + return; + } + if (speculation != null && !speculation.equals(Constant.NULL_OBJECT)) { + return; + } + if (predecessor instanceof MergeNode) { + MergeNode merge = (MergeNode) predecessor; + if (merge.phis().isEmpty()) { + for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) { + checkPredecessor(deopt, end.predecessor(), deoptimizationReason); + } + } + } else if (predecessor instanceof BeginNode) { + checkPredecessor(deopt, predecessor, deoptimizationReason); + } + } + + private static void checkPredecessor(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason) { + Node current = predecessor; Node branch = null; - while (predecessor instanceof BeginNode) { - branch = predecessor; - predecessor = predecessor.predecessor(); + while (current instanceof BeginNode) { + branch = current; + current = current.predecessor(); } - if (predecessor instanceof IfNode) { - IfNode ifNode = (IfNode) predecessor; + if (current instanceof IfNode) { + IfNode ifNode = (IfNode) current; if (branch != ifNode.trueSuccessor()) { return; } LogicNode condition = ifNode.condition(); if (condition instanceof IsNullNode) { - replaceWithTrappingNullCheck(deopt, ifNode, condition); + replaceWithTrappingNullCheck(deopt, ifNode, condition, deoptimizationReason); } } } - private static void replaceWithTrappingNullCheck(DeoptimizeNode deopt, IfNode ifNode, LogicNode condition) { + private static void replaceWithTrappingNullCheck(AbstractDeoptimizeNode deopt, IfNode ifNode, LogicNode condition, DeoptimizationReason deoptimizationReason) { + metricTrappingNullCheck.increment(); + if (deopt instanceof DynamicDeoptimizeNode) { + metricTrappingNullCheckDynamicDeoptimize.increment(); + } + if (deoptimizationReason == DeoptimizationReason.UnreachedCode) { + metricTrappingNullCheckUnreached.increment(); + } IsNullNode isNullNode = (IsNullNode) condition; BeginNode nonTrappingContinuation = ifNode.falseSuccessor(); BeginNode trappingContinuation = ifNode.trueSuccessor(); @@ -79,6 +135,17 @@ trappingNullCheck.setStateBefore(deopt.stateBefore()); deopt.graph().replaceSplit(ifNode, trappingNullCheck, nonTrappingContinuation); + /* + * We now have the pattern NullCheck/BeginNode/... It's possible some node is using the + * BeginNode as a guard input, so replace guard users of the Begin with the NullCheck and + * then remove the Begin from the graph. + */ + nonTrappingContinuation.replaceAtUsages(InputType.Guard, trappingNullCheck); + FixedNode next = nonTrappingContinuation.next(); + nonTrappingContinuation.clearSuccessors(); + trappingNullCheck.setNext(next); + nonTrappingContinuation.safeDelete(); + GraphUtil.killCFG(trappingContinuation); if (isNullNode.usages().isEmpty()) { GraphUtil.killWithUnusedFloatingInputs(isNullNode); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java Mon May 19 17:21:30 2014 -0700 @@ -594,7 +594,7 @@ if (type == null) { return; } - ResolvedJavaMethod method = type.resolveMethod(callTarget.targetMethod()); + ResolvedJavaMethod method = type.resolveMethod(callTarget.targetMethod(), invoke.getContextType()); if (method == null) { return; } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java Mon May 19 17:21:30 2014 -0700 @@ -191,8 +191,8 @@ * {@link com.oracle.graal.phases.common.cfs.FlowSensitiveReduction} determined to be * unreachable will be eliminated by canonicalization and dead code elimination. For now they * still exist, thus polluting the result of - * {@link com.oracle.graal.nodes.ValuePhiNode#inferPhiStamp()} but we are careful to skip them - * when merging type-witnesses and known-null maps. + * {@link com.oracle.graal.nodes.ValuePhiNode#inferStamp()} but we are careful to skip them when + * merging type-witnesses and known-null maps. *

*/ private void mergePhis(MergeNode merge, List withStates, Map newKnownPhiTypes) { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/ComputeInliningRelevance.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/ComputeInliningRelevance.java Mon May 19 17:14:36 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,324 +0,0 @@ -/* - * Copyright (c) 2013, 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.inlining; - -import static com.oracle.graal.graph.util.CollectionsAccess.*; - -import java.util.*; -import java.util.function.*; - -import com.oracle.graal.graph.*; -import com.oracle.graal.nodes.*; - -import edu.umd.cs.findbugs.annotations.*; - -public class ComputeInliningRelevance { - - private static final double EPSILON = 1d / Integer.MAX_VALUE; - private static final double UNINITIALIZED = -1D; - - private static final int EXPECTED_MIN_INVOKE_COUNT = 3; - private static final int EXPECTED_INVOKE_RATIO = 20; - private static final int EXPECTED_LOOP_COUNT = 3; - - private final StructuredGraph graph; - private final ToDoubleFunction nodeProbabilities; - - /** - * Node relevances are pre-computed for all invokes if the graph contains loops. If there are no - * loops, the computation happens lazily based on {@link #rootScope}. - */ - private Map nodeRelevances; - /** - * This scope is non-null if (and only if) there are no loops in the graph. In this case, the - * root scope is used to compute invoke relevances on the fly. - */ - private Scope rootScope; - - public ComputeInliningRelevance(StructuredGraph graph, ToDoubleFunction nodeProbabilities) { - this.graph = graph; - this.nodeProbabilities = nodeProbabilities; - } - - /** - * Initializes or updates the relevance computation. If there are no loops within the graph, - * most computation happens lazily. - */ - public void compute() { - rootScope = null; - if (!graph.hasLoops()) { - // fast path for the frequent case of no loops - rootScope = new Scope(graph.start(), null); - } else { - if (nodeRelevances == null) { - nodeRelevances = newNodeIdentityMap(EXPECTED_MIN_INVOKE_COUNT + graph.getNodeCount() / EXPECTED_INVOKE_RATIO); - } - NodeWorkList workList = graph.createNodeWorkList(); - Map loops = newNodeIdentityMap(EXPECTED_LOOP_COUNT); - - loops.put(null, new Scope(graph.start(), null)); - for (LoopBeginNode loopBegin : graph.getNodes(LoopBeginNode.class)) { - createLoopScope(loopBegin, loops); - } - - for (Scope scope : loops.values()) { - scope.process(workList); - } - } - } - - public double getRelevance(Invoke invoke) { - if (rootScope != null) { - return rootScope.computeInvokeRelevance(invoke); - } - assert nodeRelevances != null : "uninitialized relevance"; - return nodeRelevances.get(invoke); - } - - /** - * Determines the parent of the given loop and creates a {@link Scope} object for each one. This - * method will call itself recursively if no {@link Scope} for the parent loop exists. - */ - private Scope createLoopScope(LoopBeginNode loopBegin, Map loops) { - Scope scope = loops.get(loopBegin); - if (scope == null) { - final Scope parent; - // look for the parent scope - FixedNode current = loopBegin.forwardEnd(); - while (true) { - if (current.predecessor() == null) { - if (current instanceof LoopBeginNode) { - // if we reach a LoopBeginNode then we're within this loop - parent = createLoopScope((LoopBeginNode) current, loops); - break; - } else if (current instanceof StartNode) { - // we're within the outermost scope - parent = loops.get(null); - break; - } else { - assert current.getClass() == MergeNode.class : current; - // follow any path upwards - it doesn't matter which one - current = ((MergeNode) current).forwardEndAt(0); - } - } else if (current instanceof LoopExitNode) { - // if we reach a loop exit then we follow this loop and have the same parent - parent = createLoopScope(((LoopExitNode) current).loopBegin(), loops).parent; - break; - } else { - current = (FixedNode) current.predecessor(); - } - } - scope = new Scope(loopBegin, parent); - loops.put(loopBegin, scope); - } - return scope; - } - - /** - * A scope holds information for the contents of one loop or of the root of the method. It does - * not include child loops, i.e., the iteration in {@link #process(NodeWorkList)} explicitly - * excludes the nodes of child loops. - */ - private class Scope { - public final FixedNode start; - public final Scope parent; // can be null for the outermost scope - - /** - * The minimum probability along the most probable path in this scope. Computed lazily. - */ - private double fastPathMinProbability = UNINITIALIZED; - /** - * A measure of how important this scope is within its parent scope. Computed lazily. - */ - private double scopeRelevanceWithinParent = UNINITIALIZED; - - public Scope(FixedNode start, Scope parent) { - this.start = start; - this.parent = parent; - } - - @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") - public double getFastPathMinProbability() { - if (fastPathMinProbability == UNINITIALIZED) { - fastPathMinProbability = Math.max(EPSILON, computeFastPathMinProbability(start)); - } - return fastPathMinProbability; - } - - /** - * Computes the ratio between the probabilities of the current scope's entry point and the - * parent scope's fastPathMinProbability. - */ - @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") - public double getScopeRelevanceWithinParent() { - if (scopeRelevanceWithinParent == UNINITIALIZED) { - if (start instanceof LoopBeginNode) { - assert parent != null; - double scopeEntryProbability = nodeProbabilities.applyAsDouble(((LoopBeginNode) start).forwardEnd()); - - scopeRelevanceWithinParent = scopeEntryProbability / parent.getFastPathMinProbability(); - } else { - scopeRelevanceWithinParent = 1D; - } - } - return scopeRelevanceWithinParent; - } - - /** - * Processes all invokes in this scope by starting at the scope's start node and iterating - * all fixed nodes. Child loops are skipped by going from loop entries directly to the loop - * exits. Processing stops at loop exits of the current loop. - */ - public void process(NodeWorkList workList) { - assert !(start instanceof Invoke); - workList.addAll(start.successors()); - - for (Node current : workList) { - assert current.isAlive(); - - if (current instanceof Invoke) { - // process the invoke and queue its successors - nodeRelevances.put((FixedNode) current, computeInvokeRelevance((Invoke) current)); - workList.addAll(current.successors()); - } else if (current instanceof LoopBeginNode) { - // skip child loops by advancing over the loop exits - ((LoopBeginNode) current).loopExits().forEach(exit -> workList.add(exit.next())); - } else if (current instanceof LoopEndNode) { - // nothing to do - } else if (current instanceof LoopExitNode) { - // nothing to do - } else if (current instanceof FixedWithNextNode) { - workList.add(((FixedWithNextNode) current).next()); - } else if (current instanceof EndNode) { - workList.add(((EndNode) current).merge()); - } else if (current instanceof ControlSinkNode) { - // nothing to do - } else if (current instanceof ControlSplitNode) { - workList.addAll(current.successors()); - } else { - assert false : current; - } - } - } - - /** - * The relevance of an invoke is the ratio between the invoke's probability and the current - * scope's fastPathMinProbability, adjusted by scopeRelevanceWithinParent. - */ - public double computeInvokeRelevance(Invoke invoke) { - double invokeProbability = nodeProbabilities.applyAsDouble(invoke.asNode()); - assert !Double.isNaN(invokeProbability); - - double relevance = (invokeProbability / getFastPathMinProbability()) * Math.min(1.0, getScopeRelevanceWithinParent()); - assert !Double.isNaN(relevance) : invoke + ": " + relevance + " / " + invokeProbability + " / " + getFastPathMinProbability() + " / " + getScopeRelevanceWithinParent(); - return relevance; - } - } - - /** - * Computes the minimum probability along the most probable path within the scope. During - * iteration, the method returns immediately once a loop exit is discovered. - */ - private double computeFastPathMinProbability(FixedNode scopeStart) { - ArrayList pathBeginNodes = new ArrayList<>(); - pathBeginNodes.add(scopeStart); - double minPathProbability = nodeProbabilities.applyAsDouble(scopeStart); - boolean isLoopScope = scopeStart instanceof LoopBeginNode; - - do { - Node current = pathBeginNodes.remove(pathBeginNodes.size() - 1); - do { - if (isLoopScope && current instanceof LoopExitNode && ((LoopBeginNode) scopeStart).loopExits().contains((LoopExitNode) current)) { - return minPathProbability; - } else if (current instanceof LoopBeginNode && current != scopeStart) { - current = getMaxProbabilityLoopExit((LoopBeginNode) current, pathBeginNodes); - minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); - } else if (current instanceof ControlSplitNode) { - current = getMaxProbabilitySux((ControlSplitNode) current, pathBeginNodes); - minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); - } else { - assert current.successors().count() <= 1; - current = current.successors().first(); - } - } while (current != null); - } while (!pathBeginNodes.isEmpty()); - - return minPathProbability; - } - - private double getMinPathProbability(FixedNode current, double minPathProbability) { - return current == null ? minPathProbability : Math.min(minPathProbability, nodeProbabilities.applyAsDouble(current)); - } - - /** - * Returns the most probable successor. If multiple successors share the maximum probability, - * one is returned and the others are enqueued in pathBeginNodes. - */ - private static Node getMaxProbabilitySux(ControlSplitNode controlSplit, ArrayList pathBeginNodes) { - Node maxSux = null; - double maxProbability = 0.0; - int pathBeginCount = pathBeginNodes.size(); - - for (Node sux : controlSplit.successors()) { - double probability = controlSplit.probability((BeginNode) sux); - if (probability > maxProbability) { - maxProbability = probability; - maxSux = sux; - truncate(pathBeginNodes, pathBeginCount); - } else if (probability == maxProbability) { - pathBeginNodes.add((FixedNode) sux); - } - } - - return maxSux; - } - - /** - * Returns the most probable loop exit. If multiple successors share the maximum probability, - * one is returned and the others are enqueued in pathBeginNodes. - */ - private Node getMaxProbabilityLoopExit(LoopBeginNode loopBegin, ArrayList pathBeginNodes) { - Node maxSux = null; - double maxProbability = 0.0; - int pathBeginCount = pathBeginNodes.size(); - - for (LoopExitNode sux : loopBegin.loopExits()) { - double probability = nodeProbabilities.applyAsDouble(sux); - if (probability > maxProbability) { - maxProbability = probability; - maxSux = sux; - truncate(pathBeginNodes, pathBeginCount); - } else if (probability == maxProbability) { - pathBeginNodes.add(sux); - } - } - - return maxSux; - } - - private static void truncate(ArrayList pathBeginNodes, int pathBeginCount) { - for (int i = pathBeginNodes.size() - pathBeginCount; i > 0; i--) { - pathBeginNodes.remove(pathBeginNodes.size() - 1); - } - } -} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/DepthSearchUtil.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/DepthSearchUtil.java Mon May 19 17:14:36 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -/* - * 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.phases.common.inlining; - -import com.oracle.graal.api.meta.Constant; -import com.oracle.graal.api.meta.ResolvedJavaMethod; -import com.oracle.graal.compiler.common.type.Stamp; -import com.oracle.graal.debug.Debug; -import com.oracle.graal.graph.NodeInputList; -import com.oracle.graal.nodes.*; -import com.oracle.graal.phases.common.CanonicalizerPhase; -import com.oracle.graal.phases.common.DeadCodeEliminationPhase; -import com.oracle.graal.phases.tiers.HighTierContext; - -import static com.oracle.graal.compiler.common.GraalOptions.OptCanonicalizer; - -/** - * The workings of {@link InliningPhase#run(StructuredGraph, HighTierContext)} include delving into - * a callsite to explore inlining opportunities. The utilities used for that are grouped in this - * class. - */ -public class DepthSearchUtil { - - private DepthSearchUtil() { - // no instances - } - - static InliningUtil.Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context, CanonicalizerPhase canonicalizer) { - Class macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method); - if (macroNodeClass != null) { - return new InliningUtil.InlineableMacroNode(macroNodeClass); - } else { - return new InliningUtil.InlineableGraph(buildGraph(method, invoke, context, canonicalizer)); - } - } - - private static StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) { - final StructuredGraph newGraph; - final boolean parseBytecodes; - - // TODO (chaeubl): copying the graph is only necessary if it is modified or if it contains - // any invokes - StructuredGraph intrinsicGraph = InliningUtil.getIntrinsicGraph(context.getReplacements(), method); - if (intrinsicGraph != null) { - newGraph = intrinsicGraph.copy(); - parseBytecodes = false; - } else { - StructuredGraph cachedGraph = getCachedGraph(method, context); - if (cachedGraph != null) { - newGraph = cachedGraph.copy(); - parseBytecodes = false; - } else { - newGraph = new StructuredGraph(method); - parseBytecodes = true; - } - } - - try (Debug.Scope s = Debug.scope("InlineGraph", newGraph)) { - if (parseBytecodes) { - parseBytecodes(newGraph, context, canonicalizer); - } - - boolean callerHasMoreInformationAboutArguments = false; - NodeInputList args = invoke.callTarget().arguments(); - for (ParameterNode param : newGraph.getNodes(ParameterNode.class).snapshot()) { - ValueNode arg = args.get(param.index()); - if (arg.isConstant()) { - Constant constant = arg.asConstant(); - newGraph.replaceFloating(param, ConstantNode.forConstant(constant, context.getMetaAccess(), newGraph)); - callerHasMoreInformationAboutArguments = true; - } else { - Stamp joinedStamp = param.stamp().join(arg.stamp()); - if (joinedStamp != null && !joinedStamp.equals(param.stamp())) { - param.setStamp(joinedStamp); - callerHasMoreInformationAboutArguments = true; - } - } - } - - if (!callerHasMoreInformationAboutArguments) { - // TODO (chaeubl): if args are not more concrete, inlining should be avoided - // in most cases or we could at least use the previous graph size + invoke - // probability to check the inlining - } - - if (OptCanonicalizer.getValue()) { - canonicalizer.apply(newGraph, context); - } - - return newGraph; - } catch (Throwable e) { - throw Debug.handle(e); - } - } - - private static StructuredGraph getCachedGraph(ResolvedJavaMethod method, HighTierContext context) { - if (context.getGraphCache() != null) { - StructuredGraph cachedGraph = context.getGraphCache().get(method); - if (cachedGraph != null) { - return cachedGraph; - } - } - return null; - } - - /** - * This method builds the IR nodes for newGraph and canonicalizes them. Provided - * profiling info is mature, the resulting graph is cached. - */ - private static StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context, CanonicalizerPhase canonicalizer) { - final boolean hasMatureProfilingInfo = newGraph.method().getProfilingInfo().isMature(); - - if (context.getGraphBuilderSuite() != null) { - context.getGraphBuilderSuite().apply(newGraph, context); - } - assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING"; - - new DeadCodeEliminationPhase().apply(newGraph); - - if (OptCanonicalizer.getValue()) { - canonicalizer.apply(newGraph, context); - } - - if (hasMatureProfilingInfo && context.getGraphCache() != null) { - context.getGraphCache().put(newGraph.method(), newGraph.copy()); - } - return newGraph; - } -} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningIterator.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningIterator.java Mon May 19 17:14:36 2014 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,130 +0,0 @@ -/* - * 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.phases.common.inlining; - -import com.oracle.graal.graph.Node; -import com.oracle.graal.graph.NodeBitMap; -import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.MethodCallTargetNode; - -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.LinkedList; - -/** - * Given a graph, visit all fixed nodes in dominator-based order, collecting in the process the - * {@link Invoke} nodes with {@link MethodCallTargetNode}. Such list of callsites is returned by - * {@link #apply()} - */ -class InliningIterator { - - private final StartNode start; - private final Deque nodeQueue; - private final NodeBitMap queuedNodes; - - public InliningIterator(StructuredGraph graph) { - this.start = graph.start(); - this.nodeQueue = new ArrayDeque<>(); - this.queuedNodes = graph.createNodeBitMap(); - assert start.isAlive(); - } - - public LinkedList apply() { - LinkedList invokes = new LinkedList<>(); - FixedNode current; - forcedQueue(start); - - while ((current = nextQueuedNode()) != null) { - assert current.isAlive(); - - if (current instanceof Invoke && ((Invoke) current).callTarget() instanceof MethodCallTargetNode) { - if (current != start) { - invokes.addLast((Invoke) current); - } - queueSuccessors(current); - } else if (current instanceof LoopBeginNode) { - queueSuccessors(current); - } else if (current instanceof LoopEndNode) { - // nothing to do - } else if (current instanceof MergeNode) { - queueSuccessors(current); - } else if (current instanceof FixedWithNextNode) { - queueSuccessors(current); - } else if (current instanceof EndNode) { - queueMerge((EndNode) current); - } else if (current instanceof ControlSinkNode) { - // nothing to do - } else if (current instanceof ControlSplitNode) { - queueSuccessors(current); - } else { - assert false : current; - } - } - - return invokes; - } - - private void queueSuccessors(FixedNode x) { - for (Node node : x.successors()) { - queue(node); - } - } - - private void queue(Node node) { - if (node != null && !queuedNodes.isMarked(node)) { - forcedQueue(node); - } - } - - private void forcedQueue(Node node) { - queuedNodes.mark(node); - nodeQueue.addFirst((FixedNode) node); - } - - private FixedNode nextQueuedNode() { - if (nodeQueue.isEmpty()) { - return null; - } - - FixedNode result = nodeQueue.removeFirst(); - assert queuedNodes.isMarked(result); - return result; - } - - private void queueMerge(AbstractEndNode end) { - MergeNode merge = end.merge(); - if (!queuedNodes.isMarked(merge) && visitedAllEnds(merge)) { - queuedNodes.mark(merge); - nodeQueue.add(merge); - } - } - - private boolean visitedAllEnds(MergeNode merge) { - for (int i = 0; i < merge.forwardEndCount(); i++) { - if (!queuedNodes.isMarked(merge.forwardEndAt(i))) { - return false; - } - } - return true; - } -} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java Mon May 19 17:21:30 2014 -0700 @@ -22,36 +22,21 @@ */ package com.oracle.graal.phases.common.inlining; -import static com.oracle.graal.compiler.common.GraalOptions.*; -import static com.oracle.graal.phases.common.inlining.InliningPhase.Options.*; - import java.util.*; -import java.util.function.*; -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.meta.*; -import com.oracle.graal.compiler.common.*; -import com.oracle.graal.debug.*; -import com.oracle.graal.debug.Debug.Scope; -import com.oracle.graal.graph.Graph.Mark; -import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; -import com.oracle.graal.nodes.java.*; -import com.oracle.graal.nodes.spi.*; import com.oracle.graal.options.*; import com.oracle.graal.phases.common.*; -import com.oracle.graal.phases.common.inlining.InliningUtil.InlineInfo; -import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable; -import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableGraph; -import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableMacroNode; -import com.oracle.graal.phases.common.inlining.InliningUtil.InliningPolicy; -import com.oracle.graal.phases.graph.*; +import com.oracle.graal.phases.common.inlining.policy.GreedyInliningPolicy; +import com.oracle.graal.phases.common.inlining.policy.InliningPolicy; +import com.oracle.graal.phases.common.inlining.walker.CallsiteHolder; +import com.oracle.graal.phases.common.inlining.walker.InliningData; +import com.oracle.graal.phases.common.inlining.walker.MethodInvocation; import com.oracle.graal.phases.tiers.*; -import com.oracle.graal.phases.util.*; public class InliningPhase extends AbstractInliningPhase { - static class Options { + public static class Options { // @formatter:off @Option(help = "Unconditionally inline intrinsics") @@ -65,12 +50,6 @@ private int inliningCount; private int maxMethodPerInlining = Integer.MAX_VALUE; - // Metrics - private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed"); - private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered"); - private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize"); - private static final DebugMetric metricInliningRuns = Debug.metric("InliningRuns"); - public InliningPhase(CanonicalizerPhase canonicalizer) { this(new GreedyInliningPolicy(null), canonicalizer); } @@ -95,16 +74,17 @@ /** *

* The space of inlining decisions is explored depth-first with the help of a stack realized by - * {@link InliningData}. At any point in time, its topmost element consist of: + * {@link com.oracle.graal.phases.common.inlining.walker.InliningData}. At any point in time, + * its topmost element consist of: *

    *
  • - * one or more {@link GraphInfo}s of inlining candidates, all of them corresponding to a single - * callsite (details below). For example, "exact inline" leads to a single candidate.
  • + * one or more {@link CallsiteHolder}s of inlining candidates, all of them corresponding to a + * single callsite (details below). For example, "exact inline" leads to a single candidate. *
  • * the callsite (for the targets above) is tracked as a {@link MethodInvocation}. The difference - * between {@link MethodInvocation#totalGraphs()} and {@link MethodInvocation#processedGraphs()} - * indicates the topmost {@link GraphInfo}s that might be delved-into to explore inlining - * opportunities.
  • + * between {@link com.oracle.graal.phases.common.inlining.walker.MethodInvocation#totalGraphs()} + * and {@link MethodInvocation#processedGraphs()} indicates the topmost {@link CallsiteHolder}s + * that might be delved-into to explore inlining opportunities. *
*

* @@ -112,10 +92,11 @@ * The bottom-most element in the stack consists of: *
    *
  • - * a single {@link GraphInfo} (the root one, for the method on which inlining was called)
  • + * a single {@link CallsiteHolder} (the root one, for the method on which inlining was called) *
  • - * a single {@link MethodInvocation} (the {@link MethodInvocation#isRoot} one, ie the unknown - * caller of the root graph)
  • + * a single {@link MethodInvocation} (the + * {@link com.oracle.graal.phases.common.inlining.walker.MethodInvocation#isRoot} one, ie the + * unknown caller of the root graph) *
* *

@@ -132,7 +113,8 @@ *
  • * try to inline: move past the current inlining candidate (remove it from the topmost element). * If that was the last one then try to inline the callsite that is (still) in the topmost - * element of {@link InliningData}, and then remove such callsite.
  • + * element of {@link com.oracle.graal.phases.common.inlining.walker.InliningData}, and then + * remove such callsite. * *

    * @@ -141,12 +123,12 @@ *
      *
    • * the first step amounts to backtracking, the 2nd one to delving, and the 3rd one also involves - * bakctraking (however after may-be inlining).
    • + * backtracking (however after may-be inlining). *
    • * the choice of abandon-and-backtrack or delve-into is depends on * {@link InliningPolicy#isWorthInlining} and {@link InliningPolicy#continueInlining}.
    • *
    • - * the 3rd choice is picked when both of the previous one aren't picked
    • + * the 3rd choice is picked when both of the previous ones aren't picked *
    • * as part of trying-to-inline, {@link InliningPolicy#isWorthInlining} again sees use, but * that's another story.
    • @@ -156,35 +138,12 @@ */ @Override protected void run(final StructuredGraph graph, final HighTierContext context) { - final InliningData data = new InliningData(graph, context.getAssumptions(), maxMethodPerInlining, canonicalizer); - ToDoubleFunction probabilities = new FixedNodeProbabilityCache(); + final InliningData data = new InliningData(graph, context, maxMethodPerInlining, canonicalizer, inliningPolicy); while (data.hasUnprocessedGraphs()) { - final MethodInvocation currentInvocation = data.currentInvocation(); - if (!currentInvocation.isRoot() && - !inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), currentInvocation.callee(), data.inliningDepth(), currentInvocation.probability(), - currentInvocation.relevance(), false)) { - int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs(); - assert remainingGraphs > 0; - data.popGraphs(remainingGraphs); - data.popInvocation(); - } else if (data.currentGraph().hasRemainingInvokes() && inliningPolicy.continueInlining(data.currentGraph().graph())) { - data.processNextInvoke(context); - } else { - data.popGraph(); - if (!currentInvocation.isRoot()) { - assert currentInvocation.callee().invoke().asNode().isAlive(); - currentInvocation.incrementProcessedGraphs(); - if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) { - data.popInvocation(); - final MethodInvocation parentInvoke = data.currentInvocation(); - try (Scope s = Debug.scope("Inlining", data.inliningContext())) { - tryToInline(probabilities, data.currentGraph(), currentInvocation, parentInvoke, data.inliningDepth() + 1, context); - } catch (Throwable e) { - throw Debug.handle(e); - } - } - } + boolean wasInlined = data.moveForward(); + if (wasInlined) { + inliningCount++; } } @@ -192,552 +151,4 @@ assert data.graphCount() == 0; } - private void tryToInline(ToDoubleFunction probabilities, GraphInfo callerGraphInfo, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth, - HighTierContext context) { - InlineInfo callee = calleeInfo.callee(); - Assumptions callerAssumptions = parentInvocation.assumptions(); - - if (inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) { - doInline(callerGraphInfo, calleeInfo, callerAssumptions, context); - } else if (context.getOptimisticOptimizations().devirtualizeInvokes()) { - callee.tryToDevirtualizeInvoke(context.getMetaAccess(), callerAssumptions); - } - metricInliningConsidered.increment(); - } - - private void doInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, Assumptions callerAssumptions, HighTierContext context) { - StructuredGraph callerGraph = callerGraphInfo.graph(); - Mark markBeforeInlining = callerGraph.getMark(); - InlineInfo callee = calleeInfo.callee(); - try { - try (Scope scope = Debug.scope("doInline", callerGraph)) { - List invokeUsages = callee.invoke().asNode().usages().snapshot(); - callee.inline(new Providers(context), callerAssumptions); - callerAssumptions.record(calleeInfo.assumptions()); - metricInliningRuns.increment(); - Debug.dump(callerGraph, "after %s", callee); - - if (OptCanonicalizer.getValue()) { - Mark markBeforeCanonicalization = callerGraph.getMark(); - canonicalizer.applyIncremental(callerGraph, context, invokeUsages, markBeforeInlining); - - // process invokes that are possibly created during canonicalization - for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) { - if (newNode instanceof Invoke) { - callerGraphInfo.pushInvoke((Invoke) newNode); - } - } - } - - callerGraphInfo.computeProbabilities(); - - inliningCount++; - metricInliningPerformed.increment(); - } - } catch (BailoutException bailout) { - throw bailout; - } catch (AssertionError | RuntimeException e) { - throw new GraalInternalError(e).addContext(callee.toString()); - } catch (GraalInternalError e) { - throw e.addContext(callee.toString()); - } - } - - private abstract static class AbstractInliningPolicy implements InliningPolicy { - - protected final Map hints; - - public AbstractInliningPolicy(Map hints) { - this.hints = hints; - } - - protected double computeMaximumSize(double relevance, int configuredMaximum) { - double inlineRatio = Math.min(RelevanceCapForInlining.getValue(), relevance); - return configuredMaximum * inlineRatio; - } - - protected double getInliningBonus(InlineInfo info) { - if (hints != null && hints.containsKey(info.invoke())) { - return hints.get(info.invoke()); - } - return 1; - } - - protected boolean isIntrinsic(Replacements replacements, InlineInfo info) { - if (AlwaysInlineIntrinsics.getValue()) { - return onlyIntrinsics(replacements, info); - } else { - return onlyForcedIntrinsics(replacements, info); - } - } - - private static boolean onlyIntrinsics(Replacements replacements, InlineInfo info) { - for (int i = 0; i < info.numberOfMethods(); i++) { - if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { - return false; - } - } - return true; - } - - private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) { - for (int i = 0; i < info.numberOfMethods(); i++) { - if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { - return false; - } - if (!replacements.isForcedSubstitution(info.methodAt(i))) { - return false; - } - } - return true; - } - - protected static int previousLowLevelGraphSize(InlineInfo info) { - int size = 0; - for (int i = 0; i < info.numberOfMethods(); i++) { - ResolvedJavaMethod m = info.methodAt(i); - ProfilingInfo profile = m.getProfilingInfo(); - int compiledGraphSize = profile.getCompilerIRSize(StructuredGraph.class); - if (compiledGraphSize > 0) { - size += compiledGraphSize; - } - } - return size; - } - - protected static int determineNodeCount(InlineInfo info) { - int nodes = 0; - for (int i = 0; i < info.numberOfMethods(); i++) { - Inlineable elem = info.inlineableElementAt(i); - if (elem != null) { - nodes += elem.getNodeCount(); - } - } - return nodes; - } - - protected static double determineInvokeProbability(ToDoubleFunction probabilities, InlineInfo info) { - double invokeProbability = 0; - for (int i = 0; i < info.numberOfMethods(); i++) { - Inlineable callee = info.inlineableElementAt(i); - Iterable invokes = callee.getInvokes(); - if (invokes.iterator().hasNext()) { - for (Invoke invoke : invokes) { - invokeProbability += probabilities.applyAsDouble(invoke.asNode()); - } - } - } - return invokeProbability; - } - } - - public static class GreedyInliningPolicy extends AbstractInliningPolicy { - - public GreedyInliningPolicy(Map hints) { - super(hints); - } - - public boolean continueInlining(StructuredGraph currentGraph) { - if (currentGraph.getNodeCount() >= MaximumDesiredSize.getValue()) { - InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize"); - metricInliningStoppedByMaxDesiredSize.increment(); - return false; - } - return true; - } - - @Override - public boolean isWorthInlining(ToDoubleFunction probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, - boolean fullyProcessed) { - if (InlineEverything.getValue()) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything"); - } - - if (isIntrinsic(replacements, info)) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic"); - } - - if (info.shouldInline()) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining"); - } - - double inliningBonus = getInliningBonus(info); - int nodes = determineNodeCount(info); - int lowLevelGraphSize = previousLowLevelGraphSize(info); - - if (SmallCompiledLowLevelGraphSize.getValue() > 0 && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) { - return InliningUtil.logNotInlinedMethod(info, inliningDepth, "too large previous low-level graph (low-level-nodes: %d, relevance=%f, probability=%f, bonus=%f, nodes=%d)", - lowLevelGraphSize, relevance, probability, inliningBonus, nodes); - } - - if (nodes < TrivialInliningSize.getValue() * inliningBonus) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)", relevance, probability, inliningBonus, nodes); - } - - /* - * TODO (chaeubl): invoked methods that are on important paths but not yet compiled -> - * will be compiled anyways and it is likely that we are the only caller... might be - * useful to inline those methods but increases bootstrap time (maybe those methods are - * also getting queued in the compilation queue concurrently) - */ - double invokes = determineInvokeProbability(probabilities, info); - if (LimitInlinedInvokes.getValue() > 0 && fullyProcessed && invokes > LimitInlinedInvokes.getValue() * inliningBonus) { - return InliningUtil.logNotInlinedMethod(info, inliningDepth, "callee invoke probability is too high (invokeP=%f, relevance=%f, probability=%f, bonus=%f, nodes=%d)", invokes, - relevance, probability, inliningBonus, nodes); - } - - double maximumNodes = computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus)); - if (nodes <= maximumNodes) { - return InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)", relevance, probability, - inliningBonus, nodes, maximumNodes); - } - - return InliningUtil.logNotInlinedMethod(info, inliningDepth, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)", relevance, probability, inliningBonus, nodes, - maximumNodes); - } - } - - public static final class InlineEverythingPolicy implements InliningPolicy { - - public boolean continueInlining(StructuredGraph graph) { - if (graph.getNodeCount() >= MaximumDesiredSize.getValue()) { - throw new BailoutException("Inline all calls failed. The resulting graph is too large."); - } - return true; - } - - public boolean isWorthInlining(ToDoubleFunction probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, - boolean fullyProcessed) { - return true; - } - } - - /** - * Holds the data for building the callee graphs recursively: graphs and invocations (each - * invocation can have multiple graphs). - */ - static class InliningData { - - private static final GraphInfo DummyGraphInfo = new GraphInfo(null, 1.0, 1.0); - - /** - * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee. - */ - private final ArrayDeque graphQueue; - private final ArrayDeque invocationQueue; - private final int maxMethodPerInlining; - private final CanonicalizerPhase canonicalizer; - - private int maxGraphs; - - public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions, int maxMethodPerInlining, CanonicalizerPhase canonicalizer) { - this.graphQueue = new ArrayDeque<>(); - this.invocationQueue = new ArrayDeque<>(); - this.maxMethodPerInlining = maxMethodPerInlining; - this.canonicalizer = canonicalizer; - this.maxGraphs = 1; - - invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0)); - pushGraph(rootGraph, 1.0, 1.0); - } - - /** - * Process the next invoke and enqueue all its graphs for processing. - */ - void processNextInvoke(HighTierContext context) { - GraphInfo graphInfo = currentGraph(); - Invoke invoke = graphInfo.popInvoke(); - MethodInvocation callerInvocation = currentInvocation(); - Assumptions parentAssumptions = callerInvocation.assumptions(); - InlineInfo info = InliningUtil.getInlineInfo(this, invoke, maxMethodPerInlining, context.getReplacements(), parentAssumptions, context.getOptimisticOptimizations()); - - if (info != null) { - double invokeProbability = graphInfo.invokeProbability(invoke); - double invokeRelevance = graphInfo.invokeRelevance(invoke); - MethodInvocation calleeInvocation = pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance); - - for (int i = 0; i < info.numberOfMethods(); i++) { - Inlineable elem = DepthSearchUtil.getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions()), canonicalizer); - info.setInlinableElement(i, elem); - if (elem instanceof InlineableGraph) { - pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i)); - } else { - assert elem instanceof InlineableMacroNode; - pushDummyGraph(); - } - } - } - } - - public int graphCount() { - return graphQueue.size(); - } - - public void pushGraph(StructuredGraph graph, double probability, double relevance) { - assert !contains(graph); - graphQueue.push(new GraphInfo(graph, probability, relevance)); - assert graphQueue.size() <= maxGraphs; - } - - public void pushDummyGraph() { - graphQueue.push(DummyGraphInfo); - } - - public boolean hasUnprocessedGraphs() { - return !graphQueue.isEmpty(); - } - - public GraphInfo currentGraph() { - return graphQueue.peek(); - } - - public void popGraph() { - graphQueue.pop(); - assert graphQueue.size() <= maxGraphs; - } - - public void popGraphs(int count) { - assert count >= 0; - for (int i = 0; i < count; i++) { - graphQueue.pop(); - } - } - - private static final Object[] NO_CONTEXT = {}; - - /** - * Gets the call hierarchy of this inlining from outer most call to inner most callee. - */ - public Object[] inliningContext() { - if (!Debug.isDumpEnabled()) { - return NO_CONTEXT; - } - Object[] result = new Object[graphQueue.size()]; - int i = 0; - for (GraphInfo g : graphQueue) { - result[i++] = g.graph.method(); - } - return result; - } - - public MethodInvocation currentInvocation() { - return invocationQueue.peekFirst(); - } - - public MethodInvocation pushInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { - MethodInvocation methodInvocation = new MethodInvocation(info, new Assumptions(assumptions.useOptimisticAssumptions()), probability, relevance); - invocationQueue.addFirst(methodInvocation); - maxGraphs += info.numberOfMethods(); - assert graphQueue.size() <= maxGraphs; - return methodInvocation; - } - - public void popInvocation() { - maxGraphs -= invocationQueue.peekFirst().callee.numberOfMethods(); - assert graphQueue.size() <= maxGraphs; - invocationQueue.removeFirst(); - } - - public int countRecursiveInlining(ResolvedJavaMethod method) { - int count = 0; - for (GraphInfo graphInfo : graphQueue) { - if (method.equals(graphInfo.method())) { - count++; - } - } - return count; - } - - public int inliningDepth() { - assert invocationQueue.size() > 0; - return invocationQueue.size() - 1; - } - - @Override - public String toString() { - StringBuilder result = new StringBuilder("Invocations: "); - - for (MethodInvocation invocation : invocationQueue) { - if (invocation.callee() != null) { - result.append(invocation.callee().numberOfMethods()); - result.append("x "); - result.append(invocation.callee().invoke()); - result.append("; "); - } - } - - result.append("\nGraphs: "); - for (GraphInfo graph : graphQueue) { - result.append(graph.graph()); - result.append("; "); - } - - return result.toString(); - } - - private boolean contains(StructuredGraph graph) { - for (GraphInfo info : graphQueue) { - if (info.graph() == graph) { - return true; - } - } - return false; - } - } - - private static class MethodInvocation { - - private final InlineInfo callee; - private final Assumptions assumptions; - private final double probability; - private final double relevance; - - private int processedGraphs; - - public MethodInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { - this.callee = info; - this.assumptions = assumptions; - this.probability = probability; - this.relevance = relevance; - } - - public void incrementProcessedGraphs() { - processedGraphs++; - assert processedGraphs <= callee.numberOfMethods(); - } - - public int processedGraphs() { - assert processedGraphs <= callee.numberOfMethods(); - return processedGraphs; - } - - public int totalGraphs() { - return callee.numberOfMethods(); - } - - public InlineInfo callee() { - return callee; - } - - public Assumptions assumptions() { - return assumptions; - } - - public double probability() { - return probability; - } - - public double relevance() { - return relevance; - } - - public boolean isRoot() { - return callee == null; - } - - @Override - public String toString() { - if (isRoot()) { - return ""; - } - CallTargetNode callTarget = callee.invoke().callTarget(); - if (callTarget instanceof MethodCallTargetNode) { - ResolvedJavaMethod calleeMethod = ((MethodCallTargetNode) callTarget).targetMethod(); - return MetaUtil.format("Invoke#%H.%n(%p)", calleeMethod); - } else { - return "Invoke#" + callTarget.targetName(); - } - } - } - - /** - * Information about a graph that will potentially be inlined. This includes tracking the - * invocations in graph that will subject to inlining themselves. - */ - private static class GraphInfo { - - private final StructuredGraph graph; - private final LinkedList remainingInvokes; - private final double probability; - private final double relevance; - - private final ToDoubleFunction probabilities; - private final ComputeInliningRelevance computeInliningRelevance; - - public GraphInfo(StructuredGraph graph, double probability, double relevance) { - this.graph = graph; - if (graph == null) { - this.remainingInvokes = new LinkedList<>(); - } else { - LinkedList invokes = new InliningIterator(graph).apply(); - assert invokes.size() == count(graph.getInvokes()); - this.remainingInvokes = invokes; - } - this.probability = probability; - this.relevance = relevance; - - if (graph != null && !remainingInvokes.isEmpty()) { - probabilities = new FixedNodeProbabilityCache(); - computeInliningRelevance = new ComputeInliningRelevance(graph, probabilities); - computeProbabilities(); - } else { - probabilities = null; - computeInliningRelevance = null; - } - } - - private static int count(Iterable invokes) { - int count = 0; - Iterator iterator = invokes.iterator(); - while (iterator.hasNext()) { - iterator.next(); - count++; - } - return count; - } - - /** - * Gets the method associated with the {@linkplain #graph() graph} represented by this - * object. - */ - public ResolvedJavaMethod method() { - return graph.method(); - } - - public boolean hasRemainingInvokes() { - return !remainingInvokes.isEmpty(); - } - - /** - * The graph about which this object contains inlining information. - */ - public StructuredGraph graph() { - return graph; - } - - public Invoke popInvoke() { - return remainingInvokes.removeFirst(); - } - - public void pushInvoke(Invoke invoke) { - remainingInvokes.push(invoke); - } - - public void computeProbabilities() { - computeInliningRelevance.compute(); - } - - public double invokeProbability(Invoke invoke) { - return probability * probabilities.applyAsDouble(invoke.asNode()); - } - - public double invokeRelevance(Invoke invoke) { - return Math.min(CapInheritedRelevance.getValue(), relevance) * computeInliningRelevance.getRelevance(invoke); - } - - @Override - public String toString() { - return (graph != null ? MetaUtil.format("%H.%n(%p)", method()) : "") + remainingInvokes; - } - } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java --- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java Mon May 19 17:21:30 2014 -0700 @@ -28,15 +28,10 @@ import static com.oracle.graal.compiler.common.type.StampFactory.*; import java.util.*; -import java.util.function.*; import com.oracle.graal.api.code.*; -import com.oracle.graal.api.code.Assumptions.Assumption; 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.compiler.common.*; -import com.oracle.graal.compiler.common.calc.*; import com.oracle.graal.compiler.common.type.*; import com.oracle.graal.debug.*; import com.oracle.graal.debug.Debug.Scope; @@ -52,14 +47,11 @@ import com.oracle.graal.nodes.type.*; import com.oracle.graal.nodes.util.*; import com.oracle.graal.phases.*; -import com.oracle.graal.phases.common.*; -import com.oracle.graal.phases.common.inlining.InliningPhase.*; -import com.oracle.graal.phases.tiers.*; -import com.oracle.graal.phases.util.*; +import com.oracle.graal.phases.common.inlining.info.*; +import com.oracle.graal.phases.common.inlining.walker.InliningData; public class InliningUtil { - private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication"); private static final String inliningDecisionsScopeString = "InliningDecisions"; /** * Meters the size (in bytecodes) of all methods processed during compilation (i.e., top level @@ -68,13 +60,6 @@ */ public static final DebugMetric InlinedBytecodes = Debug.metric("InlinedBytecodes"); - public interface InliningPolicy { - - boolean continueInlining(StructuredGraph graph); - - boolean isWorthInlining(ToDoubleFunction probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed); - } - public interface Inlineable { int getNodeCount(); @@ -155,22 +140,21 @@ } } - public static boolean logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) { - return logInliningDecision(info, inliningDepth, allowLogging, true, msg, args); + public static void logInlinedMethod(InlineInfo info, int inliningDepth, boolean allowLogging, String msg, Object... args) { + logInliningDecision(info, inliningDepth, allowLogging, true, msg, args); } - public static boolean logNotInlinedMethod(InlineInfo info, int inliningDepth, String msg, Object... args) { - return logInliningDecision(info, inliningDepth, true, false, msg, args); + public static void logNotInlinedMethod(InlineInfo info, int inliningDepth, String msg, Object... args) { + logInliningDecision(info, inliningDepth, true, false, msg, args); } - public static boolean logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) { + public static void logInliningDecision(InlineInfo info, int inliningDepth, boolean allowLogging, boolean success, String msg, final Object... args) { if (allowLogging) { printInlining(info, inliningDepth, success, msg, args); if (shouldLogInliningDecision()) { logInliningDecision(methodName(info), success, msg, args); } } - return success; } public static void logInliningDecision(final String msg, final Object... args) { @@ -182,34 +166,23 @@ } } - private static boolean logNotInlinedMethod(Invoke invoke, String msg) { + public static void logNotInlinedMethod(Invoke invoke, String msg) { if (shouldLogInliningDecision()) { String methodString = invoke.toString() + (invoke.callTarget() == null ? " callTarget=null" : invoke.callTarget().targetName()); logInliningDecision(methodString, false, msg, new Object[0]); } - return false; } - private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) { - return logNotInlinedMethodAndReturnNull(invoke, inliningDepth, method, msg, new Object[0]); + public static void logNotInlined(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) { + logNotInlinedInvoke(invoke, inliningDepth, method, msg, new Object[0]); } - private static InlineInfo logNotInlinedMethodAndReturnNull(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) { + public static void logNotInlinedInvoke(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg, Object... args) { printInlining(method, invoke, inliningDepth, false, msg, args); if (shouldLogInliningDecision()) { String methodString = methodName(method, invoke); logInliningDecision(methodString, false, msg, args); } - return null; - } - - private static boolean logNotInlinedMethodAndReturnFalse(Invoke invoke, int inliningDepth, ResolvedJavaMethod method, String msg) { - printInlining(method, invoke, inliningDepth, false, msg, new Object[0]); - 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) { @@ -255,995 +228,13 @@ return sb.toString(); } - /** - * Represents an opportunity for inlining at a 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 { - - /** - * The graph containing the {@link #invoke() invocation} that may be inlined. - */ - StructuredGraph graph(); - - /** - * The invocation that may be inlined. - */ - Invoke invoke(); - - /** - * Returns the number of methods that may be inlined by the {@link #invoke() invocation}. - * This may be more than one in the case of a invocation profile showing a number of "hot" - * concrete methods dispatched to by the invocation. - */ - int numberOfMethods(); - - ResolvedJavaMethod methodAt(int index); - - Inlineable inlineableElementAt(int index); - - double probabilityAt(int index); - - double relevanceAt(int index); - - void setInlinableElement(int index, Inlineable inlineableElement); - - /** - * 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(Providers providers, Assumptions assumptions); - - /** - * Try to make the call static bindable to avoid interface and virtual method calls. - */ - void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions); - - boolean shouldInline(); - } - - public abstract static class AbstractInlineInfo implements InlineInfo { - - protected final Invoke invoke; - - public AbstractInlineInfo(Invoke invoke) { - this.invoke = invoke; - } - - @Override - public StructuredGraph graph() { - return invoke.asNode().graph(); - } - - @Override - public Invoke invoke() { - return invoke; - } - - protected static void inline(Invoke invoke, ResolvedJavaMethod concrete, Inlineable inlineable, Assumptions assumptions, boolean receiverNullCheck) { - if (inlineable instanceof InlineableGraph) { - StructuredGraph calleeGraph = ((InlineableGraph) inlineable).getGraph(); - InliningUtil.inline(invoke, calleeGraph, receiverNullCheck); - } else { - assert inlineable instanceof InlineableMacroNode; - - Class macroNodeClass = ((InlineableMacroNode) inlineable).getMacroNodeClass(); - inlineMacroNode(invoke, concrete, macroNodeClass); - } - - InlinedBytecodes.add(concrete.getCodeSize()); - assumptions.recordMethodContents(concrete); - } - } - public static void replaceInvokeCallTarget(Invoke invoke, StructuredGraph graph, InvokeKind invokeKind, ResolvedJavaMethod targetMethod) { MethodCallTargetNode oldCallTarget = (MethodCallTargetNode) invoke.callTarget(); MethodCallTargetNode newCallTarget = graph.add(new MethodCallTargetNode(invokeKind, targetMethod, oldCallTarget.arguments().toArray(new ValueNode[0]), oldCallTarget.returnType())); invoke.asNode().replaceFirstInput(oldCallTarget, newCallTarget); } - /** - * Represents an inlining opportunity where the compiler can statically determine a monomorphic - * target method and therefore is able to determine the called method exactly. - */ - public static class ExactInlineInfo extends AbstractInlineInfo { - - protected final ResolvedJavaMethod concrete; - private Inlineable inlineableElement; - private boolean suppressNullCheck; - - public ExactInlineInfo(Invoke invoke, ResolvedJavaMethod concrete) { - super(invoke); - this.concrete = concrete; - assert concrete != null; - } - - public void suppressNullCheck() { - suppressNullCheck = true; - } - - @Override - public void inline(Providers providers, Assumptions assumptions) { - inline(invoke, concrete, inlineableElement, assumptions, !suppressNullCheck); - } - - @Override - public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { - // nothing todo, can already be bound statically - } - - @Override - public int numberOfMethods() { - return 1; - } - - @Override - public ResolvedJavaMethod methodAt(int index) { - assert index == 0; - return concrete; - } - - @Override - public double probabilityAt(int index) { - assert index == 0; - return 1.0; - } - - @Override - public double relevanceAt(int index) { - assert index == 0; - return 1.0; - } - - @Override - public String toString() { - return "exact " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - - @Override - public Inlineable inlineableElementAt(int index) { - assert index == 0; - return inlineableElement; - } - - @Override - public void setInlinableElement(int index, Inlineable inlineableElement) { - assert index == 0; - this.inlineableElement = inlineableElement; - } - - public boolean shouldInline() { - return concrete.shouldBeInlined(); - } - } - - /** - * 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 { - - private final ResolvedJavaMethod concrete; - private final ResolvedJavaType type; - private Inlineable inlineableElement; - - public TypeGuardInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, ResolvedJavaType type) { - super(invoke); - this.concrete = concrete; - this.type = type; - assert type.isArray() || !type.isAbstract() : type; - } - - @Override - public int numberOfMethods() { - return 1; - } - - @Override - public ResolvedJavaMethod methodAt(int index) { - assert index == 0; - return concrete; - } - - @Override - public Inlineable inlineableElementAt(int index) { - assert index == 0; - return inlineableElement; - } - - @Override - public double probabilityAt(int index) { - assert index == 0; - return 1.0; - } - - @Override - public double relevanceAt(int index) { - assert index == 0; - return 1.0; - } - - @Override - public void setInlinableElement(int index, Inlineable inlineableElement) { - assert index == 0; - this.inlineableElement = inlineableElement; - } - - @Override - public void inline(Providers providers, Assumptions assumptions) { - createGuard(graph(), providers.getMetaAccess()); - inline(invoke, concrete, inlineableElement, assumptions, false); - } - - @Override - public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { - createGuard(graph(), metaAccess); - replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete); - } - - private void createGuard(StructuredGraph graph, MetaAccessProvider metaAccess) { - ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke); - ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(Representation.ObjectHub), metaAccess, graph); - LoadHubNode receiverHub = graph.unique(new LoadHubNode(nonNullReceiver, typeHub.getKind())); - - CompareNode typeCheck = CompareNode.createCompareNode(graph, Condition.EQ, receiverHub, typeHub); - FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile)); - assert invoke.predecessor() != null; - - ValueNode anchoredReceiver = createAnchoredReceiver(graph, guard, type, nonNullReceiver, true); - invoke.callTarget().replaceFirstInput(nonNullReceiver, anchoredReceiver); - - graph.addBeforeFixed(invoke.asNode(), guard); - } - - @Override - public String toString() { - return "type-checked with type " + type.getName() + " and method " + MetaUtil.format("%H.%n(%p):%r", concrete); - } - - public boolean shouldInline() { - return concrete.shouldBeInlined(); - } - } - - /** - * Polymorphic inlining of m methods with n type checks (n ≥ m) in case that the profiling - * information suggests a reasonable amount of different receiver types and different methods. - * If an unknown type is encountered a deoptimization is triggered. - */ - private static class MultiTypeGuardInlineInfo extends AbstractInlineInfo { - - private final List concretes; - private final double[] methodProbabilities; - private final double maximumMethodProbability; - private final ArrayList typesToConcretes; - private final ArrayList ptypes; - private final ArrayList concretesProbabilities; - private final double notRecordedTypeProbability; - private final Inlineable[] inlineableElements; - - public MultiTypeGuardInlineInfo(Invoke invoke, ArrayList concretes, ArrayList concretesProbabilities, ArrayList ptypes, - ArrayList typesToConcretes, double notRecordedTypeProbability) { - super(invoke); - assert concretes.size() > 0 : "must have at least one method"; - assert ptypes.size() == typesToConcretes.size() : "array lengths must match"; - - this.concretesProbabilities = concretesProbabilities; - this.concretes = concretes; - this.ptypes = ptypes; - this.typesToConcretes = typesToConcretes; - this.notRecordedTypeProbability = notRecordedTypeProbability; - this.inlineableElements = new Inlineable[concretes.size()]; - this.methodProbabilities = computeMethodProbabilities(); - this.maximumMethodProbability = maximumMethodProbability(); - assert maximumMethodProbability > 0; - } - - private double[] computeMethodProbabilities() { - double[] result = new double[concretes.size()]; - for (int i = 0; i < typesToConcretes.size(); i++) { - int concrete = typesToConcretes.get(i); - double probability = ptypes.get(i).getProbability(); - result[concrete] += probability; - } - return result; - } - - private double maximumMethodProbability() { - double max = 0; - for (int i = 0; i < methodProbabilities.length; i++) { - max = Math.max(max, methodProbabilities[i]); - } - return max; - } - - @Override - public int numberOfMethods() { - return concretes.size(); - } - - @Override - public ResolvedJavaMethod methodAt(int index) { - assert index >= 0 && index < concretes.size(); - return concretes.get(index); - } - - @Override - public Inlineable inlineableElementAt(int index) { - assert index >= 0 && index < concretes.size(); - return inlineableElements[index]; - } - - @Override - public double probabilityAt(int index) { - return methodProbabilities[index]; - } - - @Override - public double relevanceAt(int index) { - return probabilityAt(index) / maximumMethodProbability; - } - - @Override - public void setInlinableElement(int index, Inlineable inlineableElement) { - assert index >= 0 && index < concretes.size(); - inlineableElements[index] = inlineableElement; - } - - @Override - public void inline(Providers providers, Assumptions assumptions) { - if (hasSingleMethod()) { - inlineSingleMethod(graph(), providers.getMetaAccess(), assumptions); - } else { - inlineMultipleMethods(graph(), providers, assumptions); - } - } - - public boolean shouldInline() { - for (ResolvedJavaMethod method : concretes) { - if (method.shouldBeInlined()) { - return true; - } - } - return false; - } - - private boolean hasSingleMethod() { - return concretes.size() == 1 && !shouldFallbackToInvoke(); - } - - private boolean shouldFallbackToInvoke() { - return notRecordedTypeProbability > 0; - } - - private void inlineMultipleMethods(StructuredGraph graph, Providers providers, Assumptions assumptions) { - int numberOfMethods = concretes.size(); - FixedNode continuation = invoke.next(); - - ValueNode originalReceiver = ((MethodCallTargetNode) invoke.callTarget()).receiver(); - // setup merge and phi nodes for results and exceptions - MergeNode returnMerge = graph.add(new MergeNode()); - returnMerge.setStateAfter(invoke.stateAfter()); - - PhiNode returnValuePhi = null; - if (invoke.asNode().getKind() != Kind.Void) { - returnValuePhi = graph.addWithoutUnique(new ValuePhiNode(invoke.asNode().stamp().unrestricted(), returnMerge)); - } - - MergeNode exceptionMerge = null; - PhiNode exceptionObjectPhi = null; - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; - ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge(); - - exceptionMerge = graph.add(new MergeNode()); - - FixedNode exceptionSux = exceptionEdge.next(); - graph.addBeforeFixed(exceptionSux, exceptionMerge); - exceptionObjectPhi = graph.addWithoutUnique(new ValuePhiNode(StampFactory.forKind(Kind.Object), exceptionMerge)); - exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, Kind.Object, exceptionObjectPhi)); - } - - // create one separate block for each invoked method - BeginNode[] successors = new BeginNode[numberOfMethods + 1]; - for (int i = 0; i < numberOfMethods; i++) { - successors[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, true); - } - - // create the successor for an unknown type - FixedNode unknownTypeSux; - if (shouldFallbackToInvoke()) { - unknownTypeSux = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, false); - } else { - unknownTypeSux = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated)); - } - successors[successors.length - 1] = BeginNode.begin(unknownTypeSux); - - // replace the invoke exception edge - if (invoke instanceof InvokeWithExceptionNode) { - InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke; - ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExceptionNode.exceptionEdge(); - exceptionEdge.replaceAtUsages(exceptionObjectPhi); - exceptionEdge.setNext(null); - GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge()); - } - - assert invoke.asNode().isAlive(); - - // replace the invoke with a switch on the type of the actual receiver - boolean methodDispatch = createDispatchOnTypeBeforeInvoke(graph, successors, false, providers.getMetaAccess()); - - assert invoke.next() == continuation; - invoke.setNext(null); - returnMerge.setNext(continuation); - invoke.asNode().replaceAtUsages(returnValuePhi); - invoke.asNode().replaceAndDelete(null); - - ArrayList replacementNodes = 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; - if (methodDispatch) { - commonType = concretes.get(i).getDeclaringClass(); - } else { - commonType = getLeastCommonType(i); - } - - ValueNode receiver = ((MethodCallTargetNode) invokeForInlining.callTarget()).receiver(); - boolean exact = (getTypeCount(i) == 1 && !methodDispatch); - GuardedValueNode anchoredReceiver = createAnchoredReceiver(graph, node, commonType, receiver, exact); - invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver); - - inline(invokeForInlining, methodAt(i), inlineableElementAt(i), assumptions, false); - - replacementNodes.add(anchoredReceiver); - } - if (shouldFallbackToInvoke()) { - replacementNodes.add(null); - } - - if (OptTailDuplication.getValue()) { - /* - * 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).callTarget() instanceof MethodCallTargetNode && - ((MethodCallTargetNode) ((InvokeNode) current).callTarget()).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); - PhaseContext phaseContext = new PhaseContext(providers, assumptions); - CanonicalizerPhase canonicalizer = new CanonicalizerPhase(!ImmutableCode.getValue()); - TailDuplicationPhase.tailDuplicate(returnMerge, TailDuplicationPhase.TRUE_DECISION, replacementNodes, phaseContext, canonicalizer); - } - } - } - - private int getTypeCount(int concreteMethodIndex) { - int count = 0; - for (int i = 0; i < typesToConcretes.size(); i++) { - if (typesToConcretes.get(i) == concreteMethodIndex) { - count++; - } - } - return count; - } - - private ResolvedJavaType getLeastCommonType(int concreteMethodIndex) { - ResolvedJavaType commonType = null; - for (int i = 0; i < typesToConcretes.size(); i++) { - if (typesToConcretes.get(i) == concreteMethodIndex) { - if (commonType == null) { - commonType = ptypes.get(i).getType(); - } else { - commonType = commonType.findLeastCommonAncestor(ptypes.get(i).getType()); - } - } - } - assert commonType != null; - return commonType; - } - - private ResolvedJavaType getLeastCommonType() { - ResolvedJavaType result = getLeastCommonType(0); - for (int i = 1; i < concretes.size(); i++) { - result = result.findLeastCommonAncestor(getLeastCommonType(i)); - } - return result; - } - - private void inlineSingleMethod(StructuredGraph graph, MetaAccessProvider metaAccess, Assumptions assumptions) { - assert concretes.size() == 1 && inlineableElements.length == 1 && ptypes.size() > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0; - - BeginNode calleeEntryNode = graph.add(new BeginNode()); - - BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph); - BeginNode[] successors = new BeginNode[]{calleeEntryNode, unknownTypeSux}; - createDispatchOnTypeBeforeInvoke(graph, successors, false, metaAccess); - - calleeEntryNode.setNext(invoke.asNode()); - - inline(invoke, methodAt(0), inlineableElementAt(0), assumptions, false); - } - - private boolean createDispatchOnTypeBeforeInvoke(StructuredGraph graph, BeginNode[] successors, boolean invokeIsOnlySuccessor, MetaAccessProvider metaAccess) { - assert ptypes.size() >= 1; - ValueNode nonNullReceiver = nonNullReceiver(invoke); - Kind hubKind = ((MethodCallTargetNode) invoke.callTarget()).targetMethod().getDeclaringClass().getEncoding(Representation.ObjectHub).getKind(); - LoadHubNode hub = graph.unique(new LoadHubNode(nonNullReceiver, hubKind)); - - if (!invokeIsOnlySuccessor && chooseMethodDispatch()) { - assert successors.length == concretes.size() + 1; - assert concretes.size() > 0; - Debug.log("Method check cascade with %d methods", concretes.size()); - - ValueNode[] constantMethods = new ValueNode[concretes.size()]; - double[] probability = new double[concretes.size()]; - for (int i = 0; i < concretes.size(); ++i) { - ResolvedJavaMethod firstMethod = concretes.get(i); - Constant firstMethodConstant = firstMethod.getEncoding(); - - ValueNode firstMethodConstantNode = ConstantNode.forConstant(firstMethodConstant, metaAccess, graph); - constantMethods[i] = firstMethodConstantNode; - double concretesProbability = concretesProbabilities.get(i); - assert concretesProbability >= 0.0; - probability[i] = concretesProbability; - if (i > 0) { - double prevProbability = probability[i - 1]; - if (prevProbability == 1.0) { - probability[i] = 1.0; - } else { - probability[i] = Math.min(1.0, Math.max(0.0, probability[i] / (1.0 - prevProbability))); - } - } - } - - FixedNode lastSucc = successors[concretes.size()]; - for (int i = concretes.size() - 1; i >= 0; --i) { - LoadMethodNode method = graph.add(new LoadMethodNode(concretes.get(i), hub, constantMethods[i].getKind())); - CompareNode methodCheck = CompareNode.createCompareNode(graph, Condition.EQ, method, constantMethods[i]); - IfNode ifNode = graph.add(new IfNode(methodCheck, successors[i], lastSucc, probability[i])); - method.setNext(ifNode); - lastSucc = method; - } - - FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor(); - pred.setNext(lastSucc); - return true; - } else { - Debug.log("Type switch with %d types", concretes.size()); - } - - ResolvedJavaType[] keys = new ResolvedJavaType[ptypes.size()]; - double[] keyProbabilities = new double[ptypes.size() + 1]; - int[] keySuccessors = new int[ptypes.size() + 1]; - for (int i = 0; i < ptypes.size(); i++) { - keys[i] = ptypes.get(i).getType(); - keyProbabilities[i] = ptypes.get(i).getProbability(); - keySuccessors[i] = invokeIsOnlySuccessor ? 0 : typesToConcretes.get(i); - assert keySuccessors[i] < successors.length - 1 : "last successor is the unknownTypeSux"; - } - keyProbabilities[keyProbabilities.length - 1] = notRecordedTypeProbability; - keySuccessors[keySuccessors.length - 1] = successors.length - 1; - - TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(hub, successors, keys, keyProbabilities, keySuccessors)); - FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor(); - pred.setNext(typeSwitch); - return false; - } - - private boolean chooseMethodDispatch() { - for (ResolvedJavaMethod concrete : concretes) { - if (!concrete.isInVirtualMethodTable()) { - return false; - } - } - - if (concretes.size() == 1 && this.notRecordedTypeProbability > 0) { - // Always chose method dispatch if there is a single concrete method and the call - // site is megamorphic. - return true; - } - - if (concretes.size() == ptypes.size()) { - // Always prefer types over methods if the number of types is smaller than the - // number of methods. - return false; - } - - return chooseMethodDispatchCostBased(); - } - - private boolean chooseMethodDispatchCostBased() { - double remainder = 1.0 - this.notRecordedTypeProbability; - double costEstimateMethodDispatch = remainder; - for (int i = 0; i < concretes.size(); ++i) { - if (i != 0) { - costEstimateMethodDispatch += remainder; - } - remainder -= concretesProbabilities.get(i); - } - - double costEstimateTypeDispatch = 0.0; - remainder = 1.0; - for (int i = 0; i < ptypes.size(); ++i) { - if (i != 0) { - costEstimateTypeDispatch += remainder; - } - remainder -= ptypes.get(i).getProbability(); - } - costEstimateTypeDispatch += notRecordedTypeProbability; - return costEstimateMethodDispatch < costEstimateTypeDispatch; - } - - private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, - boolean useForInlining) { - Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining); - BeginNode calleeEntryNode = graph.add(new BeginNode()); - calleeEntryNode.setNext(duplicatedInvoke.asNode()); - - AbstractEndNode endNode = graph.add(new EndNode()); - duplicatedInvoke.setNext(endNode); - returnMerge.addForwardEnd(endNode); - - if (returnValuePhi != null) { - returnValuePhi.addInput(duplicatedInvoke.asNode()); - } - return calleeEntryNode; - } - - private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining) { - Invoke result = (Invoke) invoke.asNode().copyWithInputs(); - Node callTarget = result.callTarget().copyWithInputs(); - result.asNode().replaceFirstInput(result.callTarget(), callTarget); - result.setUseForInlining(useForInlining); - - Kind kind = invoke.asNode().getKind(); - if (kind != Kind.Void) { - FrameState stateAfter = invoke.stateAfter(); - stateAfter = stateAfter.duplicate(stateAfter.bci); - stateAfter.replaceFirstInput(invoke.asNode(), result.asNode()); - result.setStateAfter(stateAfter); - } - - if (invoke instanceof InvokeWithExceptionNode) { - assert exceptionMerge != null && exceptionObjectPhi != null; - - InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; - ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge(); - FrameState stateAfterException = exceptionEdge.stateAfter(); - - ExceptionObjectNode newExceptionEdge = (ExceptionObjectNode) exceptionEdge.copyWithInputs(); - // set new state (pop old exception object, push new one) - newExceptionEdge.setStateAfter(stateAfterException.duplicateModified(stateAfterException.bci, stateAfterException.rethrowException(), Kind.Object, newExceptionEdge)); - - AbstractEndNode endNode = graph.add(new EndNode()); - newExceptionEdge.setNext(endNode); - exceptionMerge.addForwardEnd(endNode); - exceptionObjectPhi.addInput(newExceptionEdge); - - ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge); - } - return result; - } - - @Override - public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { - if (hasSingleMethod()) { - devirtualizeWithTypeSwitch(graph(), InvokeKind.Special, concretes.get(0), metaAccess); - } else { - tryToDevirtualizeMultipleMethods(graph(), metaAccess); - } - } - - private void tryToDevirtualizeMultipleMethods(StructuredGraph graph, MetaAccessProvider metaAccess) { - MethodCallTargetNode methodCallTarget = (MethodCallTargetNode) invoke.callTarget(); - if (methodCallTarget.invokeKind() == InvokeKind.Interface) { - ResolvedJavaMethod targetMethod = methodCallTarget.targetMethod(); - ResolvedJavaType leastCommonType = getLeastCommonType(); - // check if we have a common base type that implements the interface -> in that case - // we have a vtable entry for the interface method and can use a less expensive - // virtual call - if (!leastCommonType.isInterface() && targetMethod.getDeclaringClass().isAssignableFrom(leastCommonType)) { - ResolvedJavaMethod baseClassTargetMethod = leastCommonType.resolveMethod(targetMethod); - if (baseClassTargetMethod != null) { - devirtualizeWithTypeSwitch(graph, InvokeKind.Virtual, leastCommonType.resolveMethod(targetMethod), metaAccess); - } - } - } - } - - private void devirtualizeWithTypeSwitch(StructuredGraph graph, InvokeKind kind, ResolvedJavaMethod target, MetaAccessProvider metaAccess) { - BeginNode invocationEntry = graph.add(new BeginNode()); - BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph); - BeginNode[] successors = new BeginNode[]{invocationEntry, unknownTypeSux}; - createDispatchOnTypeBeforeInvoke(graph, successors, true, metaAccess); - - invocationEntry.setNext(invoke.asNode()); - ValueNode receiver = ((MethodCallTargetNode) invoke.callTarget()).receiver(); - GuardedValueNode anchoredReceiver = createAnchoredReceiver(graph, invocationEntry, target.getDeclaringClass(), receiver, false); - invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver); - replaceInvokeCallTarget(invoke, graph, kind, target); - } - - private static BeginNode createUnknownTypeSuccessor(StructuredGraph graph) { - return BeginNode.begin(graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated))); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(shouldFallbackToInvoke() ? "megamorphic" : "polymorphic"); - builder.append(", "); - builder.append(concretes.size()); - builder.append(" methods [ "); - for (int i = 0; i < concretes.size(); i++) { - builder.append(MetaUtil.format(" %H.%n(%p):%r", concretes.get(i))); - } - builder.append(" ], "); - builder.append(ptypes.size()); - builder.append(" type checks [ "); - for (int i = 0; i < ptypes.size(); i++) { - builder.append(" "); - builder.append(ptypes.get(i).getType().getName()); - builder.append(ptypes.get(i).getProbability()); - } - builder.append(" ]"); - 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 { - - private final Assumption takenAssumption; - - public AssumptionInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, Assumption takenAssumption) { - super(invoke, concrete); - this.takenAssumption = takenAssumption; - } - - @Override - public void inline(Providers providers, Assumptions assumptions) { - assumptions.record(takenAssumption); - super.inline(providers, assumptions); - } - - @Override - public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { - assumptions.record(takenAssumption); - replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete); - } - - @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 - * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke - */ - public static InlineInfo getInlineInfo(InliningData data, Invoke invoke, int maxNumberOfMethods, Replacements replacements, Assumptions assumptions, OptimisticOptimizations optimisticOpts) { - if (!checkInvokeConditions(invoke)) { - return null; - } - MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); - ResolvedJavaMethod targetMethod = callTarget.targetMethod(); - - if (callTarget.invokeKind() == InvokeKind.Special || targetMethod.canBeStaticallyBound()) { - return getExactInlineInfo(data, invoke, replacements, optimisticOpts, targetMethod); - } - - assert callTarget.invokeKind() == InvokeKind.Virtual || callTarget.invokeKind() == InvokeKind.Interface; - - ResolvedJavaType holder = targetMethod.getDeclaringClass(); - if (!(callTarget.receiver().stamp() instanceof ObjectStamp)) { - return null; - } - ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp(); - if (receiverStamp.alwaysNull()) { - // Don't inline if receiver is known to be null - return null; - } - if (receiverStamp.type() != null) { - // the invoke target might be more specific than the holder (happens after inlining: - // parameters lose their declared type...) - ResolvedJavaType receiverType = receiverStamp.type(); - if (receiverType != null && holder.isAssignableFrom(receiverType)) { - holder = receiverType; - if (receiverStamp.isExactType()) { - assert targetMethod.getDeclaringClass().isAssignableFrom(holder) : holder + " subtype of " + targetMethod.getDeclaringClass() + " for " + targetMethod; - ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod); - if (resolvedMethod != null) { - return getExactInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod); - } - } - } - } - - if (holder.isArray()) { - // arrays can be treated as Objects - ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod); - if (resolvedMethod != null) { - return getExactInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod); - } - } - - if (assumptions.useOptimisticAssumptions()) { - ResolvedJavaType uniqueSubtype = holder.findUniqueConcreteSubtype(); - if (uniqueSubtype != null) { - ResolvedJavaMethod resolvedMethod = uniqueSubtype.resolveMethod(targetMethod); - if (resolvedMethod != null) { - return getAssumptionInlineInfo(data, invoke, replacements, optimisticOpts, resolvedMethod, new Assumptions.ConcreteSubtype(holder, uniqueSubtype)); - } - } - - ResolvedJavaMethod concrete = holder.findUniqueConcreteMethod(targetMethod); - if (concrete != null) { - return getAssumptionInlineInfo(data, invoke, replacements, optimisticOpts, concrete, new Assumptions.ConcreteMethod(targetMethod, holder, concrete)); - } - } - - // type check based inlining - return getTypeCheckedInlineInfo(data, invoke, maxNumberOfMethods, replacements, targetMethod, optimisticOpts); - } - - private static InlineInfo getAssumptionInlineInfo(InliningData data, Invoke invoke, Replacements replacements, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod concrete, - Assumption takenAssumption) { - assert !concrete.isAbstract(); - if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) { - return null; - } - return new AssumptionInlineInfo(invoke, concrete, takenAssumption); - } - - private static InlineInfo getExactInlineInfo(InliningData data, Invoke invoke, Replacements replacements, OptimisticOptimizations optimisticOpts, ResolvedJavaMethod targetMethod) { - assert !targetMethod.isAbstract(); - if (!checkTargetConditions(data, replacements, invoke, targetMethod, optimisticOpts)) { - return null; - } - return new ExactInlineInfo(invoke, targetMethod); - } - - private static InlineInfo getTypeCheckedInlineInfo(InliningData data, Invoke invoke, int maxNumberOfMethods, Replacements replacements, ResolvedJavaMethod targetMethod, - OptimisticOptimizations optimisticOpts) { - JavaTypeProfile typeProfile; - ValueNode receiver = invoke.callTarget().arguments().get(0); - if (receiver instanceof TypeProfileProxyNode) { - TypeProfileProxyNode typeProfileProxyNode = (TypeProfileProxyNode) receiver; - typeProfile = typeProfileProxyNode.getProfile(); - } else { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no type profile exists"); - } - - ProfiledType[] ptypes = typeProfile.getTypes(); - if (ptypes == null || ptypes.length <= 0) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no types in profile"); - } - - double notRecordedTypeProbability = typeProfile.getNotRecordedProbability(); - if (ptypes.length == 1 && notRecordedTypeProbability == 0) { - if (!optimisticOpts.inlineMonomorphicCalls()) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining monomorphic calls is disabled"); - } - - ResolvedJavaType type = ptypes[0].getType(); - assert type.isArray() || !type.isAbstract(); - ResolvedJavaMethod concrete = type.resolveMethod(targetMethod); - if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) { - return null; - } - return new TypeGuardInlineInfo(invoke, concrete, type); - } else { - invoke.setPolymorphic(true); - - if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length); - } - if (!optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) { - // due to filtering impossible types, notRecordedTypeProbability can be > 0 although - // the number of types is lower than what can be recorded in a type profile - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length, - notRecordedTypeProbability * 100); - } - - // Find unique methods and their probabilities. - ArrayList concreteMethods = new ArrayList<>(); - ArrayList concreteMethodsProbabilities = new ArrayList<>(); - for (int i = 0; i < ptypes.length; i++) { - ResolvedJavaMethod concrete = ptypes[i].getType().resolveMethod(targetMethod); - if (concrete == null) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "could not resolve method"); - } - int index = concreteMethods.indexOf(concrete); - double curProbability = ptypes[i].getProbability(); - if (index < 0) { - index = concreteMethods.size(); - concreteMethods.add(concrete); - concreteMethodsProbabilities.add(curProbability); - } else { - concreteMethodsProbabilities.set(index, concreteMethodsProbabilities.get(index) + curProbability); - } - } - - if (concreteMethods.size() > maxNumberOfMethods) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "polymorphic call with more than %d target methods", maxNumberOfMethods); - } - - // Clear methods that fall below the threshold. - if (notRecordedTypeProbability > 0) { - ArrayList newConcreteMethods = new ArrayList<>(); - ArrayList newConcreteMethodsProbabilities = new ArrayList<>(); - for (int i = 0; i < concreteMethods.size(); ++i) { - if (concreteMethodsProbabilities.get(i) >= MegamorphicInliningMinMethodProbability.getValue()) { - newConcreteMethods.add(concreteMethods.get(i)); - newConcreteMethodsProbabilities.add(concreteMethodsProbabilities.get(i)); - } - } - - if (newConcreteMethods.size() == 0) { - // No method left that is worth inlining. - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)", - concreteMethods.size()); - } - - concreteMethods = newConcreteMethods; - concreteMethodsProbabilities = newConcreteMethodsProbabilities; - } - - // Clean out types whose methods are no longer available. - ArrayList usedTypes = new ArrayList<>(); - ArrayList typesToConcretes = new ArrayList<>(); - for (ProfiledType type : ptypes) { - ResolvedJavaMethod concrete = type.getType().resolveMethod(targetMethod); - int index = concreteMethods.indexOf(concrete); - if (index == -1) { - notRecordedTypeProbability += type.getProbability(); - } else { - assert type.getType().isArray() || !type.getType().isAbstract() : type + " " + concrete; - usedTypes.add(type); - typesToConcretes.add(index); - } - } - - if (usedTypes.size() == 0) { - // No type left that is worth checking for. - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length); - } - - for (ResolvedJavaMethod concrete : concreteMethods) { - if (!checkTargetConditions(data, replacements, invoke, concrete, optimisticOpts)) { - return logNotInlinedMethodAndReturnNull(invoke, data.inliningDepth(), targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined"); - } - } - return new MultiTypeGuardInlineInfo(invoke, concreteMethods, concreteMethodsProbabilities, usedTypes, typesToConcretes, notRecordedTypeProbability); - } - } - - private static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) { + public static GuardedValueNode createAnchoredReceiver(StructuredGraph graph, GuardingNode anchor, ResolvedJavaType commonType, ValueNode receiver, boolean exact) { return createAnchoredReceiver(graph, anchor, receiver, exact ? StampFactory.exactNonNull(commonType) : StampFactory.declaredNonNull(commonType)); } @@ -1252,44 +243,56 @@ return graph.unique(new GuardedValueNode(receiver, anchor, stamp)); } - // TODO (chaeubl): cleanup this method - private static boolean checkInvokeConditions(Invoke invoke) { + /** + * @return null iff the check succeeds, otherwise a (non-null) descriptive message. + */ + public static String checkInvokeConditions(Invoke invoke) { if (invoke.predecessor() == null || !invoke.asNode().isAlive()) { - return logNotInlinedMethod(invoke, "the invoke is dead code"); - } else if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { - return logNotInlinedMethod(invoke, "the invoke has already been lowered, or has been created as a low-level node"); - } else if (((MethodCallTargetNode) invoke.callTarget()).targetMethod() == null) { - return logNotInlinedMethod(invoke, "target method is null"); - } else if (invoke.stateAfter() == null) { + return "the invoke is dead code"; + } + if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { + return "the invoke has already been lowered, or has been created as a low-level node"; + } + MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); + if (callTarget.targetMethod() == null) { + return "target method is null"; + } + if (invoke.stateAfter() == null) { // TODO (chaeubl): why should an invoke not have a state after? - return logNotInlinedMethod(invoke, "the invoke has no after state"); - } else if (!invoke.useForInlining()) { - return logNotInlinedMethod(invoke, "the invoke is marked to be not used for inlining"); - } else if (((MethodCallTargetNode) invoke.callTarget()).receiver() != null && ((MethodCallTargetNode) invoke.callTarget()).receiver().isConstant() && - ((MethodCallTargetNode) invoke.callTarget()).receiver().asConstant().isNull()) { - return logNotInlinedMethod(invoke, "receiver is null"); - } else { - return true; + return "the invoke has no after state"; + } + if (!invoke.useForInlining()) { + return "the invoke is marked to be not used for inlining"; } + ValueNode receiver = callTarget.receiver(); + if (receiver != null && receiver.isConstant() && receiver.asConstant().isNull()) { + return "receiver is null"; + } + return null; } - private static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) { + public static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) { + String failureMessage = null; if (method == null) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method is not resolved"); + failureMessage = "the method is not resolved"; } else if (method.isNative() && (!Intrinsify.getValue() || !InliningUtil.canIntrinsify(replacements, method))) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is a non-intrinsic native method"); + failureMessage = "it is a non-intrinsic native method"; } else if (method.isAbstract()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is an abstract method"); + failureMessage = "it is an abstract method"; } else if (!method.getDeclaringClass().isInitialized()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method's class is not initialized"); + failureMessage = "the method's class is not initialized"; } else if (!method.canBeInlined()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is marked non-inlinable"); + failureMessage = "it is marked non-inlinable"; } else if (data.countRecursiveInlining(method) > MaximumRecursiveInlining.getValue()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it exceeds the maximum recursive inlining depth"); + failureMessage = "it exceeds the maximum recursive inlining depth"; } else if (new OptimisticOptimizations(method.getProfilingInfo()).lessOptimisticThan(optimisticOpts)) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the callee uses less optimistic optimizations than caller"); + failureMessage = "the callee uses less optimistic optimizations than caller"; + } + if (failureMessage == null) { + return true; } else { - return true; + logNotInlined(invoke, data.inliningDepth(), method, failureMessage); + return false; } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/AbstractInlineInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/AbstractInlineInfo.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2013, 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.inlining.info; + +import com.oracle.graal.api.code.Assumptions; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.FixedWithNextNode; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.phases.common.inlining.InliningUtil; + +import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable; +import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableMacroNode; +import com.oracle.graal.phases.common.inlining.InliningUtil.InlineableGraph; + +public abstract class AbstractInlineInfo implements InlineInfo { + + protected final Invoke invoke; + + public AbstractInlineInfo(Invoke invoke) { + this.invoke = invoke; + } + + @Override + public StructuredGraph graph() { + return invoke.asNode().graph(); + } + + @Override + public Invoke invoke() { + return invoke; + } + + protected static void inline(Invoke invoke, ResolvedJavaMethod concrete, Inlineable inlineable, Assumptions assumptions, boolean receiverNullCheck) { + if (inlineable instanceof InlineableGraph) { + StructuredGraph calleeGraph = ((InlineableGraph) inlineable).getGraph(); + InliningUtil.inline(invoke, calleeGraph, receiverNullCheck); + } else { + assert inlineable instanceof InlineableMacroNode; + + Class macroNodeClass = ((InlineableMacroNode) inlineable).getMacroNodeClass(); + InliningUtil.inlineMacroNode(invoke, concrete, macroNodeClass); + } + + InliningUtil.InlinedBytecodes.add(concrete.getCodeSize()); + assumptions.recordMethodContents(concrete); + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/AssumptionInlineInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/AssumptionInlineInfo.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2013, 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.inlining.info; + +import com.oracle.graal.api.code.Assumptions; +import com.oracle.graal.api.meta.MetaAccessProvider; +import com.oracle.graal.api.meta.MetaUtil; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.util.Providers; +import com.oracle.graal.api.code.Assumptions.Assumption; +import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; + +/** + * 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. + */ +public class AssumptionInlineInfo extends ExactInlineInfo { + + private final Assumption takenAssumption; + + public AssumptionInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, Assumption takenAssumption) { + super(invoke, concrete); + this.takenAssumption = takenAssumption; + } + + @Override + public void inline(Providers providers, Assumptions assumptions) { + assumptions.record(takenAssumption); + super.inline(providers, assumptions); + } + + @Override + public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { + assumptions.record(takenAssumption); + InliningUtil.replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete); + } + + @Override + public String toString() { + return "assumption " + MetaUtil.format("%H.%n(%p):%r", concrete); + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/ExactInlineInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/ExactInlineInfo.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2013, 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.inlining.info; + +import com.oracle.graal.api.code.Assumptions; +import com.oracle.graal.api.meta.MetaAccessProvider; +import com.oracle.graal.api.meta.MetaUtil; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.phases.util.Providers; +import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable; + +/** + * Represents an inlining opportunity where the compiler can statically determine a monomorphic + * target method and therefore is able to determine the called method exactly. + */ +public class ExactInlineInfo extends AbstractInlineInfo { + + protected final ResolvedJavaMethod concrete; + private Inlineable inlineableElement; + private boolean suppressNullCheck; + + public ExactInlineInfo(Invoke invoke, ResolvedJavaMethod concrete) { + super(invoke); + this.concrete = concrete; + assert concrete != null; + } + + public void suppressNullCheck() { + suppressNullCheck = true; + } + + @Override + public void inline(Providers providers, Assumptions assumptions) { + inline(invoke, concrete, inlineableElement, assumptions, !suppressNullCheck); + } + + @Override + public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { + // nothing todo, can already be bound statically + } + + @Override + public int numberOfMethods() { + return 1; + } + + @Override + public ResolvedJavaMethod methodAt(int index) { + assert index == 0; + return concrete; + } + + @Override + public double probabilityAt(int index) { + assert index == 0; + return 1.0; + } + + @Override + public double relevanceAt(int index) { + assert index == 0; + return 1.0; + } + + @Override + public String toString() { + return "exact " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + + @Override + public Inlineable inlineableElementAt(int index) { + assert index == 0; + return inlineableElement; + } + + @Override + public void setInlinableElement(int index, Inlineable inlineableElement) { + assert index == 0; + this.inlineableElement = inlineableElement; + } + + public boolean shouldInline() { + return concrete.shouldBeInlined(); + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/InlineInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/InlineInfo.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2013, 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.inlining.info; + +import com.oracle.graal.api.code.Assumptions; +import com.oracle.graal.api.meta.MetaAccessProvider; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.util.Providers; + +/** + * Represents an opportunity for inlining at a 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 { + + /** + * The graph containing the {@link #invoke() invocation} that may be inlined. + */ + StructuredGraph graph(); + + /** + * The invocation that may be inlined. + */ + Invoke invoke(); + + /** + * Returns the number of methods that may be inlined by the {@link #invoke() invocation}. This + * may be more than one in the case of a invocation profile showing a number of "hot" concrete + * methods dispatched to by the invocation. + */ + int numberOfMethods(); + + ResolvedJavaMethod methodAt(int index); + + InliningUtil.Inlineable inlineableElementAt(int index); + + double probabilityAt(int index); + + double relevanceAt(int index); + + void setInlinableElement(int index, InliningUtil.Inlineable inlineableElement); + + /** + * 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(Providers providers, Assumptions assumptions); + + /** + * Try to make the call static bindable to avoid interface and virtual method calls. + */ + void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions); + + boolean shouldInline(); +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/MultiTypeGuardInlineInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/MultiTypeGuardInlineInfo.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,551 @@ +/* + * Copyright (c) 2013, 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.inlining.info; + +import static com.oracle.graal.compiler.common.GraalOptions.*; + +import java.util.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.api.meta.JavaTypeProfile.ProfiledType; +import com.oracle.graal.compiler.common.calc.*; +import com.oracle.graal.compiler.common.type.*; +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.util.*; +import com.oracle.graal.phases.common.*; +import com.oracle.graal.phases.common.inlining.*; +import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable; +import com.oracle.graal.phases.tiers.*; +import com.oracle.graal.phases.util.*; + +/** + * Polymorphic inlining of m methods with n type checks (n ≥ m) in case that the profiling + * information suggests a reasonable amount of different receiver types and different methods. If an + * unknown type is encountered a deoptimization is triggered. + */ +public class MultiTypeGuardInlineInfo extends AbstractInlineInfo { + + private static final DebugMetric metricInliningTailDuplication = Debug.metric("InliningTailDuplication"); + + private final List concretes; + private final double[] methodProbabilities; + private final double maximumMethodProbability; + private final ArrayList typesToConcretes; + private final ArrayList ptypes; + private final ArrayList concretesProbabilities; + private final double notRecordedTypeProbability; + private final Inlineable[] inlineableElements; + + public MultiTypeGuardInlineInfo(Invoke invoke, ArrayList concretes, ArrayList concretesProbabilities, ArrayList ptypes, + ArrayList typesToConcretes, double notRecordedTypeProbability) { + super(invoke); + assert concretes.size() > 0 : "must have at least one method"; + assert ptypes.size() == typesToConcretes.size() : "array lengths must match"; + + this.concretesProbabilities = concretesProbabilities; + this.concretes = concretes; + this.ptypes = ptypes; + this.typesToConcretes = typesToConcretes; + this.notRecordedTypeProbability = notRecordedTypeProbability; + this.inlineableElements = new Inlineable[concretes.size()]; + this.methodProbabilities = computeMethodProbabilities(); + this.maximumMethodProbability = maximumMethodProbability(); + assert maximumMethodProbability > 0; + } + + private double[] computeMethodProbabilities() { + double[] result = new double[concretes.size()]; + for (int i = 0; i < typesToConcretes.size(); i++) { + int concrete = typesToConcretes.get(i); + double probability = ptypes.get(i).getProbability(); + result[concrete] += probability; + } + return result; + } + + private double maximumMethodProbability() { + double max = 0; + for (int i = 0; i < methodProbabilities.length; i++) { + max = Math.max(max, methodProbabilities[i]); + } + return max; + } + + @Override + public int numberOfMethods() { + return concretes.size(); + } + + @Override + public ResolvedJavaMethod methodAt(int index) { + assert index >= 0 && index < concretes.size(); + return concretes.get(index); + } + + @Override + public Inlineable inlineableElementAt(int index) { + assert index >= 0 && index < concretes.size(); + return inlineableElements[index]; + } + + @Override + public double probabilityAt(int index) { + return methodProbabilities[index]; + } + + @Override + public double relevanceAt(int index) { + return probabilityAt(index) / maximumMethodProbability; + } + + @Override + public void setInlinableElement(int index, Inlineable inlineableElement) { + assert index >= 0 && index < concretes.size(); + inlineableElements[index] = inlineableElement; + } + + @Override + public void inline(Providers providers, Assumptions assumptions) { + if (hasSingleMethod()) { + inlineSingleMethod(graph(), providers.getMetaAccess(), assumptions); + } else { + inlineMultipleMethods(graph(), providers, assumptions); + } + } + + public boolean shouldInline() { + for (ResolvedJavaMethod method : concretes) { + if (method.shouldBeInlined()) { + return true; + } + } + return false; + } + + private boolean hasSingleMethod() { + return concretes.size() == 1 && !shouldFallbackToInvoke(); + } + + private boolean shouldFallbackToInvoke() { + return notRecordedTypeProbability > 0; + } + + private void inlineMultipleMethods(StructuredGraph graph, Providers providers, Assumptions assumptions) { + int numberOfMethods = concretes.size(); + FixedNode continuation = invoke.next(); + + ValueNode originalReceiver = ((MethodCallTargetNode) invoke.callTarget()).receiver(); + // setup merge and phi nodes for results and exceptions + MergeNode returnMerge = graph.add(new MergeNode()); + returnMerge.setStateAfter(invoke.stateAfter()); + + PhiNode returnValuePhi = null; + if (invoke.asNode().getKind() != Kind.Void) { + returnValuePhi = graph.addWithoutUnique(new ValuePhiNode(invoke.asNode().stamp().unrestricted(), returnMerge)); + } + + MergeNode exceptionMerge = null; + PhiNode exceptionObjectPhi = null; + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge(); + + exceptionMerge = graph.add(new MergeNode()); + + FixedNode exceptionSux = exceptionEdge.next(); + graph.addBeforeFixed(exceptionSux, exceptionMerge); + exceptionObjectPhi = graph.addWithoutUnique(new ValuePhiNode(StampFactory.forKind(Kind.Object), exceptionMerge)); + exceptionMerge.setStateAfter(exceptionEdge.stateAfter().duplicateModified(invoke.stateAfter().bci, true, Kind.Object, exceptionObjectPhi)); + } + + // create one separate block for each invoked method + BeginNode[] successors = new BeginNode[numberOfMethods + 1]; + for (int i = 0; i < numberOfMethods; i++) { + successors[i] = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, true); + } + + // create the successor for an unknown type + FixedNode unknownTypeSux; + if (shouldFallbackToInvoke()) { + unknownTypeSux = createInvocationBlock(graph, invoke, returnMerge, returnValuePhi, exceptionMerge, exceptionObjectPhi, false); + } else { + unknownTypeSux = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated)); + } + successors[successors.length - 1] = BeginNode.begin(unknownTypeSux); + + // replace the invoke exception edge + if (invoke instanceof InvokeWithExceptionNode) { + InvokeWithExceptionNode invokeWithExceptionNode = (InvokeWithExceptionNode) invoke; + ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithExceptionNode.exceptionEdge(); + exceptionEdge.replaceAtUsages(exceptionObjectPhi); + exceptionEdge.setNext(null); + GraphUtil.killCFG(invokeWithExceptionNode.exceptionEdge()); + } + + assert invoke.asNode().isAlive(); + + // replace the invoke with a switch on the type of the actual receiver + boolean methodDispatch = createDispatchOnTypeBeforeInvoke(graph, successors, false, providers.getMetaAccess()); + + assert invoke.next() == continuation; + invoke.setNext(null); + returnMerge.setNext(continuation); + invoke.asNode().replaceAtUsages(returnValuePhi); + invoke.asNode().replaceAndDelete(null); + + ArrayList replacementNodes = 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; + if (methodDispatch) { + commonType = concretes.get(i).getDeclaringClass(); + } else { + commonType = getLeastCommonType(i); + } + + ValueNode receiver = ((MethodCallTargetNode) invokeForInlining.callTarget()).receiver(); + boolean exact = (getTypeCount(i) == 1 && !methodDispatch); + GuardedValueNode anchoredReceiver = InliningUtil.createAnchoredReceiver(graph, node, commonType, receiver, exact); + invokeForInlining.callTarget().replaceFirstInput(receiver, anchoredReceiver); + + inline(invokeForInlining, methodAt(i), inlineableElementAt(i), assumptions, false); + + replacementNodes.add(anchoredReceiver); + } + if (shouldFallbackToInvoke()) { + replacementNodes.add(null); + } + + if (OptTailDuplication.getValue()) { + /* + * 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).callTarget() instanceof MethodCallTargetNode && + ((MethodCallTargetNode) ((InvokeNode) current).callTarget()).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); + PhaseContext phaseContext = new PhaseContext(providers, assumptions); + CanonicalizerPhase canonicalizer = new CanonicalizerPhase(!ImmutableCode.getValue()); + TailDuplicationPhase.tailDuplicate(returnMerge, TailDuplicationPhase.TRUE_DECISION, replacementNodes, phaseContext, canonicalizer); + } + } + } + + private int getTypeCount(int concreteMethodIndex) { + int count = 0; + for (int i = 0; i < typesToConcretes.size(); i++) { + if (typesToConcretes.get(i) == concreteMethodIndex) { + count++; + } + } + return count; + } + + private ResolvedJavaType getLeastCommonType(int concreteMethodIndex) { + ResolvedJavaType commonType = null; + for (int i = 0; i < typesToConcretes.size(); i++) { + if (typesToConcretes.get(i) == concreteMethodIndex) { + if (commonType == null) { + commonType = ptypes.get(i).getType(); + } else { + commonType = commonType.findLeastCommonAncestor(ptypes.get(i).getType()); + } + } + } + assert commonType != null; + return commonType; + } + + private ResolvedJavaType getLeastCommonType() { + ResolvedJavaType result = getLeastCommonType(0); + for (int i = 1; i < concretes.size(); i++) { + result = result.findLeastCommonAncestor(getLeastCommonType(i)); + } + return result; + } + + private void inlineSingleMethod(StructuredGraph graph, MetaAccessProvider metaAccess, Assumptions assumptions) { + assert concretes.size() == 1 && inlineableElements.length == 1 && ptypes.size() > 1 && !shouldFallbackToInvoke() && notRecordedTypeProbability == 0; + + BeginNode calleeEntryNode = graph.add(new BeginNode()); + + BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph); + BeginNode[] successors = new BeginNode[]{calleeEntryNode, unknownTypeSux}; + createDispatchOnTypeBeforeInvoke(graph, successors, false, metaAccess); + + calleeEntryNode.setNext(invoke.asNode()); + + inline(invoke, methodAt(0), inlineableElementAt(0), assumptions, false); + } + + private boolean createDispatchOnTypeBeforeInvoke(StructuredGraph graph, BeginNode[] successors, boolean invokeIsOnlySuccessor, MetaAccessProvider metaAccess) { + assert ptypes.size() >= 1; + ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke); + Kind hubKind = ((MethodCallTargetNode) invoke.callTarget()).targetMethod().getDeclaringClass().getEncoding(ResolvedJavaType.Representation.ObjectHub).getKind(); + LoadHubNode hub = graph.unique(new LoadHubNode(nonNullReceiver, hubKind)); + + if (!invokeIsOnlySuccessor && chooseMethodDispatch()) { + assert successors.length == concretes.size() + 1; + assert concretes.size() > 0; + Debug.log("Method check cascade with %d methods", concretes.size()); + + ValueNode[] constantMethods = new ValueNode[concretes.size()]; + double[] probability = new double[concretes.size()]; + for (int i = 0; i < concretes.size(); ++i) { + ResolvedJavaMethod firstMethod = concretes.get(i); + Constant firstMethodConstant = firstMethod.getEncoding(); + + ValueNode firstMethodConstantNode = ConstantNode.forConstant(firstMethodConstant, metaAccess, graph); + constantMethods[i] = firstMethodConstantNode; + double concretesProbability = concretesProbabilities.get(i); + assert concretesProbability >= 0.0; + probability[i] = concretesProbability; + if (i > 0) { + double prevProbability = probability[i - 1]; + if (prevProbability == 1.0) { + probability[i] = 1.0; + } else { + probability[i] = Math.min(1.0, Math.max(0.0, probability[i] / (1.0 - prevProbability))); + } + } + } + + ResolvedJavaType receiverType = invoke.getReceiverType(); + FixedNode lastSucc = successors[concretes.size()]; + for (int i = concretes.size() - 1; i >= 0; --i) { + LoadMethodNode method = graph.add(new LoadMethodNode(concretes.get(i), receiverType, hub, constantMethods[i].getKind())); + CompareNode methodCheck = CompareNode.createCompareNode(graph, Condition.EQ, method, constantMethods[i]); + IfNode ifNode = graph.add(new IfNode(methodCheck, successors[i], lastSucc, probability[i])); + method.setNext(ifNode); + lastSucc = method; + } + + FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor(); + pred.setNext(lastSucc); + return true; + } else { + Debug.log("Type switch with %d types", concretes.size()); + } + + ResolvedJavaType[] keys = new ResolvedJavaType[ptypes.size()]; + double[] keyProbabilities = new double[ptypes.size() + 1]; + int[] keySuccessors = new int[ptypes.size() + 1]; + for (int i = 0; i < ptypes.size(); i++) { + keys[i] = ptypes.get(i).getType(); + keyProbabilities[i] = ptypes.get(i).getProbability(); + keySuccessors[i] = invokeIsOnlySuccessor ? 0 : typesToConcretes.get(i); + assert keySuccessors[i] < successors.length - 1 : "last successor is the unknownTypeSux"; + } + keyProbabilities[keyProbabilities.length - 1] = notRecordedTypeProbability; + keySuccessors[keySuccessors.length - 1] = successors.length - 1; + + TypeSwitchNode typeSwitch = graph.add(new TypeSwitchNode(hub, successors, keys, keyProbabilities, keySuccessors)); + FixedWithNextNode pred = (FixedWithNextNode) invoke.asNode().predecessor(); + pred.setNext(typeSwitch); + return false; + } + + private boolean chooseMethodDispatch() { + ResolvedJavaType receiverType = invoke.getReceiverType(); + for (ResolvedJavaMethod concrete : concretes) { + if (!concrete.isInVirtualMethodTable(receiverType)) { + return false; + } + } + + if (concretes.size() == 1 && this.notRecordedTypeProbability > 0) { + // Always chose method dispatch if there is a single concrete method and the call + // site is megamorphic. + return true; + } + + if (concretes.size() == ptypes.size()) { + // Always prefer types over methods if the number of types is smaller than the + // number of methods. + return false; + } + + return chooseMethodDispatchCostBased(); + } + + private boolean chooseMethodDispatchCostBased() { + double remainder = 1.0 - this.notRecordedTypeProbability; + double costEstimateMethodDispatch = remainder; + for (int i = 0; i < concretes.size(); ++i) { + if (i != 0) { + costEstimateMethodDispatch += remainder; + } + remainder -= concretesProbabilities.get(i); + } + + double costEstimateTypeDispatch = 0.0; + remainder = 1.0; + for (int i = 0; i < ptypes.size(); ++i) { + if (i != 0) { + costEstimateTypeDispatch += remainder; + } + remainder -= ptypes.get(i).getProbability(); + } + costEstimateTypeDispatch += notRecordedTypeProbability; + return costEstimateMethodDispatch < costEstimateTypeDispatch; + } + + private static BeginNode createInvocationBlock(StructuredGraph graph, Invoke invoke, MergeNode returnMerge, PhiNode returnValuePhi, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, + boolean useForInlining) { + Invoke duplicatedInvoke = duplicateInvokeForInlining(graph, invoke, exceptionMerge, exceptionObjectPhi, useForInlining); + BeginNode calleeEntryNode = graph.add(new BeginNode()); + calleeEntryNode.setNext(duplicatedInvoke.asNode()); + + AbstractEndNode endNode = graph.add(new EndNode()); + duplicatedInvoke.setNext(endNode); + returnMerge.addForwardEnd(endNode); + + if (returnValuePhi != null) { + returnValuePhi.addInput(duplicatedInvoke.asNode()); + } + return calleeEntryNode; + } + + private static Invoke duplicateInvokeForInlining(StructuredGraph graph, Invoke invoke, MergeNode exceptionMerge, PhiNode exceptionObjectPhi, boolean useForInlining) { + Invoke result = (Invoke) invoke.asNode().copyWithInputs(); + Node callTarget = result.callTarget().copyWithInputs(); + result.asNode().replaceFirstInput(result.callTarget(), callTarget); + result.setUseForInlining(useForInlining); + + Kind kind = invoke.asNode().getKind(); + if (kind != Kind.Void) { + FrameState stateAfter = invoke.stateAfter(); + stateAfter = stateAfter.duplicate(stateAfter.bci); + stateAfter.replaceFirstInput(invoke.asNode(), result.asNode()); + result.setStateAfter(stateAfter); + } + + if (invoke instanceof InvokeWithExceptionNode) { + assert exceptionMerge != null && exceptionObjectPhi != null; + + InvokeWithExceptionNode invokeWithException = (InvokeWithExceptionNode) invoke; + ExceptionObjectNode exceptionEdge = (ExceptionObjectNode) invokeWithException.exceptionEdge(); + FrameState stateAfterException = exceptionEdge.stateAfter(); + + ExceptionObjectNode newExceptionEdge = (ExceptionObjectNode) exceptionEdge.copyWithInputs(); + // set new state (pop old exception object, push new one) + newExceptionEdge.setStateAfter(stateAfterException.duplicateModified(stateAfterException.bci, stateAfterException.rethrowException(), Kind.Object, newExceptionEdge)); + + AbstractEndNode endNode = graph.add(new EndNode()); + newExceptionEdge.setNext(endNode); + exceptionMerge.addForwardEnd(endNode); + exceptionObjectPhi.addInput(newExceptionEdge); + + ((InvokeWithExceptionNode) result).setExceptionEdge(newExceptionEdge); + } + return result; + } + + @Override + public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { + if (hasSingleMethod()) { + devirtualizeWithTypeSwitch(graph(), InvokeKind.Special, concretes.get(0), metaAccess); + } else { + tryToDevirtualizeMultipleMethods(graph(), metaAccess); + } + } + + private void tryToDevirtualizeMultipleMethods(StructuredGraph graph, MetaAccessProvider metaAccess) { + MethodCallTargetNode methodCallTarget = (MethodCallTargetNode) invoke.callTarget(); + if (methodCallTarget.invokeKind() == InvokeKind.Interface) { + ResolvedJavaMethod targetMethod = methodCallTarget.targetMethod(); + ResolvedJavaType leastCommonType = getLeastCommonType(); + ResolvedJavaType contextType = invoke.getContextType(); + // check if we have a common base type that implements the interface -> in that case + // we have a vtable entry for the interface method and can use a less expensive + // virtual call + if (!leastCommonType.isInterface() && targetMethod.getDeclaringClass().isAssignableFrom(leastCommonType)) { + ResolvedJavaMethod baseClassTargetMethod = leastCommonType.resolveMethod(targetMethod, contextType); + if (baseClassTargetMethod != null) { + devirtualizeWithTypeSwitch(graph, InvokeKind.Virtual, leastCommonType.resolveMethod(targetMethod, contextType), metaAccess); + } + } + } + } + + private void devirtualizeWithTypeSwitch(StructuredGraph graph, InvokeKind kind, ResolvedJavaMethod target, MetaAccessProvider metaAccess) { + BeginNode invocationEntry = graph.add(new BeginNode()); + BeginNode unknownTypeSux = createUnknownTypeSuccessor(graph); + BeginNode[] successors = new BeginNode[]{invocationEntry, unknownTypeSux}; + createDispatchOnTypeBeforeInvoke(graph, successors, true, metaAccess); + + invocationEntry.setNext(invoke.asNode()); + ValueNode receiver = ((MethodCallTargetNode) invoke.callTarget()).receiver(); + GuardedValueNode anchoredReceiver = InliningUtil.createAnchoredReceiver(graph, invocationEntry, target.getDeclaringClass(), receiver, false); + invoke.callTarget().replaceFirstInput(receiver, anchoredReceiver); + InliningUtil.replaceInvokeCallTarget(invoke, graph, kind, target); + } + + private static BeginNode createUnknownTypeSuccessor(StructuredGraph graph) { + return BeginNode.begin(graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.TypeCheckedInliningViolated))); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(shouldFallbackToInvoke() ? "megamorphic" : "polymorphic"); + builder.append(", "); + builder.append(concretes.size()); + builder.append(" methods [ "); + for (int i = 0; i < concretes.size(); i++) { + builder.append(MetaUtil.format(" %H.%n(%p):%r", concretes.get(i))); + } + builder.append(" ], "); + builder.append(ptypes.size()); + builder.append(" type checks [ "); + for (int i = 0; i < ptypes.size(); i++) { + builder.append(" "); + builder.append(ptypes.get(i).getType().getName()); + builder.append(ptypes.get(i).getProbability()); + } + builder.append(" ]"); + return builder.toString(); + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/TypeGuardInlineInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/TypeGuardInlineInfo.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2013, 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.inlining.info; + +import com.oracle.graal.api.code.Assumptions; +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.calc.Condition; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.CompareNode; +import com.oracle.graal.nodes.extended.LoadHubNode; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.util.Providers; +import com.oracle.graal.phases.common.inlining.InliningUtil.Inlineable; +import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind; + +/** + * 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. + */ +public class TypeGuardInlineInfo extends AbstractInlineInfo { + + private final ResolvedJavaMethod concrete; + private final ResolvedJavaType type; + private Inlineable inlineableElement; + + public TypeGuardInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, ResolvedJavaType type) { + super(invoke); + this.concrete = concrete; + this.type = type; + assert type.isArray() || !type.isAbstract() : type; + } + + @Override + public int numberOfMethods() { + return 1; + } + + @Override + public ResolvedJavaMethod methodAt(int index) { + assert index == 0; + return concrete; + } + + @Override + public Inlineable inlineableElementAt(int index) { + assert index == 0; + return inlineableElement; + } + + @Override + public double probabilityAt(int index) { + assert index == 0; + return 1.0; + } + + @Override + public double relevanceAt(int index) { + assert index == 0; + return 1.0; + } + + @Override + public void setInlinableElement(int index, Inlineable inlineableElement) { + assert index == 0; + this.inlineableElement = inlineableElement; + } + + @Override + public void inline(Providers providers, Assumptions assumptions) { + createGuard(graph(), providers.getMetaAccess()); + inline(invoke, concrete, inlineableElement, assumptions, false); + } + + @Override + public void tryToDevirtualizeInvoke(MetaAccessProvider metaAccess, Assumptions assumptions) { + createGuard(graph(), metaAccess); + InliningUtil.replaceInvokeCallTarget(invoke, graph(), InvokeKind.Special, concrete); + } + + private void createGuard(StructuredGraph graph, MetaAccessProvider metaAccess) { + ValueNode nonNullReceiver = InliningUtil.nonNullReceiver(invoke); + ConstantNode typeHub = ConstantNode.forConstant(type.getEncoding(ResolvedJavaType.Representation.ObjectHub), metaAccess, graph); + LoadHubNode receiverHub = graph.unique(new LoadHubNode(nonNullReceiver, typeHub.getKind())); + + CompareNode typeCheck = CompareNode.createCompareNode(graph, Condition.EQ, receiverHub, typeHub); + FixedGuardNode guard = graph.add(new FixedGuardNode(typeCheck, DeoptimizationReason.TypeCheckedInliningViolated, DeoptimizationAction.InvalidateReprofile)); + assert invoke.predecessor() != null; + + ValueNode anchoredReceiver = InliningUtil.createAnchoredReceiver(graph, guard, type, nonNullReceiver, true); + invoke.callTarget().replaceFirstInput(nonNullReceiver, anchoredReceiver); + + graph.addBeforeFixed(invoke.asNode(), guard); + } + + @Override + public String toString() { + return "type-checked with type " + type.getName() + " and method " + MetaUtil.format("%H.%n(%p):%r", concrete); + } + + public boolean shouldInline() { + return concrete.shouldBeInlined(); + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/AbstractInliningPolicy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/AbstractInliningPolicy.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,126 @@ +/* + * 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.phases.common.inlining.policy; + +import com.oracle.graal.api.meta.ProfilingInfo; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.spi.Replacements; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; + +import java.util.Map; +import java.util.function.ToDoubleFunction; + +import static com.oracle.graal.compiler.common.GraalOptions.RelevanceCapForInlining; +import static com.oracle.graal.phases.common.inlining.InliningPhase.Options.AlwaysInlineIntrinsics; + +public abstract class AbstractInliningPolicy implements InliningPolicy { + + protected final Map hints; + + public AbstractInliningPolicy(Map hints) { + this.hints = hints; + } + + protected double computeMaximumSize(double relevance, int configuredMaximum) { + double inlineRatio = Math.min(RelevanceCapForInlining.getValue(), relevance); + return configuredMaximum * inlineRatio; + } + + protected double getInliningBonus(InlineInfo info) { + if (hints != null && hints.containsKey(info.invoke())) { + return hints.get(info.invoke()); + } + return 1; + } + + protected boolean isIntrinsic(Replacements replacements, InlineInfo info) { + if (AlwaysInlineIntrinsics.getValue()) { + return onlyIntrinsics(replacements, info); + } else { + return onlyForcedIntrinsics(replacements, info); + } + } + + private static boolean onlyIntrinsics(Replacements replacements, InlineInfo info) { + for (int i = 0; i < info.numberOfMethods(); i++) { + if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { + return false; + } + } + return true; + } + + private static boolean onlyForcedIntrinsics(Replacements replacements, InlineInfo info) { + for (int i = 0; i < info.numberOfMethods(); i++) { + if (!InliningUtil.canIntrinsify(replacements, info.methodAt(i))) { + return false; + } + if (!replacements.isForcedSubstitution(info.methodAt(i))) { + return false; + } + } + return true; + } + + protected static int previousLowLevelGraphSize(InlineInfo info) { + int size = 0; + for (int i = 0; i < info.numberOfMethods(); i++) { + ResolvedJavaMethod m = info.methodAt(i); + ProfilingInfo profile = m.getProfilingInfo(); + int compiledGraphSize = profile.getCompilerIRSize(StructuredGraph.class); + if (compiledGraphSize > 0) { + size += compiledGraphSize; + } + } + return size; + } + + protected static int determineNodeCount(InlineInfo info) { + int nodes = 0; + for (int i = 0; i < info.numberOfMethods(); i++) { + InliningUtil.Inlineable elem = info.inlineableElementAt(i); + if (elem != null) { + nodes += elem.getNodeCount(); + } + } + return nodes; + } + + protected static double determineInvokeProbability(ToDoubleFunction probabilities, InlineInfo info) { + double invokeProbability = 0; + for (int i = 0; i < info.numberOfMethods(); i++) { + InliningUtil.Inlineable callee = info.inlineableElementAt(i); + Iterable invokes = callee.getInvokes(); + if (invokes.iterator().hasNext()) { + for (Invoke invoke : invokes) { + invokeProbability += probabilities.applyAsDouble(invoke.asNode()); + } + } + } + return invokeProbability; + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/GreedyInliningPolicy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/GreedyInliningPolicy.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,112 @@ +/* + * 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.phases.common.inlining.policy; + +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.DebugMetric; +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.spi.Replacements; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; + +import java.util.Map; +import java.util.function.ToDoubleFunction; + +import static com.oracle.graal.compiler.common.GraalOptions.*; + +public class GreedyInliningPolicy extends AbstractInliningPolicy { + + private static final DebugMetric metricInliningStoppedByMaxDesiredSize = Debug.metric("InliningStoppedByMaxDesiredSize"); + + public GreedyInliningPolicy(Map hints) { + super(hints); + } + + public boolean continueInlining(StructuredGraph currentGraph) { + if (currentGraph.getNodeCount() >= MaximumDesiredSize.getValue()) { + InliningUtil.logInliningDecision("inlining is cut off by MaximumDesiredSize"); + metricInliningStoppedByMaxDesiredSize.increment(); + return false; + } + return true; + } + + @Override + public boolean isWorthInlining(ToDoubleFunction probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, + boolean fullyProcessed) { + if (InlineEverything.getValue()) { + InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "inline everything"); + return true; + } + + if (isIntrinsic(replacements, info)) { + InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "intrinsic"); + return true; + } + + if (info.shouldInline()) { + InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "forced inlining"); + return true; + } + + double inliningBonus = getInliningBonus(info); + int nodes = determineNodeCount(info); + int lowLevelGraphSize = previousLowLevelGraphSize(info); + + if (SmallCompiledLowLevelGraphSize.getValue() > 0 && lowLevelGraphSize > SmallCompiledLowLevelGraphSize.getValue() * inliningBonus) { + InliningUtil.logNotInlinedMethod(info, inliningDepth, "too large previous low-level graph (low-level-nodes: %d, relevance=%f, probability=%f, bonus=%f, nodes=%d)", lowLevelGraphSize, + relevance, probability, inliningBonus, nodes); + return false; + } + + if (nodes < TrivialInliningSize.getValue() * inliningBonus) { + InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "trivial (relevance=%f, probability=%f, bonus=%f, nodes=%d)", relevance, probability, inliningBonus, nodes); + return true; + } + + /* + * TODO (chaeubl): invoked methods that are on important paths but not yet compiled -> will + * be compiled anyways and it is likely that we are the only caller... might be useful to + * inline those methods but increases bootstrap time (maybe those methods are also getting + * queued in the compilation queue concurrently) + */ + double invokes = determineInvokeProbability(probabilities, info); + if (LimitInlinedInvokes.getValue() > 0 && fullyProcessed && invokes > LimitInlinedInvokes.getValue() * inliningBonus) { + InliningUtil.logNotInlinedMethod(info, inliningDepth, "callee invoke probability is too high (invokeP=%f, relevance=%f, probability=%f, bonus=%f, nodes=%d)", invokes, relevance, + probability, inliningBonus, nodes); + return false; + } + + double maximumNodes = computeMaximumSize(relevance, (int) (MaximumInliningSize.getValue() * inliningBonus)); + if (nodes <= maximumNodes) { + InliningUtil.logInlinedMethod(info, inliningDepth, fullyProcessed, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d <= %f)", relevance, probability, inliningBonus, + nodes, maximumNodes); + return true; + } + + InliningUtil.logNotInlinedMethod(info, inliningDepth, "relevance-based (relevance=%f, probability=%f, bonus=%f, nodes=%d > %f)", relevance, probability, inliningBonus, nodes, maximumNodes); + return false; + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/InlineEverythingPolicy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/InlineEverythingPolicy.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,48 @@ +/* + * 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.phases.common.inlining.policy; + +import com.oracle.graal.api.code.BailoutException; +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.spi.Replacements; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; + +import java.util.function.ToDoubleFunction; + +import static com.oracle.graal.compiler.common.GraalOptions.MaximumDesiredSize; + +public final class InlineEverythingPolicy implements InliningPolicy { + + public boolean continueInlining(StructuredGraph graph) { + if (graph.getNodeCount() >= MaximumDesiredSize.getValue()) { + throw new BailoutException("Inline all calls failed. The resulting graph is too large."); + } + return true; + } + + public boolean isWorthInlining(ToDoubleFunction probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, + boolean fullyProcessed) { + return true; + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/InliningPolicy.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/policy/InliningPolicy.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,37 @@ +/* + * 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.phases.common.inlining.policy; + +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.nodes.spi.Replacements; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; + +import java.util.function.ToDoubleFunction; + +public interface InliningPolicy { + + boolean continueInlining(StructuredGraph graph); + + boolean isWorthInlining(ToDoubleFunction probabilities, Replacements replacements, InlineInfo info, int inliningDepth, double probability, double relevance, boolean fullyProcessed); +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/CallsiteHolder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/CallsiteHolder.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,126 @@ +/* + * 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.phases.common.inlining.walker; + +import com.oracle.graal.api.meta.MetaUtil; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.FixedNode; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.nodes.StructuredGraph; +import com.oracle.graal.phases.graph.FixedNodeProbabilityCache; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.function.ToDoubleFunction; + +import static com.oracle.graal.compiler.common.GraalOptions.CapInheritedRelevance; + +/** + * Information about a graph that will potentially be inlined. This includes tracking the + * invocations in graph that will subject to inlining themselves. + */ +public class CallsiteHolder { + + private final StructuredGraph graph; + private final LinkedList remainingInvokes; + private final double probability; + private final double relevance; + + private final ToDoubleFunction probabilities; + private final ComputeInliningRelevance computeInliningRelevance; + + public CallsiteHolder(StructuredGraph graph, double probability, double relevance) { + this.graph = graph; + if (graph == null) { + this.remainingInvokes = new LinkedList<>(); + } else { + LinkedList invokes = new InliningIterator(graph).apply(); + assert invokes.size() == count(graph.getInvokes()); + this.remainingInvokes = invokes; + } + this.probability = probability; + this.relevance = relevance; + + if (graph != null && !remainingInvokes.isEmpty()) { + probabilities = new FixedNodeProbabilityCache(); + computeInliningRelevance = new ComputeInliningRelevance(graph, probabilities); + computeProbabilities(); + } else { + probabilities = null; + computeInliningRelevance = null; + } + } + + private static int count(Iterable invokes) { + int count = 0; + Iterator iterator = invokes.iterator(); + while (iterator.hasNext()) { + iterator.next(); + count++; + } + return count; + } + + /** + * Gets the method associated with the {@linkplain #graph() graph} represented by this object. + */ + public ResolvedJavaMethod method() { + return graph == null ? null : graph.method(); + } + + public boolean hasRemainingInvokes() { + return !remainingInvokes.isEmpty(); + } + + /** + * The graph about which this object contains inlining information. + */ + public StructuredGraph graph() { + return graph; + } + + public Invoke popInvoke() { + return remainingInvokes.removeFirst(); + } + + public void pushInvoke(Invoke invoke) { + remainingInvokes.push(invoke); + } + + public void computeProbabilities() { + computeInliningRelevance.compute(); + } + + public double invokeProbability(Invoke invoke) { + return probability * probabilities.applyAsDouble(invoke.asNode()); + } + + public double invokeRelevance(Invoke invoke) { + return Math.min(CapInheritedRelevance.getValue(), relevance) * computeInliningRelevance.getRelevance(invoke); + } + + @Override + public String toString() { + return (graph != null ? MetaUtil.format("%H.%n(%p)", method()) : "") + remainingInvokes; + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/ComputeInliningRelevance.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/ComputeInliningRelevance.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2013, 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.inlining.walker; + +import static com.oracle.graal.graph.util.CollectionsAccess.*; + +import java.util.*; +import java.util.function.*; + +import com.oracle.graal.graph.*; +import com.oracle.graal.nodes.*; + +import edu.umd.cs.findbugs.annotations.*; + +public class ComputeInliningRelevance { + + private static final double EPSILON = 1d / Integer.MAX_VALUE; + private static final double UNINITIALIZED = -1D; + + private static final int EXPECTED_MIN_INVOKE_COUNT = 3; + private static final int EXPECTED_INVOKE_RATIO = 20; + private static final int EXPECTED_LOOP_COUNT = 3; + + private final StructuredGraph graph; + private final ToDoubleFunction nodeProbabilities; + + /** + * Node relevances are pre-computed for all invokes if the graph contains loops. If there are no + * loops, the computation happens lazily based on {@link #rootScope}. + */ + private Map nodeRelevances; + /** + * This scope is non-null if (and only if) there are no loops in the graph. In this case, the + * root scope is used to compute invoke relevances on the fly. + */ + private Scope rootScope; + + public ComputeInliningRelevance(StructuredGraph graph, ToDoubleFunction nodeProbabilities) { + this.graph = graph; + this.nodeProbabilities = nodeProbabilities; + } + + /** + * Initializes or updates the relevance computation. If there are no loops within the graph, + * most computation happens lazily. + */ + public void compute() { + rootScope = null; + if (!graph.hasLoops()) { + // fast path for the frequent case of no loops + rootScope = new Scope(graph.start(), null); + } else { + if (nodeRelevances == null) { + nodeRelevances = newNodeIdentityMap(EXPECTED_MIN_INVOKE_COUNT + graph.getNodeCount() / EXPECTED_INVOKE_RATIO); + } + NodeWorkList workList = graph.createNodeWorkList(); + Map loops = newNodeIdentityMap(EXPECTED_LOOP_COUNT); + + loops.put(null, new Scope(graph.start(), null)); + for (LoopBeginNode loopBegin : graph.getNodes(LoopBeginNode.class)) { + createLoopScope(loopBegin, loops); + } + + for (Scope scope : loops.values()) { + scope.process(workList); + } + } + } + + public double getRelevance(Invoke invoke) { + if (rootScope != null) { + return rootScope.computeInvokeRelevance(invoke); + } + assert nodeRelevances != null : "uninitialized relevance"; + return nodeRelevances.get(invoke); + } + + /** + * Determines the parent of the given loop and creates a {@link Scope} object for each one. This + * method will call itself recursively if no {@link Scope} for the parent loop exists. + */ + private Scope createLoopScope(LoopBeginNode loopBegin, Map loops) { + Scope scope = loops.get(loopBegin); + if (scope == null) { + final Scope parent; + // look for the parent scope + FixedNode current = loopBegin.forwardEnd(); + while (true) { + if (current.predecessor() == null) { + if (current instanceof LoopBeginNode) { + // if we reach a LoopBeginNode then we're within this loop + parent = createLoopScope((LoopBeginNode) current, loops); + break; + } else if (current instanceof StartNode) { + // we're within the outermost scope + parent = loops.get(null); + break; + } else { + assert current.getClass() == MergeNode.class : current; + // follow any path upwards - it doesn't matter which one + current = ((MergeNode) current).forwardEndAt(0); + } + } else if (current instanceof LoopExitNode) { + // if we reach a loop exit then we follow this loop and have the same parent + parent = createLoopScope(((LoopExitNode) current).loopBegin(), loops).parent; + break; + } else { + current = (FixedNode) current.predecessor(); + } + } + scope = new Scope(loopBegin, parent); + loops.put(loopBegin, scope); + } + return scope; + } + + /** + * A scope holds information for the contents of one loop or of the root of the method. It does + * not include child loops, i.e., the iteration in {@link #process(NodeWorkList)} explicitly + * excludes the nodes of child loops. + */ + private class Scope { + public final FixedNode start; + public final Scope parent; // can be null for the outermost scope + + /** + * The minimum probability along the most probable path in this scope. Computed lazily. + */ + private double fastPathMinProbability = UNINITIALIZED; + /** + * A measure of how important this scope is within its parent scope. Computed lazily. + */ + private double scopeRelevanceWithinParent = UNINITIALIZED; + + public Scope(FixedNode start, Scope parent) { + this.start = start; + this.parent = parent; + } + + @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") + public double getFastPathMinProbability() { + if (fastPathMinProbability == UNINITIALIZED) { + fastPathMinProbability = Math.max(EPSILON, computeFastPathMinProbability(start)); + } + return fastPathMinProbability; + } + + /** + * Computes the ratio between the probabilities of the current scope's entry point and the + * parent scope's fastPathMinProbability. + */ + @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY") + public double getScopeRelevanceWithinParent() { + if (scopeRelevanceWithinParent == UNINITIALIZED) { + if (start instanceof LoopBeginNode) { + assert parent != null; + double scopeEntryProbability = nodeProbabilities.applyAsDouble(((LoopBeginNode) start).forwardEnd()); + + scopeRelevanceWithinParent = scopeEntryProbability / parent.getFastPathMinProbability(); + } else { + scopeRelevanceWithinParent = 1D; + } + } + return scopeRelevanceWithinParent; + } + + /** + * Processes all invokes in this scope by starting at the scope's start node and iterating + * all fixed nodes. Child loops are skipped by going from loop entries directly to the loop + * exits. Processing stops at loop exits of the current loop. + */ + public void process(NodeWorkList workList) { + assert !(start instanceof Invoke); + workList.addAll(start.successors()); + + for (Node current : workList) { + assert current.isAlive(); + + if (current instanceof Invoke) { + // process the invoke and queue its successors + nodeRelevances.put((FixedNode) current, computeInvokeRelevance((Invoke) current)); + workList.addAll(current.successors()); + } else if (current instanceof LoopBeginNode) { + // skip child loops by advancing over the loop exits + ((LoopBeginNode) current).loopExits().forEach(exit -> workList.add(exit.next())); + } else if (current instanceof LoopEndNode) { + // nothing to do + } else if (current instanceof LoopExitNode) { + // nothing to do + } else if (current instanceof FixedWithNextNode) { + workList.add(((FixedWithNextNode) current).next()); + } else if (current instanceof EndNode) { + workList.add(((EndNode) current).merge()); + } else if (current instanceof ControlSinkNode) { + // nothing to do + } else if (current instanceof ControlSplitNode) { + workList.addAll(current.successors()); + } else { + assert false : current; + } + } + } + + /** + * The relevance of an invoke is the ratio between the invoke's probability and the current + * scope's fastPathMinProbability, adjusted by scopeRelevanceWithinParent. + */ + public double computeInvokeRelevance(Invoke invoke) { + double invokeProbability = nodeProbabilities.applyAsDouble(invoke.asNode()); + assert !Double.isNaN(invokeProbability); + + double relevance = (invokeProbability / getFastPathMinProbability()) * Math.min(1.0, getScopeRelevanceWithinParent()); + assert !Double.isNaN(relevance) : invoke + ": " + relevance + " / " + invokeProbability + " / " + getFastPathMinProbability() + " / " + getScopeRelevanceWithinParent(); + return relevance; + } + } + + /** + * Computes the minimum probability along the most probable path within the scope. During + * iteration, the method returns immediately once a loop exit is discovered. + */ + private double computeFastPathMinProbability(FixedNode scopeStart) { + ArrayList pathBeginNodes = new ArrayList<>(); + pathBeginNodes.add(scopeStart); + double minPathProbability = nodeProbabilities.applyAsDouble(scopeStart); + boolean isLoopScope = scopeStart instanceof LoopBeginNode; + + do { + Node current = pathBeginNodes.remove(pathBeginNodes.size() - 1); + do { + if (isLoopScope && current instanceof LoopExitNode && ((LoopBeginNode) scopeStart).loopExits().contains((LoopExitNode) current)) { + return minPathProbability; + } else if (current instanceof LoopBeginNode && current != scopeStart) { + current = getMaxProbabilityLoopExit((LoopBeginNode) current, pathBeginNodes); + minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); + } else if (current instanceof ControlSplitNode) { + current = getMaxProbabilitySux((ControlSplitNode) current, pathBeginNodes); + minPathProbability = getMinPathProbability((FixedNode) current, minPathProbability); + } else { + assert current.successors().count() <= 1; + current = current.successors().first(); + } + } while (current != null); + } while (!pathBeginNodes.isEmpty()); + + return minPathProbability; + } + + private double getMinPathProbability(FixedNode current, double minPathProbability) { + return current == null ? minPathProbability : Math.min(minPathProbability, nodeProbabilities.applyAsDouble(current)); + } + + /** + * Returns the most probable successor. If multiple successors share the maximum probability, + * one is returned and the others are enqueued in pathBeginNodes. + */ + private static Node getMaxProbabilitySux(ControlSplitNode controlSplit, ArrayList pathBeginNodes) { + Node maxSux = null; + double maxProbability = 0.0; + int pathBeginCount = pathBeginNodes.size(); + + for (Node sux : controlSplit.successors()) { + double probability = controlSplit.probability((BeginNode) sux); + if (probability > maxProbability) { + maxProbability = probability; + maxSux = sux; + truncate(pathBeginNodes, pathBeginCount); + } else if (probability == maxProbability) { + pathBeginNodes.add((FixedNode) sux); + } + } + + return maxSux; + } + + /** + * Returns the most probable loop exit. If multiple successors share the maximum probability, + * one is returned and the others are enqueued in pathBeginNodes. + */ + private Node getMaxProbabilityLoopExit(LoopBeginNode loopBegin, ArrayList pathBeginNodes) { + Node maxSux = null; + double maxProbability = 0.0; + int pathBeginCount = pathBeginNodes.size(); + + for (LoopExitNode sux : loopBegin.loopExits()) { + double probability = nodeProbabilities.applyAsDouble(sux); + if (probability > maxProbability) { + maxProbability = probability; + maxSux = sux; + truncate(pathBeginNodes, pathBeginCount); + } else if (probability == maxProbability) { + pathBeginNodes.add(sux); + } + } + + return maxSux; + } + + private static void truncate(ArrayList pathBeginNodes, int pathBeginCount) { + for (int i = pathBeginNodes.size() - pathBeginCount; i > 0; i--) { + pathBeginNodes.remove(pathBeginNodes.size() - 1); + } + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/DepthSearchUtil.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/DepthSearchUtil.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,149 @@ +/* + * 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.phases.common.inlining.walker; + +import com.oracle.graal.api.meta.Constant; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.compiler.common.type.Stamp; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.graph.NodeInputList; +import com.oracle.graal.nodes.*; +import com.oracle.graal.phases.common.CanonicalizerPhase; +import com.oracle.graal.phases.common.DeadCodeEliminationPhase; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.tiers.HighTierContext; + +import static com.oracle.graal.compiler.common.GraalOptions.OptCanonicalizer; + +/** + * The workings of {@link InliningData} include delving into a callsite to explore inlining + * opportunities. The utilities used for that are grouped in this class. + */ +public class DepthSearchUtil { + + private DepthSearchUtil() { + // no instances + } + + public static InliningUtil.Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context, CanonicalizerPhase canonicalizer) { + Class macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method); + if (macroNodeClass != null) { + return new InliningUtil.InlineableMacroNode(macroNodeClass); + } else { + return new InliningUtil.InlineableGraph(buildGraph(method, invoke, context, canonicalizer)); + } + } + + private static StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) { + final StructuredGraph newGraph; + final boolean parseBytecodes; + + // TODO (chaeubl): copying the graph is only necessary if it is modified or if it contains + // any invokes + StructuredGraph intrinsicGraph = InliningUtil.getIntrinsicGraph(context.getReplacements(), method); + if (intrinsicGraph != null) { + newGraph = intrinsicGraph.copy(); + parseBytecodes = false; + } else { + StructuredGraph cachedGraph = getCachedGraph(method, context); + if (cachedGraph != null) { + newGraph = cachedGraph.copy(); + parseBytecodes = false; + } else { + newGraph = new StructuredGraph(method); + parseBytecodes = true; + } + } + + try (Debug.Scope s = Debug.scope("InlineGraph", newGraph)) { + if (parseBytecodes) { + parseBytecodes(newGraph, context, canonicalizer); + } + + boolean callerHasMoreInformationAboutArguments = false; + NodeInputList args = invoke.callTarget().arguments(); + for (ParameterNode param : newGraph.getNodes(ParameterNode.class).snapshot()) { + ValueNode arg = args.get(param.index()); + if (arg.isConstant()) { + Constant constant = arg.asConstant(); + newGraph.replaceFloating(param, ConstantNode.forConstant(constant, context.getMetaAccess(), newGraph)); + callerHasMoreInformationAboutArguments = true; + } else { + Stamp joinedStamp = param.stamp().join(arg.stamp()); + if (joinedStamp != null && !joinedStamp.equals(param.stamp())) { + param.setStamp(joinedStamp); + callerHasMoreInformationAboutArguments = true; + } + } + } + + if (!callerHasMoreInformationAboutArguments) { + // TODO (chaeubl): if args are not more concrete, inlining should be avoided + // in most cases or we could at least use the previous graph size + invoke + // probability to check the inlining + } + + if (OptCanonicalizer.getValue()) { + canonicalizer.apply(newGraph, context); + } + + return newGraph; + } catch (Throwable e) { + throw Debug.handle(e); + } + } + + private static StructuredGraph getCachedGraph(ResolvedJavaMethod method, HighTierContext context) { + if (context.getGraphCache() != null) { + StructuredGraph cachedGraph = context.getGraphCache().get(method); + if (cachedGraph != null) { + return cachedGraph; + } + } + return null; + } + + /** + * This method builds the IR nodes for newGraph and canonicalizes them. Provided + * profiling info is mature, the resulting graph is cached. + */ + private static StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context, CanonicalizerPhase canonicalizer) { + final boolean hasMatureProfilingInfo = newGraph.method().getProfilingInfo().isMature(); + + if (context.getGraphBuilderSuite() != null) { + context.getGraphBuilderSuite().apply(newGraph, context); + } + assert newGraph.start().next() != null : "graph needs to be populated during PhasePosition.AFTER_PARSING"; + + new DeadCodeEliminationPhase().apply(newGraph); + + if (OptCanonicalizer.getValue()) { + canonicalizer.apply(newGraph, context); + } + + if (hasMatureProfilingInfo && context.getGraphCache() != null) { + context.getGraphCache().put(newGraph.method(), newGraph.copy()); + } + return newGraph; + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/InliningData.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/InliningData.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,553 @@ +/* + * 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.phases.common.inlining.walker; + +import com.oracle.graal.api.code.Assumptions; +import com.oracle.graal.api.code.BailoutException; +import com.oracle.graal.api.meta.JavaTypeProfile; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.api.meta.ResolvedJavaType; +import com.oracle.graal.compiler.common.GraalInternalError; +import com.oracle.graal.compiler.common.type.ObjectStamp; +import com.oracle.graal.debug.Debug; +import com.oracle.graal.debug.DebugMetric; +import com.oracle.graal.graph.Graph; +import com.oracle.graal.graph.Node; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.MethodCallTargetNode; +import com.oracle.graal.phases.OptimisticOptimizations; +import com.oracle.graal.phases.common.CanonicalizerPhase; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.common.inlining.info.*; +import com.oracle.graal.phases.common.inlining.policy.InliningPolicy; +import com.oracle.graal.phases.graph.FixedNodeProbabilityCache; +import com.oracle.graal.phases.tiers.HighTierContext; +import com.oracle.graal.phases.util.Providers; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.function.ToDoubleFunction; + +import static com.oracle.graal.compiler.common.GraalOptions.MegamorphicInliningMinMethodProbability; +import static com.oracle.graal.compiler.common.GraalOptions.OptCanonicalizer; + +/** + * Holds the data for building the callee graphs recursively: graphs and invocations (each + * invocation can have multiple graphs). + */ +public class InliningData { + + private static final CallsiteHolder DUMMY_CALLSITE_HOLDER = new CallsiteHolder(null, 1.0, 1.0); + // Metrics + private static final DebugMetric metricInliningPerformed = Debug.metric("InliningPerformed"); + private static final DebugMetric metricInliningRuns = Debug.metric("InliningRuns"); + private static final DebugMetric metricInliningConsidered = Debug.metric("InliningConsidered"); + + /** + * Call hierarchy from outer most call (i.e., compilation unit) to inner most callee. + */ + private final ArrayDeque graphQueue = new ArrayDeque<>(); + private final ArrayDeque invocationQueue = new ArrayDeque<>(); + private final ToDoubleFunction probabilities = new FixedNodeProbabilityCache(); + + private final HighTierContext context; + private final int maxMethodPerInlining; + private final CanonicalizerPhase canonicalizer; + private final InliningPolicy inliningPolicy; + + private int maxGraphs; + + public InliningData(StructuredGraph rootGraph, HighTierContext context, int maxMethodPerInlining, CanonicalizerPhase canonicalizer, InliningPolicy inliningPolicy) { + assert rootGraph != null; + this.context = context; + this.maxMethodPerInlining = maxMethodPerInlining; + this.canonicalizer = canonicalizer; + this.inliningPolicy = inliningPolicy; + this.maxGraphs = 1; + + Assumptions rootAssumptions = context.getAssumptions(); + invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0)); + pushGraph(rootGraph, 1.0, 1.0); + } + + /** + * Determines if inlining is possible at the given invoke node. + * + * @param invoke the invoke that should be inlined + * @return an instance of InlineInfo, or null if no inlining is possible at the given invoke + */ + private InlineInfo getInlineInfo(Invoke invoke, Assumptions assumptions) { + final String failureMessage = InliningUtil.checkInvokeConditions(invoke); + if (failureMessage != null) { + InliningUtil.logNotInlinedMethod(invoke, failureMessage); + return null; + } + MethodCallTargetNode callTarget = (MethodCallTargetNode) invoke.callTarget(); + ResolvedJavaMethod targetMethod = callTarget.targetMethod(); + + if (callTarget.invokeKind() == MethodCallTargetNode.InvokeKind.Special || targetMethod.canBeStaticallyBound()) { + return getExactInlineInfo(invoke, targetMethod); + } + + assert callTarget.invokeKind() == MethodCallTargetNode.InvokeKind.Virtual || callTarget.invokeKind() == MethodCallTargetNode.InvokeKind.Interface; + + ResolvedJavaType holder = targetMethod.getDeclaringClass(); + if (!(callTarget.receiver().stamp() instanceof ObjectStamp)) { + return null; + } + ObjectStamp receiverStamp = (ObjectStamp) callTarget.receiver().stamp(); + if (receiverStamp.alwaysNull()) { + // Don't inline if receiver is known to be null + return null; + } + ResolvedJavaType contextType = invoke.getContextType(); + if (receiverStamp.type() != null) { + // the invoke target might be more specific than the holder (happens after inlining: + // parameters lose their declared type...) + ResolvedJavaType receiverType = receiverStamp.type(); + if (receiverType != null && holder.isAssignableFrom(receiverType)) { + holder = receiverType; + if (receiverStamp.isExactType()) { + assert targetMethod.getDeclaringClass().isAssignableFrom(holder) : holder + " subtype of " + targetMethod.getDeclaringClass() + " for " + targetMethod; + ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod, contextType); + if (resolvedMethod != null) { + return getExactInlineInfo(invoke, resolvedMethod); + } + } + } + } + + if (holder.isArray()) { + // arrays can be treated as Objects + ResolvedJavaMethod resolvedMethod = holder.resolveMethod(targetMethod, contextType); + if (resolvedMethod != null) { + return getExactInlineInfo(invoke, resolvedMethod); + } + } + + if (assumptions.useOptimisticAssumptions()) { + ResolvedJavaType uniqueSubtype = holder.findUniqueConcreteSubtype(); + if (uniqueSubtype != null) { + ResolvedJavaMethod resolvedMethod = uniqueSubtype.resolveMethod(targetMethod, contextType); + if (resolvedMethod != null) { + return getAssumptionInlineInfo(invoke, resolvedMethod, new Assumptions.ConcreteSubtype(holder, uniqueSubtype)); + } + } + + ResolvedJavaMethod concrete = holder.findUniqueConcreteMethod(targetMethod); + if (concrete != null) { + return getAssumptionInlineInfo(invoke, concrete, new Assumptions.ConcreteMethod(targetMethod, holder, concrete)); + } + } + + // type check based inlining + return getTypeCheckedInlineInfo(invoke, targetMethod); + } + + private InlineInfo getTypeCheckedInlineInfo(Invoke invoke, ResolvedJavaMethod targetMethod) { + JavaTypeProfile typeProfile; + ValueNode receiver = invoke.callTarget().arguments().get(0); + if (receiver instanceof TypeProfileProxyNode) { + TypeProfileProxyNode typeProfileProxyNode = (TypeProfileProxyNode) receiver; + typeProfile = typeProfileProxyNode.getProfile(); + } else { + InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "no type profile exists"); + return null; + } + + JavaTypeProfile.ProfiledType[] ptypes = typeProfile.getTypes(); + if (ptypes == null || ptypes.length <= 0) { + InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "no types in profile"); + return null; + } + ResolvedJavaType contextType = invoke.getContextType(); + double notRecordedTypeProbability = typeProfile.getNotRecordedProbability(); + final OptimisticOptimizations optimisticOpts = context.getOptimisticOptimizations(); + if (ptypes.length == 1 && notRecordedTypeProbability == 0) { + if (!optimisticOpts.inlineMonomorphicCalls()) { + InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "inlining monomorphic calls is disabled"); + return null; + } + + ResolvedJavaType type = ptypes[0].getType(); + assert type.isArray() || !type.isAbstract(); + ResolvedJavaMethod concrete = type.resolveMethod(targetMethod, contextType); + if (!InliningUtil.checkTargetConditions(this, context.getReplacements(), invoke, concrete, optimisticOpts)) { + return null; + } + return new TypeGuardInlineInfo(invoke, concrete, type); + } else { + invoke.setPolymorphic(true); + + if (!optimisticOpts.inlinePolymorphicCalls() && notRecordedTypeProbability == 0) { + InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "inlining polymorphic calls is disabled (%d types)", ptypes.length); + return null; + } + if (!optimisticOpts.inlineMegamorphicCalls() && notRecordedTypeProbability > 0) { + // due to filtering impossible types, notRecordedTypeProbability can be > 0 although + // the number of types is lower than what can be recorded in a type profile + InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "inlining megamorphic calls is disabled (%d types, %f %% not recorded types)", ptypes.length, + notRecordedTypeProbability * 100); + return null; + } + + // Find unique methods and their probabilities. + ArrayList concreteMethods = new ArrayList<>(); + ArrayList concreteMethodsProbabilities = new ArrayList<>(); + for (int i = 0; i < ptypes.length; i++) { + ResolvedJavaMethod concrete = ptypes[i].getType().resolveMethod(targetMethod, contextType); + if (concrete == null) { + InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "could not resolve method"); + return null; + } + int index = concreteMethods.indexOf(concrete); + double curProbability = ptypes[i].getProbability(); + if (index < 0) { + index = concreteMethods.size(); + concreteMethods.add(concrete); + concreteMethodsProbabilities.add(curProbability); + } else { + concreteMethodsProbabilities.set(index, concreteMethodsProbabilities.get(index) + curProbability); + } + } + + if (concreteMethods.size() > maxMethodPerInlining) { + InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "polymorphic call with more than %d target methods", maxMethodPerInlining); + return null; + } + + // Clear methods that fall below the threshold. + if (notRecordedTypeProbability > 0) { + ArrayList newConcreteMethods = new ArrayList<>(); + ArrayList newConcreteMethodsProbabilities = new ArrayList<>(); + for (int i = 0; i < concreteMethods.size(); ++i) { + if (concreteMethodsProbabilities.get(i) >= MegamorphicInliningMinMethodProbability.getValue()) { + newConcreteMethods.add(concreteMethods.get(i)); + newConcreteMethodsProbabilities.add(concreteMethodsProbabilities.get(i)); + } + } + + if (newConcreteMethods.size() == 0) { + // No method left that is worth inlining. + InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "no methods remaining after filtering less frequent methods (%d methods previously)", + concreteMethods.size()); + return null; + } + + concreteMethods = newConcreteMethods; + concreteMethodsProbabilities = newConcreteMethodsProbabilities; + } + + // Clean out types whose methods are no longer available. + ArrayList usedTypes = new ArrayList<>(); + ArrayList typesToConcretes = new ArrayList<>(); + for (JavaTypeProfile.ProfiledType type : ptypes) { + ResolvedJavaMethod concrete = type.getType().resolveMethod(targetMethod, contextType); + int index = concreteMethods.indexOf(concrete); + if (index == -1) { + notRecordedTypeProbability += type.getProbability(); + } else { + assert type.getType().isArray() || !type.getType().isAbstract() : type + " " + concrete; + usedTypes.add(type); + typesToConcretes.add(index); + } + } + + if (usedTypes.size() == 0) { + // No type left that is worth checking for. + InliningUtil.logNotInlinedInvoke(invoke, inliningDepth(), targetMethod, "no types remaining after filtering less frequent types (%d types previously)", ptypes.length); + return null; + } + + for (ResolvedJavaMethod concrete : concreteMethods) { + if (!InliningUtil.checkTargetConditions(this, context.getReplacements(), invoke, concrete, optimisticOpts)) { + InliningUtil.logNotInlined(invoke, inliningDepth(), targetMethod, "it is a polymorphic method call and at least one invoked method cannot be inlined"); + return null; + } + } + return new MultiTypeGuardInlineInfo(invoke, concreteMethods, concreteMethodsProbabilities, usedTypes, typesToConcretes, notRecordedTypeProbability); + } + } + + private InlineInfo getAssumptionInlineInfo(Invoke invoke, ResolvedJavaMethod concrete, Assumptions.Assumption takenAssumption) { + assert !concrete.isAbstract(); + if (!InliningUtil.checkTargetConditions(this, context.getReplacements(), invoke, concrete, context.getOptimisticOptimizations())) { + return null; + } + return new AssumptionInlineInfo(invoke, concrete, takenAssumption); + } + + private InlineInfo getExactInlineInfo(Invoke invoke, ResolvedJavaMethod targetMethod) { + assert !targetMethod.isAbstract(); + if (!InliningUtil.checkTargetConditions(this, context.getReplacements(), invoke, targetMethod, context.getOptimisticOptimizations())) { + return null; + } + return new ExactInlineInfo(invoke, targetMethod); + } + + private void doInline(CallsiteHolder callerCallsiteHolder, MethodInvocation calleeInfo, Assumptions callerAssumptions) { + StructuredGraph callerGraph = callerCallsiteHolder.graph(); + Graph.Mark markBeforeInlining = callerGraph.getMark(); + InlineInfo callee = calleeInfo.callee(); + try { + try (Debug.Scope scope = Debug.scope("doInline", callerGraph)) { + List invokeUsages = callee.invoke().asNode().usages().snapshot(); + callee.inline(new Providers(context), callerAssumptions); + callerAssumptions.record(calleeInfo.assumptions()); + metricInliningRuns.increment(); + Debug.dump(callerGraph, "after %s", callee); + + if (OptCanonicalizer.getValue()) { + Graph.Mark markBeforeCanonicalization = callerGraph.getMark(); + canonicalizer.applyIncremental(callerGraph, context, invokeUsages, markBeforeInlining); + + // process invokes that are possibly created during canonicalization + for (Node newNode : callerGraph.getNewNodes(markBeforeCanonicalization)) { + if (newNode instanceof Invoke) { + callerCallsiteHolder.pushInvoke((Invoke) newNode); + } + } + } + + callerCallsiteHolder.computeProbabilities(); + + metricInliningPerformed.increment(); + } + } catch (BailoutException bailout) { + throw bailout; + } catch (AssertionError | RuntimeException e) { + throw new GraalInternalError(e).addContext(callee.toString()); + } catch (GraalInternalError e) { + throw e.addContext(callee.toString()); + } + } + + /** + * @return true iff inlining was actually performed + */ + private boolean tryToInline(CallsiteHolder callerCallsiteHolder, MethodInvocation calleeInfo, MethodInvocation parentInvocation, int inliningDepth) { + InlineInfo callee = calleeInfo.callee(); + Assumptions callerAssumptions = parentInvocation.assumptions(); + metricInliningConsidered.increment(); + + if (inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), callee, inliningDepth, calleeInfo.probability(), calleeInfo.relevance(), true)) { + doInline(callerCallsiteHolder, calleeInfo, callerAssumptions); + return true; + } + + if (context.getOptimisticOptimizations().devirtualizeInvokes()) { + callee.tryToDevirtualizeInvoke(context.getMetaAccess(), callerAssumptions); + } + + return false; + } + + /** + * Process the next invoke and enqueue all its graphs for processing. + */ + private void processNextInvoke() { + CallsiteHolder callsiteHolder = currentGraph(); + Invoke invoke = callsiteHolder.popInvoke(); + MethodInvocation callerInvocation = currentInvocation(); + Assumptions parentAssumptions = callerInvocation.assumptions(); + InlineInfo info = getInlineInfo(invoke, parentAssumptions); + + if (info != null) { + double invokeProbability = callsiteHolder.invokeProbability(invoke); + double invokeRelevance = callsiteHolder.invokeRelevance(invoke); + MethodInvocation calleeInvocation = pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance); + + for (int i = 0; i < info.numberOfMethods(); i++) { + InliningUtil.Inlineable elem = DepthSearchUtil.getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions()), canonicalizer); + info.setInlinableElement(i, elem); + if (elem instanceof InliningUtil.InlineableGraph) { + pushGraph(((InliningUtil.InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i)); + } else { + assert elem instanceof InliningUtil.InlineableMacroNode; + pushDummyGraph(); + } + } + } + } + + public int graphCount() { + return graphQueue.size(); + } + + private void pushGraph(StructuredGraph graph, double probability, double relevance) { + assert graph != null; + assert !contains(graph); + graphQueue.push(new CallsiteHolder(graph, probability, relevance)); + assert graphQueue.size() <= maxGraphs; + } + + private void pushDummyGraph() { + graphQueue.push(DUMMY_CALLSITE_HOLDER); + } + + public boolean hasUnprocessedGraphs() { + return !graphQueue.isEmpty(); + } + + private CallsiteHolder currentGraph() { + return graphQueue.peek(); + } + + private void popGraph() { + graphQueue.pop(); + assert graphQueue.size() <= maxGraphs; + } + + private void popGraphs(int count) { + assert count >= 0; + for (int i = 0; i < count; i++) { + graphQueue.pop(); + } + } + + private static final Object[] NO_CONTEXT = {}; + + /** + * Gets the call hierarchy of this inlining from outer most call to inner most callee. + */ + private Object[] inliningContext() { + if (!Debug.isDumpEnabled()) { + return NO_CONTEXT; + } + Object[] result = new Object[graphQueue.size()]; + int i = 0; + for (CallsiteHolder g : graphQueue) { + result[i++] = g.method(); + } + return result; + } + + private MethodInvocation currentInvocation() { + return invocationQueue.peekFirst(); + } + + private MethodInvocation pushInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { + MethodInvocation methodInvocation = new MethodInvocation(info, new Assumptions(assumptions.useOptimisticAssumptions()), probability, relevance); + invocationQueue.addFirst(methodInvocation); + maxGraphs += info.numberOfMethods(); + assert graphQueue.size() <= maxGraphs; + return methodInvocation; + } + + private void popInvocation() { + maxGraphs -= invocationQueue.peekFirst().callee().numberOfMethods(); + assert graphQueue.size() <= maxGraphs; + invocationQueue.removeFirst(); + } + + public int countRecursiveInlining(ResolvedJavaMethod method) { + int count = 0; + for (CallsiteHolder callsiteHolder : graphQueue) { + if (method.equals(callsiteHolder.method())) { + count++; + } + } + return count; + } + + public int inliningDepth() { + assert invocationQueue.size() > 0; + return invocationQueue.size() - 1; + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder("Invocations: "); + + for (MethodInvocation invocation : invocationQueue) { + if (invocation.callee() != null) { + result.append(invocation.callee().numberOfMethods()); + result.append("x "); + result.append(invocation.callee().invoke()); + result.append("; "); + } + } + + result.append("\nGraphs: "); + for (CallsiteHolder graph : graphQueue) { + result.append(graph.graph()); + result.append("; "); + } + + return result.toString(); + } + + private boolean contains(StructuredGraph graph) { + for (CallsiteHolder info : graphQueue) { + if (info.graph() == graph) { + return true; + } + } + return false; + } + + /** + * @return true iff inlining was actually performed + */ + public boolean moveForward() { + + final MethodInvocation currentInvocation = currentInvocation(); + + final boolean backtrack = (!currentInvocation.isRoot() && !inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), currentInvocation.callee(), inliningDepth(), + currentInvocation.probability(), currentInvocation.relevance(), false)); + if (backtrack) { + int remainingGraphs = currentInvocation.totalGraphs() - currentInvocation.processedGraphs(); + assert remainingGraphs > 0; + popGraphs(remainingGraphs); + popInvocation(); + return false; + } + + final boolean delve = currentGraph().hasRemainingInvokes() && inliningPolicy.continueInlining(currentGraph().graph()); + if (delve) { + processNextInvoke(); + return false; + } + + popGraph(); + if (currentInvocation.isRoot()) { + return false; + } + + // try to inline + assert currentInvocation.callee().invoke().asNode().isAlive(); + currentInvocation.incrementProcessedGraphs(); + if (currentInvocation.processedGraphs() == currentInvocation.totalGraphs()) { + popInvocation(); + final MethodInvocation parentInvoke = currentInvocation(); + try (Debug.Scope s = Debug.scope("Inlining", inliningContext())) { + return tryToInline(currentGraph(), currentInvocation, parentInvoke, inliningDepth() + 1); + } catch (Throwable e) { + throw Debug.handle(e); + } + } + + return false; + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/InliningIterator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/InliningIterator.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,130 @@ +/* + * 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.phases.common.inlining.walker; + +import com.oracle.graal.graph.Node; +import com.oracle.graal.graph.NodeBitMap; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.java.MethodCallTargetNode; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.LinkedList; + +/** + * Given a graph, visit all fixed nodes in dominator-based order, collecting in the process the + * {@link Invoke} nodes with {@link MethodCallTargetNode}. Such list of callsites is returned by + * {@link #apply()} + */ +public class InliningIterator { + + private final StartNode start; + private final Deque nodeQueue; + private final NodeBitMap queuedNodes; + + public InliningIterator(StructuredGraph graph) { + this.start = graph.start(); + this.nodeQueue = new ArrayDeque<>(); + this.queuedNodes = graph.createNodeBitMap(); + assert start.isAlive(); + } + + public LinkedList apply() { + LinkedList invokes = new LinkedList<>(); + FixedNode current; + forcedQueue(start); + + while ((current = nextQueuedNode()) != null) { + assert current.isAlive(); + + if (current instanceof Invoke && ((Invoke) current).callTarget() instanceof MethodCallTargetNode) { + if (current != start) { + invokes.addLast((Invoke) current); + } + queueSuccessors(current); + } else if (current instanceof LoopBeginNode) { + queueSuccessors(current); + } else if (current instanceof LoopEndNode) { + // nothing to do + } else if (current instanceof MergeNode) { + queueSuccessors(current); + } else if (current instanceof FixedWithNextNode) { + queueSuccessors(current); + } else if (current instanceof EndNode) { + queueMerge((EndNode) current); + } else if (current instanceof ControlSinkNode) { + // nothing to do + } else if (current instanceof ControlSplitNode) { + queueSuccessors(current); + } else { + assert false : current; + } + } + + return invokes; + } + + private void queueSuccessors(FixedNode x) { + for (Node node : x.successors()) { + queue(node); + } + } + + private void queue(Node node) { + if (node != null && !queuedNodes.isMarked(node)) { + forcedQueue(node); + } + } + + private void forcedQueue(Node node) { + queuedNodes.mark(node); + nodeQueue.addFirst((FixedNode) node); + } + + private FixedNode nextQueuedNode() { + if (nodeQueue.isEmpty()) { + return null; + } + + FixedNode result = nodeQueue.removeFirst(); + assert queuedNodes.isMarked(result); + return result; + } + + private void queueMerge(AbstractEndNode end) { + MergeNode merge = end.merge(); + if (!queuedNodes.isMarked(merge) && visitedAllEnds(merge)) { + queuedNodes.mark(merge); + nodeQueue.add(merge); + } + } + + private boolean visitedAllEnds(MergeNode merge) { + for (int i = 0; i < merge.forwardEndCount(); i++) { + if (!queuedNodes.isMarked(merge.forwardEndAt(i))) { + return false; + } + } + return true; + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/MethodInvocation.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/walker/MethodInvocation.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,95 @@ +/* + * 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.phases.common.inlining.walker; + +import com.oracle.graal.api.code.Assumptions; +import com.oracle.graal.api.meta.MetaUtil; +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.CallTargetNode; +import com.oracle.graal.nodes.java.MethodCallTargetNode; +import com.oracle.graal.phases.common.inlining.info.InlineInfo; + +public class MethodInvocation { + + private final InlineInfo callee; + private final Assumptions assumptions; + private final double probability; + private final double relevance; + + private int processedGraphs; + + public MethodInvocation(InlineInfo info, Assumptions assumptions, double probability, double relevance) { + this.callee = info; + this.assumptions = assumptions; + this.probability = probability; + this.relevance = relevance; + } + + public void incrementProcessedGraphs() { + processedGraphs++; + assert processedGraphs <= callee.numberOfMethods(); + } + + public int processedGraphs() { + assert processedGraphs <= callee.numberOfMethods(); + return processedGraphs; + } + + public int totalGraphs() { + return callee.numberOfMethods(); + } + + public InlineInfo callee() { + return callee; + } + + public Assumptions assumptions() { + return assumptions; + } + + public double probability() { + return probability; + } + + public double relevance() { + return relevance; + } + + public boolean isRoot() { + return callee == null; + } + + @Override + public String toString() { + if (isRoot()) { + return ""; + } + CallTargetNode callTarget = callee.invoke().callTarget(); + if (callTarget instanceof MethodCallTargetNode) { + ResolvedJavaMethod calleeMethod = ((MethodCallTargetNode) callTarget).targetMethod(); + return MetaUtil.format("Invoke#%H.%n(%p)", calleeMethod); + } else { + return "Invoke#" + callTarget.targetName(); + } + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java Mon May 19 17:21:30 2014 -0700 @@ -28,6 +28,7 @@ import com.oracle.graal.debug.Debug.Scope; import com.oracle.graal.debug.DebugMemUseTracker.Closeable; import com.oracle.graal.debug.internal.*; +import com.oracle.graal.graph.*; import com.oracle.graal.nodes.*; /** @@ -39,9 +40,26 @@ private CharSequence name; - private final DebugTimer phaseTimer; - private final DebugMetric phaseMetric; - private final DebugMemUseTracker phaseMemUseTracker; + /** + * Records time spent in {@link #apply(StructuredGraph, Object, boolean)}. + */ + private final DebugTimer timer; + + /** + * Counts calls to {@link #apply(StructuredGraph, Object, boolean)}. + */ + private final DebugMetric executionCount; + + /** + * Accumulates the {@linkplain Graph#getNodeCount() live node count} of all graphs sent to + * {@link #apply(StructuredGraph, Object, boolean)}. + */ + private final DebugMetric inputNodesCount; + + /** + * Records memory usage within {@link #apply(StructuredGraph, Object, boolean)}. + */ + private final DebugMemUseTracker memUseTracker; private static final Pattern NAME_PATTERN = Pattern.compile("[A-Z][A-Za-z0-9]+"); @@ -51,17 +69,19 @@ } protected BasePhase() { - phaseTimer = Debug.timer("PhaseTime_%s", getClass()); - phaseMetric = Debug.metric("PhaseCount_%s", getClass()); - phaseMemUseTracker = Debug.memUseTracker("PhaseMemUse_%s", getClass()); + timer = Debug.timer("PhaseTime_%s", getClass()); + executionCount = Debug.metric("PhaseCount_%s", getClass()); + memUseTracker = Debug.memUseTracker("PhaseMemUse_%s", getClass()); + inputNodesCount = Debug.metric("PhaseNodes_%s", getClass()); } protected BasePhase(String name) { assert checkName(name); this.name = name; - phaseTimer = Debug.timer("PhaseTime_%s", getClass()); - phaseMetric = Debug.metric("PhaseCount_%s", getClass()); - phaseMemUseTracker = Debug.memUseTracker("PhaseMemUse_%s", getClass()); + timer = Debug.timer("PhaseTime_%s", getClass()); + executionCount = Debug.metric("PhaseCount_%s", getClass()); + memUseTracker = Debug.memUseTracker("PhaseMemUse_%s", getClass()); + inputNodesCount = Debug.metric("PhaseNodes_%s", getClass()); } protected CharSequence getDetailedName() { @@ -73,9 +93,10 @@ } public final void apply(final StructuredGraph graph, final C context, final boolean dumpGraph) { - try (TimerCloseable a = phaseTimer.start(); Scope s = Debug.scope(getClass(), this); Closeable c = phaseMemUseTracker.start()) { + try (TimerCloseable a = timer.start(); Scope s = Debug.scope(getClass(), this); Closeable c = memUseTracker.start()) { BasePhase.this.run(graph, context); - phaseMetric.increment(); + executionCount.increment(); + inputNodesCount.add(graph.getNodeCount()); if (dumpGraph && Debug.isDumpEnabled()) { Debug.dump(graph, "After phase %s", getName()); } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/SinglePassNodeIterator.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/SinglePassNodeIterator.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/SinglePassNodeIterator.java Mon May 19 17:21:30 2014 -0700 @@ -58,9 +58,9 @@ private final NodeBitMap visitedEnds; /** - * @see SinglePassNodeIterator.QElem + * @see SinglePassNodeIterator.PathStart */ - private final Deque> nodeQueue; + private final Deque> nodeQueue; /** * The keys in this map may be: @@ -104,23 +104,32 @@ *
        *
      • a {@link MergeNode} whose pre-state results from merging those of its forward-ends, see * {@link #nextQueuedNode()}
      • - *
      • the successor of a control-split node, in which case the pre-state of the successor in - * question is also stored in the item, see {@link #nextQueuedNode()}
      • + *
      • a successor of a control-split node, in which case the state on entry to it (the + * successor) is also stored in the item, see {@link #nextQueuedNode()}
      • *
      *

      */ - private static class QElem { - private final FixedNode node; - private final U preState; + private static class PathStart { + private final BeginNode node; + private final U stateOnEntry; - private QElem(FixedNode node, U preState) { + private PathStart(BeginNode node, U stateOnEntry) { this.node = node; - this.preState = preState; + this.stateOnEntry = stateOnEntry; assert repOK(); } + /** + * @return true iff this instance is internally consistent (ie, its "representation is OK") + */ private boolean repOK() { - return (node instanceof MergeNode && preState == null) || (node instanceof BeginNode && preState != null); + if (node == null) { + return false; + } + if (node instanceof MergeNode) { + return stateOnEntry == null; + } + return (stateOnEntry != null); } } @@ -188,11 +197,29 @@ finished(); } + /** + * Two methods enqueue items in {@link #nodeQueue}. Of them, only this method enqueues items + * with non-null state (the other method being {@link #queueMerge(EndNode)}). + * + *

      + * A space optimization is made: the state is cloned for all successors except the first. Given + * that right after invoking this method, {@link #nextQueuedNode()} is invoked, that single + * non-cloned state instance is in effect "handed over" to its next owner (thus realizing an + * owner-is-mutator access protocol). + *

      + */ private void queueSuccessors(FixedNode x) { - for (Node node : x.successors()) { - if (node != null) { - nodeQueue.addFirst(new QElem<>((BeginNode) node, state)); - } + Iterator iter = x.successors().nonNull().iterator(); + if (iter.hasNext()) { + BeginNode begin = (BeginNode) iter.next(); + // the current state isn't cloned for the first successor + // conceptually, the state is handed over to it + nodeQueue.addFirst(new PathStart<>(begin, state)); + } + while (iter.hasNext()) { + BeginNode begin = (BeginNode) iter.next(); + // for all other successors it is cloned + nodeQueue.addFirst(new PathStart<>(begin, state.clone())); } } @@ -210,10 +237,10 @@ if (nodeQueue.isEmpty()) { return null; } - QElem elem = nodeQueue.removeFirst(); + PathStart elem = nodeQueue.removeFirst(); if (elem.node instanceof MergeNode) { MergeNode merge = (MergeNode) elem.node; - state = pruneEntry(merge.forwardEndAt(0)).clone(); + state = pruneEntry(merge.forwardEndAt(0)); ArrayList states = new ArrayList<>(merge.forwardEndCount() - 1); for (int i = 1; i < merge.forwardEndCount(); i++) { T other = pruneEntry(merge.forwardEndAt(i)); @@ -223,9 +250,9 @@ assert ready : "Not a single-pass iterator after all"; return merge; } else { - BeginNode begin = (BeginNode) elem.node; + BeginNode begin = elem.node; assert begin.predecessor() != null; - state = elem.preState.clone(); + state = elem.stateOnEntry; state.afterSplit(begin); return begin; } @@ -292,7 +319,7 @@ } } if (endsVisited) { - nodeQueue.add(new QElem<>(merge, null)); + nodeQueue.add(new PathStart<>(merge, null)); } } @@ -338,6 +365,7 @@ private void keepForLater(FixedNode x, T s) { assert !nodeStates.containsKey(x); assert (x instanceof LoopBeginNode) || (x instanceof LoopEndNode) || (x instanceof EndNode); + assert s != null; nodeStates.put(x, s); } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java Mon May 19 17:21:30 2014 -0700 @@ -224,7 +224,8 @@ if (pendingStateAfter != null) { pendingStateAfter.applyToNonVirtual(new NodeClosure() { public void apply(Node usage, Node nonVirtualNode) { - assert currentState.isMarked(nonVirtualNode) : nonVirtualNode + " not available at virtualstate " + usage + " at end of block " + block + " \n" + list; + assert currentState.isMarked(nonVirtualNode) || nonVirtualNode instanceof VirtualObjectNode : nonVirtualNode + " not available at virtualstate " + usage + + " at end of block " + block + " \n" + list; } }); } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.test/src/com/oracle/graal/test/AnsiTerminalDecorator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/AnsiTerminalDecorator.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.test; + +import org.junit.runner.*; +import org.junit.runner.notification.*; + +import static com.oracle.graal.debug.AnsiColor.*; + +/** + * Color support for JUnit test output using ANSI escapes codes. + */ +public class AnsiTerminalDecorator extends GraalJUnitRunListenerDecorator { + + public AnsiTerminalDecorator(GraalJUnitRunListener l) { + super(l); + } + + @Override + public void testSucceeded(Description description) { + getWriter().print(GREEN); + super.testSucceeded(description); + getWriter().print(RESET); + } + + @Override + public void testFailed(Failure failure) { + getWriter().print(RED); + super.testFailed(failure); + getWriter().print(RESET); + } + + @Override + public void testIgnored(Description description) { + getWriter().print(MAGENTA); + super.testIgnored(description); + getWriter().print(RESET); + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.test/src/com/oracle/graal/test/EagerStackTraceDecorator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/EagerStackTraceDecorator.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.test; + +import org.junit.runner.notification.*; + +public class EagerStackTraceDecorator extends GraalJUnitRunListenerDecorator { + + public EagerStackTraceDecorator(GraalJUnitRunListener l) { + super(l); + } + + @Override + public void testFailed(Failure failure) { + super.testFailed(failure); + failure.getException().printStackTrace(getWriter()); + } + +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java --- a/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java Mon May 19 17:21:30 2014 -0700 @@ -49,6 +49,8 @@ List missingClasses = new ArrayList<>(); boolean verbose = false; boolean enableTiming = false; + boolean color = false; + boolean eagerStackTrace = false; for (String each : args) { if (each.charAt(0) == '-') { // command line arguments @@ -56,6 +58,10 @@ verbose = true; } else if (each.contentEquals("-JUnitEnableTiming")) { enableTiming = true; + } else if (each.contentEquals("-JUnitColor")) { + color = true; + } else if (each.contentEquals("-JUnitEagerStackTrace")) { + eagerStackTrace = true; } else { system.out().println("Unknown command line argument: " + each); } @@ -80,6 +86,12 @@ if (enableTiming) { graalListener = new TimingDecorator(graalListener); } + if (color) { + graalListener = new AnsiTerminalDecorator(graalListener); + } + if (eagerStackTrace) { + graalListener = new EagerStackTraceDecorator(graalListener); + } junitCore.addListener(GraalTextListener.createRunListener(graalListener)); Result result = junitCore.run(classes.toArray(new Class[0])); for (Failure each : missingClasses) { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTest.java --- a/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTest.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTest.java Mon May 19 17:21:30 2014 -0700 @@ -136,6 +136,8 @@ Assert.assertEquals((double) expected, (double) actual, delta); } else if (expectedClass.equals(float.class) && actualClass.equals(float.class)) { Assert.assertEquals((float) expected, (float) actual, delta); + } else { + Assert.assertEquals(message, expected, actual); } } else { Assert.assertEquals(message, expected, actual); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalVerboseTextListener.java --- a/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalVerboseTextListener.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalVerboseTextListener.java Mon May 19 17:21:30 2014 -0700 @@ -65,8 +65,7 @@ @Override public void testFailed(Failure failure) { - getWriter().println("FAILED"); - failure.getException().printStackTrace(getWriter()); + getWriter().print("FAILED"); } @Override diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java --- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Mon May 19 17:21:30 2014 -0700 @@ -284,7 +284,6 @@ if (frames.hasNext()) { return new HotSpotFrameInstance.CallTargetFrame(frames.next(), true); } else { - System.out.println("no current frame found"); return null; } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java --- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java Mon May 19 17:21:30 2014 -0700 @@ -62,6 +62,20 @@ } @Test + public void testMulHigh() { + test("mulHigh", 7, 15); + test("mulHigh", Integer.MAX_VALUE, 15); + test("mulHigh", Integer.MIN_VALUE, 15); + } + + @Test + public void testMulHighUnsigned() { + test("mulHighUnsigned", 7, 15); + test("mulHighUnsigned", Integer.MAX_VALUE, 15); + test("mulHighUnsigned", Integer.MIN_VALUE, 15); + } + + @Test public void testLongAdd() { test("longAdd", (long) Integer.MAX_VALUE, 2L); test("longAdd", Long.MAX_VALUE, 2L); @@ -80,6 +94,20 @@ test("longSub", Long.MIN_VALUE, 2L); } + @Test + public void testLongMulHigh() { + test("longMulHigh", 7L, 15L); + test("longMulHigh", Long.MAX_VALUE, 15L); + test("longMulHigh", Long.MIN_VALUE, 15L); + } + + @Test + public void testLongMulHighUnsigned() { + test("longMulHighUnsigned", 7L, 15L); + test("longMulHighUnsigned", Long.MAX_VALUE, 15L); + test("longMulHighUnsigned", Long.MIN_VALUE, 15L); + } + public static int add(int a, int b) { return ExactMath.addExact(a, b); } @@ -92,6 +120,14 @@ return ExactMath.subtractExact(a, b); } + public static int mulHigh(int a, int b) { + return ExactMath.multiplyHigh(a, b); + } + + public static int mulHighUnsigned(int a, int b) { + return ExactMath.multiplyHighUnsigned(a, b); + } + public static long longAdd(long a, long b) { return ExactMath.addExact(a, b); } @@ -103,4 +139,12 @@ public static long longSub(long a, long b) { return ExactMath.subtractExact(a, b); } + + public static long longMulHigh(long a, long b) { + return ExactMath.multiplyHigh(a, b); + } + + public static long longMulHighUnsigned(long a, long b) { + return ExactMath.multiplyHighUnsigned(a, b); + } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Mon May 19 17:21:30 2014 -0700 @@ -108,7 +108,7 @@ canonicalizer.apply(graph, baseContext); // Intrinsify methods. - new ReplaceIntrinsicsPhase(providers.getReplacements()).apply(graph); + new IncrementalCanonicalizerPhase<>(canonicalizer, new ReplaceIntrinsicsPhase(providers.getReplacements())).apply(graph, baseContext); Debug.dump(graph, "Before inlining"); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/arithmetic/IntegerMulHighNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/arithmetic/IntegerMulHighNode.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.truffle.nodes.arithmetic; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.truffle.api.*; + +@NodeInfo(shortName = "*H") +public class IntegerMulHighNode extends IntegerArithmeticNode { + + public IntegerMulHighNode(ValueNode x, ValueNode y) { + this(x.stamp().unrestricted(), x, y); + } + + public IntegerMulHighNode(Stamp stamp, ValueNode x, ValueNode y) { + super(stamp, x, y); + } + + @Override + public Constant evalConst(Constant... inputs) { + assert inputs.length == 2 && inputs[0].getKind() == inputs[1].getKind(); + switch (inputs[0].getKind()) { + case Int: + return Constant.forInt(ExactMath.multiplyHigh(inputs[0].asInt(), inputs[1].asInt())); + case Long: + return Constant.forLong(ExactMath.multiplyHigh(inputs[0].asLong(), inputs[1].asLong())); + default: + throw GraalInternalError.unimplemented(); + } + } + + @Override + public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) { + Value a = builder.operand(x()); + Value b = builder.operand(y()); + builder.setResult(this, gen.emitMulHigh(a, b)); + } + + @NodeIntrinsic + public static int multiplyHigh(int a, int b) { + return ExactMath.multiplyHigh(a, b); + } + + @NodeIntrinsic + public static long multiplyHigh(long a, long b) { + return ExactMath.multiplyHigh(a, b); + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/arithmetic/UnsignedMulHighNode.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/arithmetic/UnsignedMulHighNode.java Mon May 19 17:21:30 2014 -0700 @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.truffle.nodes.arithmetic; + +import com.oracle.graal.api.meta.*; +import com.oracle.graal.compiler.common.*; +import com.oracle.graal.compiler.common.type.*; +import com.oracle.graal.graph.*; +import com.oracle.graal.lir.gen.*; +import com.oracle.graal.nodes.*; +import com.oracle.graal.nodes.calc.*; +import com.oracle.graal.nodes.spi.*; +import com.oracle.truffle.api.*; + +@NodeInfo(shortName = "|*H|") +public class UnsignedMulHighNode extends IntegerArithmeticNode { + + public UnsignedMulHighNode(ValueNode x, ValueNode y) { + this(x.stamp().unrestricted(), x, y); + } + + public UnsignedMulHighNode(Stamp stamp, ValueNode x, ValueNode y) { + super(stamp, x, y); + } + + @Override + public Constant evalConst(Constant... inputs) { + assert inputs.length == 2 && inputs[0].getKind() == inputs[1].getKind(); + switch (inputs[0].getKind()) { + case Int: + return Constant.forInt(ExactMath.multiplyHighUnsigned(inputs[0].asInt(), inputs[1].asInt())); + case Long: + return Constant.forLong(ExactMath.multiplyHighUnsigned(inputs[0].asLong(), inputs[1].asLong())); + default: + throw GraalInternalError.unimplemented(); + } + } + + @Override + public void generate(NodeMappableLIRBuilder builder, ArithmeticLIRGenerator gen) { + Value a = builder.operand(x()); + Value b = builder.operand(y()); + builder.setResult(this, gen.emitUMulHigh(a, b)); + } + + @NodeIntrinsic + public static int multiplyHighUnsigned(int a, int b) { + return ExactMath.multiplyHighUnsigned(a, b); + } + + @NodeIntrinsic + public static long multiplyHighUnsigned(long a, long b) { + return ExactMath.multiplyHighUnsigned(a, b); + } +} diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/ExactMathSubstitutions.java --- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/ExactMathSubstitutions.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/ExactMathSubstitutions.java Mon May 19 17:21:30 2014 -0700 @@ -62,4 +62,24 @@ public static long multiplyExact(long x, long y) { return IntegerMulExactNode.multiplyExact(x, y); } + + @MethodSubstitution + public static int multiplyHigh(int x, int y) { + return IntegerMulHighNode.multiplyHigh(x, y); + } + + @MethodSubstitution + public static int multiplyHighUnsigned(int x, int y) { + return UnsignedMulHighNode.multiplyHighUnsigned(x, y); + } + + @MethodSubstitution + public static long multiplyHigh(long x, long y) { + return IntegerMulHighNode.multiplyHigh(x, y); + } + + @MethodSubstitution + public static long multiplyHighUnsigned(long x, long y) { + return UnsignedMulHighNode.multiplyHighUnsigned(x, y); + } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/VirtualObjectState.java --- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/VirtualObjectState.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/VirtualObjectState.java Mon May 19 17:21:30 2014 -0700 @@ -45,7 +45,7 @@ this.fieldValues = new NodeInputList<>(this, fieldValues); } - private VirtualObjectState(VirtualObjectNode object, List fieldValues) { + public VirtualObjectState(VirtualObjectNode object, List fieldValues) { super(object); assert object.entryCount() == fieldValues.size(); this.fieldValues = new NodeInputList<>(this, fieldValues); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java --- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java Mon May 19 17:21:30 2014 -0700 @@ -178,15 +178,15 @@ return; } + Invoke invoke = callTargetNode.invoke(); if (!callTargetNode.isStatic()) { assert callTargetNode.receiver().getKind() == wordKind : "changeToWord() missed the receiver"; - targetMethod = wordImplType.resolveMethod(targetMethod); + targetMethod = wordImplType.resolveMethod(targetMethod, invoke.getContextType()); } Operation operation = targetMethod.getAnnotation(Word.Operation.class); assert operation != null : targetMethod; NodeInputList arguments = callTargetNode.arguments(); - Invoke invoke = callTargetNode.invoke(); switch (operation.opcode()) { case NODE_CLASS: diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java --- a/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java Mon May 19 17:21:30 2014 -0700 @@ -113,7 +113,7 @@ if (!isStatic) { ValueNode receiver = arguments.get(argc); if (receiver == node && isWord(node)) { - ResolvedJavaMethod resolvedMethod = wordAccess.wordImplType.resolveMethod(method); + ResolvedJavaMethod resolvedMethod = wordAccess.wordImplType.resolveMethod(method, invoke.getContextType()); verify(resolvedMethod != null, node, invoke.asNode(), "cannot resolve method on Word class: " + MetaUtil.format("%H.%n(%P) %r", method)); Operation operation = resolvedMethod.getAnnotation(Word.Operation.class); verify(operation != null, node, invoke.asNode(), "cannot dispatch on word value to non @Operation annotated method " + resolvedMethod); diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExactMath.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExactMath.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExactMath.java Mon May 19 17:21:30 2014 -0700 @@ -91,4 +91,54 @@ } return r; } + + public static int multiplyHigh(int x, int y) { + long r = (long) x * (long) y; + return (int) (r >> 32); + } + + public static int multiplyHighUnsigned(int x, int y) { + long xl = x & 0xFFFFFFFFL; + long yl = y & 0xFFFFFFFFL; + long r = xl * yl; + return (int) (r >> 32); + } + + public static long multiplyHigh(long x, long y) { + long x0, y0, z0; + long x1, y1, z1, z2, t; + + x0 = x & 0xFFFFFFFFL; + x1 = x >> 32; + + y0 = y & 0xFFFFFFFFL; + y1 = y >> 32; + + z0 = x0 * y0; + t = x1 * y0 + (z0 >>> 32); + z1 = t & 0xFFFFFFFFL; + z2 = t >> 32; + z1 += x0 * y1; + + return x1 * y1 + z2 + (z1 >> 32); + } + + public static long multiplyHighUnsigned(long x, long y) { + long x0, y0, z0; + long x1, y1, z1, z2, t; + + x0 = x & 0xFFFFFFFFL; + x1 = x >>> 32; + + y0 = y & 0xFFFFFFFFL; + y1 = y >>> 32; + + z0 = x0 * y0; + t = x1 * y0 + (z0 >>> 32); + z1 = t & 0xFFFFFFFFL; + z2 = t >>> 32; + z1 += x0 * y1; + + return x1 * y1 + z2 + (z1 >>> 32); + } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Mon May 19 17:21:30 2014 -0700 @@ -506,4 +506,31 @@ throw new RuntimeException(e); } } + + /** + * Returns a user-readable description of the purpose of the Node, or "" if no description is + * available. + */ + public String getDescription() { + NodeInfo info = getClass().getAnnotation(NodeInfo.class); + if (info != null) { + return info.description(); + } + return ""; + } + + /** + * Returns a string representing the language this node has been implemented for. If the + * language is unknown, returns "". + */ + public String getLanguage() { + NodeInfo info = getClass().getAnnotation(NodeInfo.class); + if (info != null && info.language() != null && info.language().length() > 0) { + return info.language(); + } + if (parent != null) { + return parent.getLanguage(); + } + return ""; + } } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeInfo.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeInfo.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeInfo.java Mon May 19 17:21:30 2014 -0700 @@ -35,7 +35,7 @@ /** * Short name representing the node that can be used for debugging. - * + * * @return the short name */ String shortName() default ""; @@ -43,10 +43,26 @@ /** * Provides a rough estimate for the cost of the annotated {@link Node}. This estimate can be * used by runtime systems or guest languages to implement heuristics based on Truffle ASTs. - * + * * @see Node#getCost() * @see NodeCost */ NodeCost cost() default NodeCost.MONOMORPHIC; + /** + * A human readable explanation of the purpose of the annotated {@link Node}. Can be used e.g. + * for debugging or visualization purposes. + * + * @return the description + */ + String description() default ""; + + /** + * A description, providing a user-readable explanation of the source language of the annotated + * {@link Node}. Can be used e.g. for debugging or visualization purposes. Typically this + * information is set only in an abstract base node for the language implementation. + * + * @return the description + */ + String language() default ""; } diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ast/CodeElement.java --- a/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ast/CodeElement.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ast/CodeElement.java Mon May 19 17:21:30 2014 -0700 @@ -112,7 +112,7 @@ /** * Support JDK8 langtools. - * + * * @param annotationType */ public A[] getAnnotationsByType(Class annotationType) { @@ -121,7 +121,7 @@ /** * Support for some JDK8 builds. (remove after jdk8 is released) - * + * * @param annotationType */ public A[] getAnnotations(Class annotationType) { @@ -130,7 +130,7 @@ /** * Support for some JDK8 builds. (remove after jdk8 is released) - * + * * @param annotationType */ public A getAnnotation(Class annotationType) { diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java Mon May 19 17:21:30 2014 -0700 @@ -35,6 +35,7 @@ * type system for all subclasses. */ @TypeSystemReference(SLTypes.class) +@NodeInfo(description = "The abstract base node for all expressions") public abstract class SLExpressionNode extends SLStatementNode { /** diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Mon May 19 17:21:30 2014 -0700 @@ -33,6 +33,7 @@ * builtin functions, the {@link #bodyNode} is a subclass of {@link SLBuiltinNode}. For user-defined * functions, the {@link #bodyNode} is a {@link SLFunctionBodyNode}. */ +@NodeInfo(language = "Simple Language", description = "The root of all Simple Language execution trees") public final class SLRootNode extends RootNode { /** The function body that is executed, and specialized during execution. */ diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Mon May 19 17:21:30 2014 -0700 @@ -30,6 +30,7 @@ * statements, i.e., without returning a value. The {@link VirtualFrame} provides access to the * local variables. */ +@NodeInfo(language = "Simple Language", description = "The abstract base node for all statements") public abstract class SLStatementNode extends Node { /** diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBlockNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBlockNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBlockNode.java Mon May 19 17:21:30 2014 -0700 @@ -30,7 +30,7 @@ /** * A statement node that just executes a list of other statements. */ -@NodeInfo(shortName = "block") +@NodeInfo(shortName = "block", description = "The node implementing a source code block") public final class SLBlockNode extends SLStatementNode { /** diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBreakNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBreakNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBreakNode.java Mon May 19 17:21:30 2014 -0700 @@ -32,7 +32,7 @@ * breaking out. This is done by throwing an {@link SLBreakException exception} that is caught by * the {@link SLWhileNode#executeVoid loop node}. */ -@NodeInfo(shortName = "break") +@NodeInfo(shortName = "break", description = "The node implementing a break statement") public final class SLBreakNode extends SLStatementNode { @Override diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLContinueNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLContinueNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLContinueNode.java Mon May 19 17:21:30 2014 -0700 @@ -32,7 +32,7 @@ * are continuing. This is done by throwing an {@link SLContinueException exception} that is caught * by the {@link SLWhileNode#executeVoid loop node}. */ -@NodeInfo(shortName = "continue") +@NodeInfo(shortName = "continue", description = "The node implementing a continue statement") public final class SLContinueNode extends SLStatementNode { @Override diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLIfNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLIfNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLIfNode.java Mon May 19 17:21:30 2014 -0700 @@ -28,7 +28,7 @@ import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.sl.nodes.*; -@NodeInfo(shortName = "if") +@NodeInfo(shortName = "if", description = "The node implementing a condional statement") public final class SLIfNode extends SLStatementNode { /** diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLReturnNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLReturnNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLReturnNode.java Mon May 19 17:21:30 2014 -0700 @@ -34,7 +34,7 @@ * caught by the {@link SLFunctionBodyNode#executeGeneric function body}. The exception transports * the return value. */ -@NodeInfo(shortName = "return") +@NodeInfo(shortName = "return", description = "The node implementing a return statement") public final class SLReturnNode extends SLStatementNode { @Child private SLExpressionNode valueNode; diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLWhileNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLWhileNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLWhileNode.java Mon May 19 17:21:30 2014 -0700 @@ -29,7 +29,7 @@ import com.oracle.truffle.api.utilities.*; import com.oracle.truffle.sl.nodes.*; -@NodeInfo(shortName = "while") +@NodeInfo(shortName = "while", description = "The node implementing a while loop") public final class SLWhileNode extends SLStatementNode { /** diff -r 8c34e2cc4add -r bdf260d8e163 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/expression/SLMulNode.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/expression/SLMulNode.java Mon May 19 17:14:36 2014 -0700 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/expression/SLMulNode.java Mon May 19 17:21:30 2014 -0700 @@ -25,6 +25,7 @@ import java.math.*; import com.oracle.truffle.api.*; +import com.oracle.truffle.api.CompilerDirectives.*; import com.oracle.truffle.api.dsl.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.sl.nodes.*; @@ -41,6 +42,7 @@ } @Specialization + @SlowPath protected BigInteger mul(BigInteger left, BigInteger right) { return left.multiply(right); } diff -r 8c34e2cc4add -r bdf260d8e163 mx/mx_graal.py --- a/mx/mx_graal.py Mon May 19 17:14:36 2014 -0700 +++ b/mx/mx_graal.py Mon May 19 17:21:30 2014 -0700 @@ -957,7 +957,7 @@ f_testfile.close() harness(projectscp, vmArgs) -def _unittest(args, annotations, prefixcp="", whitelist=None, verbose=False, enable_timing=False, regex=None): +def _unittest(args, annotations, prefixcp="", whitelist=None, verbose=False, enable_timing=False, regex=None, color=False, eager_stacktrace=False): mxdir = dirname(__file__) name = 'JUnitWrapper' javaSource = join(mxdir, name + '.java') @@ -976,6 +976,10 @@ coreArgs.append('-JUnitVerbose') if enable_timing: coreArgs.append('-JUnitEnableTiming') + if color: + coreArgs.append('-JUnitColor') + if eager_stacktrace: + coreArgs.append('-JUnitEagerStackTrace') def harness(projectscp, vmArgs): @@ -1006,6 +1010,8 @@ --verbose enable verbose JUnit output --enable-timing enable JUnit test timing --regex run only testcases matching a regular expression + --color enable colors output + --eager-stacktrace print stacktrace eagerly To avoid conflicts with VM options '--' can be used as delimiter. @@ -1046,6 +1052,8 @@ parser.add_argument('--verbose', help='enable verbose JUnit output', action='store_true') parser.add_argument('--enable-timing', help='enable JUnit test timing', action='store_true') parser.add_argument('--regex', help='run only testcases matching a regular expression', metavar='') + parser.add_argument('--color', help='enable color output', action='store_true') + parser.add_argument('--eager-stacktrace', help='print stacktrace eagerly', action='store_true') ut_args = [] delimiter = False @@ -1064,15 +1072,14 @@ # parse all know arguments parsed_args, args = parser.parse_known_args(ut_args) - whitelist = None if parsed_args.whitelist: try: with open(join(_graal_home, parsed_args.whitelist)) as fp: - whitelist = [re.compile(fnmatch.translate(l.rstrip())) for l in fp.readlines() if not l.startswith('#')] + parsed_args.whitelist = [re.compile(fnmatch.translate(l.rstrip())) for l in fp.readlines() if not l.startswith('#')] except IOError: mx.log('warning: could not read whitelist: ' + parsed_args.whitelist) - _unittest(args, ['@Test', '@Parameters'], whitelist=whitelist, verbose=parsed_args.verbose, enable_timing=parsed_args.enable_timing, regex=parsed_args.regex) + _unittest(args, ['@Test', '@Parameters'], **parsed_args.__dict__) def shortunittest(args): """alias for 'unittest --whitelist test/whitelist_shortunittest.txt'{0}""" @@ -1535,7 +1542,7 @@ if not mx.library(name, fatalIfMissing=False): mx.log('Skipping ' + groupId + '.' + artifactId + '.jar as ' + name + ' cannot be resolved') return - d = mx.Distribution(graalSuite, name=artifactId, path=path, sourcesPath=path, deps=deps, excludedDependencies=[]) + d = mx.Distribution(graalSuite, name=artifactId, path=path, sourcesPath=path, deps=deps, excludedDependencies=[], distDependencies=[]) d.make_archive() cmd = ['mvn', 'install:install-file', '-DgroupId=' + groupId, '-DartifactId=' + artifactId, '-Dversion=1.0-SNAPSHOT', '-Dpackaging=jar', '-Dfile=' + d.path] diff -r 8c34e2cc4add -r bdf260d8e163 mx/projects --- a/mx/projects Mon May 19 17:14:36 2014 -0700 +++ b/mx/projects Mon May 19 17:21:30 2014 -0700 @@ -2,8 +2,7 @@ mxversion=1.0 suite=graal -library@JDK_TOOLS@path=${JAVA_HOME}/lib/tools.jar -library@JDK_TOOLS@optional=true +jrelibrary@JFR@jar=jfr.jar library@JUNIT@path=lib/junit-4.11.jar library@JUNIT@urls=http://repo1.maven.org/maven2/junit/junit/4.11/junit-4.11.jar @@ -75,6 +74,7 @@ com.oracle.graal.truffle.hotspot.amd64,\ com.oracle.graal.hotspot.sparc,\ com.oracle.graal.hotspot,\ +com.oracle.graal.hotspot.jfr,\ com.oracle.graal.hotspot.hsail distribution@GRAAL@exclude=FINDBUGS @@ -87,7 +87,7 @@ distribution@TRUFFLE-DSL-PROCESSOR@sourcesPath=truffle-dsl-processor-sources.jar distribution@TRUFFLE-DSL-PROCESSOR@dependencies=\ com.oracle.truffle.dsl.processor -distribution@TRUFFLE-DSL-PROCESSOR@distDependency=TRUFFLE +distribution@TRUFFLE-DSL-PROCESSOR@distDependencies=TRUFFLE # graal.api.collections project@com.oracle.graal.api.collections@subDir=graal @@ -191,6 +191,16 @@ project@com.oracle.graal.hotspot@javaCompliance=1.8 project@com.oracle.graal.hotspot@workingSets=Graal,HotSpot +# graal.hotspot.jfr +project@com.oracle.graal.hotspot.jfr@subDir=graal +project@com.oracle.graal.hotspot.jfr@sourceDirs=src +project@com.oracle.graal.hotspot.jfr@dependencies=com.oracle.graal.hotspot,JFR +project@com.oracle.graal.hotspot.jfr@checkstyle=com.oracle.graal.graph +project@com.oracle.graal.hotspot.jfr@annotationProcessors=com.oracle.graal.service.processor +project@com.oracle.graal.hotspot.jfr@javaCompliance=1.8 +project@com.oracle.graal.hotspot.jfr@profile= +project@com.oracle.graal.hotspot.jfr@workingSets=Graal,HotSpot + # graal.hotspot.amd64 project@com.oracle.graal.hotspot.amd64@subDir=graal project@com.oracle.graal.hotspot.amd64@sourceDirs=src @@ -568,7 +578,7 @@ # graal.test project@com.oracle.graal.test@subDir=graal project@com.oracle.graal.test@sourceDirs=src -project@com.oracle.graal.test@dependencies=JUNIT +project@com.oracle.graal.test@dependencies=JUNIT,com.oracle.graal.debug project@com.oracle.graal.test@checkstyle=com.oracle.graal.graph project@com.oracle.graal.test@javaCompliance=1.8 project@com.oracle.graal.test@workingSets=Graal,Test diff -r 8c34e2cc4add -r bdf260d8e163 mxtool/mx.py --- a/mxtool/mx.py Mon May 19 17:14:36 2014 -0700 +++ b/mxtool/mx.py Mon May 19 17:21:30 2014 -0700 @@ -49,6 +49,7 @@ _projects = dict() _libs = dict() +_jreLibs = dict() _dists = dict() _suites = dict() _annotationProcessors = None @@ -62,7 +63,7 @@ A distribution is a jar or zip file containing the output from one or more Java projects. """ class Distribution: - def __init__(self, suite, name, path, sourcesPath, deps, excludedDependencies, distDependency): + def __init__(self, suite, name, path, sourcesPath, deps, excludedDependencies, distDependencies): self.suite = suite self.name = name self.path = path.replace('/', os.sep) @@ -71,7 +72,7 @@ self.deps = deps self.update_listeners = set() self.excludedDependencies = excludedDependencies - self.distDependency = distDependency + self.distDependencies = distDependencies def sorted_deps(self, includeLibs=False): try: @@ -123,18 +124,22 @@ for arcname in lp.namelist(): overwriteCheck(srcArc.zf, arcname, lpath + '!' + arcname) srcArc.zf.writestr(arcname, lp.read(arcname)) - else: + elif dep.isProject(): p = dep - if self.distDependency and p in _dists[self.distDependency].sorted_deps(): - logv("Excluding {0} from {1} because it's provided by the dependency {2}".format(p.name, self.path, self.distDependency)) + isCoveredByDependecy = False + for d in self.distDependencies: + if p in _dists[d].sorted_deps(): + logv("Excluding {0} from {1} because it's provided by the dependency {2}".format(p.name, self.path, d)) + isCoveredByDependecy = True + break + + if isCoveredByDependecy: continue # skip a Java project if its Java compliance level is "higher" than the configured JDK jdk = java(p.javaCompliance) - if not jdk: - log('Excluding {0} from {2} (Java compliance level {1} required)'.format(p.name, p.javaCompliance, self.path)) - continue + assert jdk logv('[' + self.path + ': adding project ' + p.name + ']') outputDir = p.output_dir() @@ -201,6 +206,9 @@ def isLibrary(self): return isinstance(self, Library) + def isJreLibrary(self): + return isinstance(self, JreLibrary) + def isProject(self): return isinstance(self, Project) @@ -229,7 +237,7 @@ if not exists(s): os.mkdir(s) - def all_deps(self, deps, includeLibs, includeSelf=True, includeAnnotationProcessors=False): + def all_deps(self, deps, includeLibs, includeSelf=True, includeJreLibs=False, includeAnnotationProcessors=False): """ Add the transitive set of dependencies for this project, including libraries if 'includeLibs' is true, to the 'deps' list. @@ -242,8 +250,8 @@ for name in childDeps: assert name != self.name dep = dependency(name) - if not dep in deps and (includeLibs or not dep.isLibrary()): - dep.all_deps(deps, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors) + if not dep in deps and (dep.isProject or (dep.isLibrary() and includeLibs) or (dep.isJreLibrary() and includeJreLibs)): + dep.all_deps(deps, includeLibs=includeLibs, includeJreLibs=includeJreLibs, includeAnnotationProcessors=includeAnnotationProcessors) if not self in deps and includeSelf: deps.append(self) return deps @@ -506,17 +514,74 @@ return path -class Library(Dependency): - def __init__(self, suite, name, path, mustExist, urls, sha1, sourcePath, sourceUrls, sourceSha1, deps): +class BaseLibrary(Dependency): + def __init__(self, suite, name, optional): Dependency.__init__(self, suite, name) + self.optional = optional + + def __ne__(self, other): + result = self.__eq__(other) + if result is NotImplemented: + return result + return not result + +""" +A library that will be provided by the JDK but may be absent. +Any project or normal library that depends on a missing library +will be removed from the global project and library dictionaries +(i.e., _projects and _libs). + +This mechanism exists primarily to be able to support code +that may use functionality in one JDK (e.g., Oracle JDK) +that is not present in another JDK (e.g., OpenJDK). A +motivating example is the Java Flight Recorder library +found in the Oracle JDK. +""" +class JreLibrary(BaseLibrary): + def __init__(self, suite, name, jar, optional): + BaseLibrary.__init__(self, suite, name, optional) + self.jar = jar + + def __eq__(self, other): + if isinstance(other, JreLibrary): + return self.jar == other.jar + else: + return NotImplemented + + def is_present_in_jdk(self, jdk): + for e in jdk.bootclasspath().split(os.pathsep): + if basename(e) == self.jar: + return True + for d in jdk.extdirs().split(os.pathsep): + if len(d) and self.jar in os.listdir(d): + return True + for d in jdk.endorseddirs().split(os.pathsep): + if len(d) and self.jar in os.listdir(d): + return True + return False + + def all_deps(self, deps, includeLibs, includeSelf=True, includeJreLibs=False, includeAnnotationProcessors=False): + """ + Add the transitive set of dependencies for this JRE library to the 'deps' list. + """ + if includeJreLibs and includeSelf and not self in deps: + deps.append(self) + return deps + +class Library(BaseLibrary): + def __init__(self, suite, name, path, optional, urls, sha1, sourcePath, sourceUrls, sourceSha1, deps): + BaseLibrary.__init__(self, suite, name, optional) self.path = path.replace('/', os.sep) self.urls = urls self.sha1 = sha1 - self.mustExist = mustExist self.sourcePath = sourcePath self.sourceUrls = sourceUrls self.sourceSha1 = sourceSha1 self.deps = deps + abspath = _make_absolute(self.path, self.suite.dir) + if not optional and not exists(abspath): + if not len(urls): + abort('Non-optional library {} must either exist at {} or specify one or more URLs from which it can be retrieved'.format(name, abspath)) for url in urls: if url.endswith('/') != self.path.endswith(os.sep): abort('Path for dependency directory must have a URL ending with "/": path=' + self.path + ' url=' + url) @@ -530,14 +595,6 @@ else: return NotImplemented - - def __ne__(self, other): - result = self.__eq__(other) - if result is NotImplemented: - return result - return not result - - def get_path(self, resolve): path = _make_absolute(self.path, self.suite.dir) sha1path = path + '.sha1' @@ -546,8 +603,7 @@ if includedInJDK and java().javaCompliance >= JavaCompliance(includedInJDK): return None - return _download_file_with_sha1(self.name, path, self.urls, self.sha1, sha1path, resolve, self.mustExist) - + return _download_file_with_sha1(self.name, path, self.urls, self.sha1, sha1path, resolve, not self.optional) def get_source_path(self, resolve): if self.sourcePath is None: @@ -562,7 +618,7 @@ if path and (exists(path) or not resolve): cp.append(path) - def all_deps(self, deps, includeLibs, includeSelf=True, includeAnnotationProcessors=False): + def all_deps(self, deps, includeLibs, includeSelf=True, includeJreLibs=False, includeAnnotationProcessors=False): """ Add the transitive set of dependencies for this library to the 'deps' list. """ @@ -575,7 +631,7 @@ assert name != self.name dep = library(name) if not dep in deps: - dep.all_deps(deps, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors) + dep.all_deps(deps, includeLibs=includeLibs, includeJreLibs=includeJreLibs, includeAnnotationProcessors=includeAnnotationProcessors) if not self in deps and includeSelf: deps.append(self) return deps @@ -631,6 +687,7 @@ self.mxDir = mxDir self.projects = [] self.libs = [] + self.jreLibs = [] self.dists = [] self.commands = None self.primary = primary @@ -648,6 +705,7 @@ def _load_projects(self): libsMap = dict() + jreLibsMap = dict() projsMap = dict() distsMap = dict() projectsFile = join(self.mxDir, 'projects') @@ -697,6 +755,8 @@ m = projsMap elif kind == 'library': m = libsMap + elif kind == 'jrelibrary': + m = jreLibsMap elif kind == 'distribution': m = distsMap else: @@ -737,16 +797,24 @@ p.__dict__.update(attrs) self.projects.append(p) + for name, attrs in jreLibsMap.iteritems(): + jar = attrs.pop('jar') + # JRE libraries are optional by default + optional = attrs.pop('optional', 'true') != 'false' + l = JreLibrary(self, name, jar, optional) + self.jreLibs.append(l) + for name, attrs in libsMap.iteritems(): path = attrs.pop('path') - mustExist = attrs.pop('optional', 'false') != 'true' urls = pop_list(attrs, 'urls') sha1 = attrs.pop('sha1', None) sourcePath = attrs.pop('sourcePath', None) sourceUrls = pop_list(attrs, 'sourceUrls') sourceSha1 = attrs.pop('sourceSha1', None) deps = pop_list(attrs, 'dependencies') - l = Library(self, name, path, mustExist, urls, sha1, sourcePath, sourceUrls, sourceSha1, deps) + # Add support optional libraries once we have a good use case + optional = False + l = Library(self, name, path, optional, urls, sha1, sourcePath, sourceUrls, sourceSha1, deps) l.__dict__.update(attrs) self.libs.append(l) @@ -755,8 +823,8 @@ sourcesPath = attrs.pop('sourcesPath', None) deps = pop_list(attrs, 'dependencies') exclDeps = pop_list(attrs, 'exclude') - distDep = attrs.pop('distDependency', None) - d = Distribution(self, name, path, sourcesPath, deps, exclDeps, distDep) + distDeps = pop_list(attrs, 'distDependencies') + d = Distribution(self, name, path, sourcesPath, deps, exclDeps, distDeps) d.__dict__.update(attrs) self.dists.append(d) @@ -836,6 +904,12 @@ if existing is not None and existing != l: abort('inconsistent library redefinition of ' + l.name + ' in ' + existing.suite.dir + ' and ' + l.suite.dir) _libs[l.name] = l + for l in self.jreLibs: + existing = _jreLibs.get(l.name) + # Check that suites that define same library are consistent + if existing is not None and existing != l: + abort('inconsistent JRE library redefinition of ' + l.name + ' in ' + existing.suite.dir + ' and ' + l.suite.dir) + _jreLibs[l.name] = l for d in self.dists: existing = _dists.get(d.name) if existing is not None: @@ -844,6 +918,54 @@ warn('distribution ' + d.name + ' redefined') d.path = existing.path _dists[d.name] = d + + # Remove projects and libraries that (recursively) depend on an optional library + # whose artifact does not exist or on a JRE library that is not present in the + # JDK for a project. Also remove projects whose Java compliance requirement + # cannot be satisfied by the configured JDKs. + # + # Removed projects and libraries are also removed from + # distributions in they are listed as dependencies. + for d in sorted_deps(includeLibs=True): + if d.isLibrary(): + if d.optional: + try: + d.optional = False + path = d.get_path(resolve=True) + except SystemExit: + path = None + finally: + d.optional = True + if not path: + logv('[omitting optional library {} as {} does not exist]'.format(d, d.path)) + del _libs[d.name] + self.libs.remove(d) + elif d.isProject(): + if java(d.javaCompliance) is None: + logv('[omitting project {} as Java compliance {} cannot be satisfied by configured JDKs]'.format(d, d.javaCompliance)) + del _projects[d.name] + self.projects.remove(d) + else: + for name in list(d.deps): + jreLib = _jreLibs.get(name) + if jreLib: + if not jreLib.is_present_in_jdk(java(d.javaCompliance)): + if jreLib.optional: + logv('[omitting project {} as dependency {} is missing]'.format(d, name)) + del _projects[d.name] + self.projects.remove(d) + else: + abort('JRE library {} required by {} not found'.format(jreLib, d)) + elif not dependency(name, fatalIfMissing=False): + logv('[omitting project {} as dependency {} is missing]'.format(d, name)) + del _projects[d.name] + self.projects.remove(d) + for dist in _dists.values(): + for name in list(dist.deps): + if not dependency(name, fatalIfMissing=False): + logv('[omitting {} from distribution {}]'.format(name, dist)) + dist.deps.remove(name) + if hasattr(self, 'mx_post_parse_cmd_line'): self.mx_post_parse_cmd_line(opts) @@ -1025,6 +1147,8 @@ d = _projects.get(name) if d is None: d = _libs.get(name) + if d is None: + d = _jreLibs.get(name) if d is None and fatalIfMissing: if name in _opts.ignored_projects: abort('project named ' + name + ' is ignored') @@ -1381,11 +1505,11 @@ sub = _addSubprocess(p, args) if callable(out): t = Thread(target=redirect, args=(p.stdout, out)) - t.daemon = True # thread dies with the program + # Don't make the reader thread a daemon otherwise output can be droppped t.start() if callable(err): t = Thread(target=redirect, args=(p.stderr, err)) - t.daemon = True # thread dies with the program + # Don't make the reader thread a daemon otherwise output can be droppped t.start() if timeout is None or timeout == 0: retcode = waitOn(p) @@ -1540,6 +1664,8 @@ self.javadoc = exe_suffix(join(self.jdk, 'bin', 'javadoc')) self.toolsjar = join(self.jdk, 'lib', 'tools.jar') self._bootclasspath = None + self._extdirs = None + self._endorseddirs = None if not exists(self.java): abort('Java launcher does not exist: ' + self.java) @@ -1708,8 +1834,6 @@ if _opts.killwithsigquit: _send_sigquit() - # import traceback - # traceback.print_stack() for p, args in _currentSubprocesses: try: if get_os() == 'windows': @@ -1719,6 +1843,9 @@ except BaseException as e: log('error while killing subprocess {} "{}": {}'.format(p.pid, ' '.join(args), e)) + if _opts and _opts.verbose: + import traceback + traceback.print_stack() raise SystemExit(codeOrMessage) def download(path, urls, verbose=False): @@ -2024,9 +2151,7 @@ # skip building this Java project if its Java compliance level is "higher" than the configured JDK requiredCompliance = p.javaCompliance if p.javaCompliance else JavaCompliance(args.compliance) if args.compliance else None jdk = java(requiredCompliance) - if not jdk: - log('Excluding {0} from build (Java compliance level {1} required)'.format(p.name, requiredCompliance)) - continue + assert jdk outputDir = prepareOutputDirs(p, args.clean) @@ -2622,9 +2747,7 @@ # skip checking this Java project if its Java compliance level is "higher" than the configured JDK jdk = java(p.javaCompliance) - if not jdk: - log('Excluding {0} from checking (Java compliance level {1} required)'.format(p.name, p.javaCompliance)) - continue + assert jdk for sourceDir in sourceDirs: javafilelist = [] @@ -2868,10 +2991,10 @@ elif dep.get_source_path(resolve=True): memento = XMLDoc().element('archive', {'detectRoot' : 'true', 'path' : dep.get_source_path(resolve=True)}).xml(standalone='no') slm.element('container', {'memento' : memento, 'typeId':'org.eclipse.debug.core.containerType.externalArchive'}) - else: + elif dep.isProject(): memento = XMLDoc().element('javaProject', {'name' : dep.name}).xml(standalone='no') slm.element('container', {'memento' : memento, 'typeId':'org.eclipse.jdt.launching.sourceContainer.javaProject'}) - if javaCompliance is None or dep.javaCompliance < javaCompliance: + if javaCompliance is None or dep.javaCompliance > javaCompliance: javaCompliance = dep.javaCompliance if javaCompliance: @@ -3035,9 +3158,7 @@ if p.native: continue - if not java(p.javaCompliance): - log('Excluding {0} (JDK with compliance level {1} not available)'.format(p.name, p.javaCompliance)) - continue + assert java(p.javaCompliance) if not exists(p.dir): os.makedirs(p.dir) @@ -3078,7 +3199,7 @@ libraryDeps -= set(dep.all_deps([], True)) else: libraryDeps.add(dep) - else: + elif dep.isProject(): projectDeps.add(dep) for dep in containerDeps: @@ -3087,8 +3208,6 @@ for dep in libraryDeps: path = dep.path dep.get_path(resolve=True) - if not path or (not exists(path) and not dep.mustExist): - continue # Relative paths for "lib" class path entries have various semantics depending on the Eclipse # version being used (e.g. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274737) so it's @@ -3246,16 +3365,13 @@ for dep in dependency(ap).all_deps([], True): if dep.isLibrary(): if not hasattr(dep, 'eclipse.container') and not hasattr(dep, 'eclipse.project'): - if dep.mustExist: - path = dep.get_path(resolve=True) - if path: - # Relative paths for "lib" class path entries have various semantics depending on the Eclipse - # version being used (e.g. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274737) so it's - # safest to simply use absolute paths. - path = _make_absolute(path, p.suite.dir) - out.element('factorypathentry', {'kind' : 'EXTJAR', 'id' : path, 'enabled' : 'true', 'runInBatchMode' : 'false'}) - files.append(path) - else: + # Relative paths for "lib" class path entries have various semantics depending on the Eclipse + # version being used (e.g. see https://bugs.eclipse.org/bugs/show_bug.cgi?id=274737) so it's + # safest to simply use absolute paths. + path = _make_absolute(dep.get_path(resolve=True), p.suite.dir) + out.element('factorypathentry', {'kind' : 'EXTJAR', 'id' : path, 'enabled' : 'true', 'runInBatchMode' : 'false'}) + files.append(path) + elif dep.isProject(): out.element('factorypathentry', {'kind' : 'WKSPJAR', 'id' : '/' + dep.name + '/' + dep.name + '.jar', 'enabled' : 'true', 'runInBatchMode' : 'false'}) out.close('factorypath') update_file(join(p.dir, '.factorypath'), out.xml(indent='\t', newl='\n')) @@ -3558,10 +3674,7 @@ os.makedirs(join(p.dir, 'nbproject')) jdk = java(p.javaCompliance) - - if not jdk: - log('Excluding {0} (JDK with compliance level {1} not available)'.format(p.name, p.javaCompliance)) - continue + assert jdk jdks.add(jdk) @@ -3602,7 +3715,7 @@ if dep == p: continue - if not dep.isLibrary(): + if dep.isProject(): n = dep.name.replace('.', '_') if firstDep: out.open('references', {'xmlns' : 'http://www.netbeans.org/ns/ant-project-references/1'}) @@ -3737,8 +3850,6 @@ continue if dep.isLibrary(): - if not dep.mustExist: - continue path = dep.get_path(resolve=True) if path: if os.sep == '\\': @@ -3747,7 +3858,7 @@ print >> out, ref + '=' + path libFiles.append(path) - else: + elif dep.isProject(): n = dep.name.replace('.', '_') relDepPath = os.path.relpath(dep.dir, p.dir).replace(os.sep, '/') ref = 'reference.' + n + '.jar' @@ -3814,9 +3925,7 @@ if p.native: continue - if not java(p.javaCompliance): - log('Excluding {0} (JDK with compliance level {1} not available)'.format(p.name, p.javaCompliance)) - continue + assert java(p.javaCompliance) if not exists(p.dir): os.makedirs(p.dir) @@ -3863,10 +3972,9 @@ continue if dep.isLibrary(): - if dep.mustExist: - libraries.add(dep) - moduleXml.element('orderEntry', attributes={'type': 'library', 'name': dep.name, 'level': 'project'}) - else: + libraries.add(dep) + moduleXml.element('orderEntry', attributes={'type': 'library', 'name': dep.name, 'level': 'project'}) + elif dep.isProject(): moduleXml.element('orderEntry', attributes={'type': 'module', 'module-name': dep.name}) moduleXml.close('component') @@ -3938,7 +4046,7 @@ for entry in pDep.all_deps([], True): if entry.isLibrary(): compilerXml.element('entry', attributes={'name': '$PROJECT_DIR$/' + os.path.relpath(entry.path, suite.dir)}) - else: + elif entry.isProject(): assert entry.isProject() compilerXml.element('entry', attributes={'name': '$PROJECT_DIR$/' + os.path.relpath(entry.output_dir(), suite.dir)}) compilerXml.close('processorPath') diff -r 8c34e2cc4add -r bdf260d8e163 src/share/vm/graal/graalCodeInstaller.cpp --- a/src/share/vm/graal/graalCodeInstaller.cpp Mon May 19 17:14:36 2014 -0700 +++ b/src/share/vm/graal/graalCodeInstaller.cpp Mon May 19 17:21:30 2014 -0700 @@ -605,10 +605,11 @@ Klass* context = java_lang_Class::as_Klass(HotSpotResolvedObjectType::javaClass(context_handle)); Klass* subtype = java_lang_Class::as_Klass(HotSpotResolvedObjectType::javaClass(subtype_handle)); - _dependencies->assert_leaf_type(subtype); if (context != subtype) { assert(context->is_abstract(), ""); _dependencies->assert_abstract_with_unique_concrete_subtype(context, subtype); + } else { + _dependencies->assert_leaf_type(subtype); } } diff -r 8c34e2cc4add -r bdf260d8e163 src/share/vm/graal/graalCompilerToVM.cpp --- a/src/share/vm/graal/graalCompilerToVM.cpp Mon May 19 17:14:36 2014 -0700 +++ b/src/share/vm/graal/graalCompilerToVM.cpp Mon May 19 17:21:30 2014 -0700 @@ -468,11 +468,76 @@ return (jlong) (address) result.field_holder(); C2V_END -C2V_VMENTRY(jlong, resolveMethod, (JNIEnv *, jobject, jlong metaspace_klass, jstring name, jstring signature)) +C2V_VMENTRY(jint, getVtableIndexForInterface, (JNIEnv *, jobject, jlong metaspace_klass, jlong metaspace_method)) Klass* klass = (Klass*) metaspace_klass; - Symbol* name_symbol = java_lang_String::as_symbol(JNIHandles::resolve(name), THREAD); - Symbol* signature_symbol = java_lang_String::as_symbol(JNIHandles::resolve(signature), THREAD); - return (jlong) (address) klass->lookup_method(name_symbol, signature_symbol); + Method* method = (Method*) metaspace_method; + + return LinkResolver::vtable_index_of_interface_method(klass, method); +C2V_END + +C2V_VMENTRY(jlong, resolveMethod, (JNIEnv *, jobject, jlong metaspace_klass_receiver, jlong metaspace_method, jlong metaspace_klass_caller)) + Klass* recv_klass = (Klass*) metaspace_klass_receiver; + Klass* caller_klass = (Klass*) metaspace_klass_caller; + Method* method = (Method*) metaspace_method; + + if (recv_klass->oop_is_array() || (InstanceKlass::cast(recv_klass)->is_linked())) { + Klass* holder_klass = method->method_holder(); + Symbol* method_name = method->name(); + Symbol* method_signature = method->signature(); + + + if (holder_klass->is_interface()) { + // do link-time resolution to check all access rules. + methodHandle resolved_method; + LinkResolver::linktime_resolve_interface_method(resolved_method, holder_klass, method_name, method_signature, caller_klass, true, CHECK_AND_CLEAR_0); + if (resolved_method->is_private()) { + return (jlong) NULL; + } + assert(recv_klass->is_subtype_of(holder_klass), ""); + // do actual lookup + methodHandle sel_method; + LinkResolver::lookup_instance_method_in_klasses(sel_method, recv_klass, + resolved_method->name(), + resolved_method->signature(), CHECK_AND_CLEAR_0); + return (jlong) (address) sel_method(); + } else { + // do link-time resolution to check all access rules. + methodHandle resolved_method; + LinkResolver::linktime_resolve_virtual_method(resolved_method, holder_klass, method_name, method_signature, caller_klass, true, CHECK_AND_CLEAR_0); + // do actual lookup (see LinkResolver::runtime_resolve_virtual_method) + int vtable_index = Method::invalid_vtable_index; + Method* selected_method; + + if (resolved_method->method_holder()->is_interface()) { // miranda method + vtable_index = LinkResolver::vtable_index_of_interface_method(holder_klass, resolved_method); + assert(vtable_index >= 0 , "we should have valid vtable index at this point"); + + InstanceKlass* inst = InstanceKlass::cast(recv_klass); + selected_method = inst->method_at_vtable(vtable_index); + } else { + // at this point we are sure that resolved_method is virtual and not + // a miranda method; therefore, it must have a valid vtable index. + assert(!resolved_method->has_itable_index(), ""); + vtable_index = resolved_method->vtable_index(); + // We could get a negative vtable_index for final methods, + // because as an optimization they are they are never put in the vtable, + // unless they override an existing method. + // If we do get a negative, it means the resolved method is the the selected + // method, and it can never be changed by an override. + if (vtable_index == Method::nonvirtual_vtable_index) { + assert(resolved_method->can_be_statically_bound(), "cannot override this method"); + selected_method = resolved_method(); + } else { + // recv_klass might be an arrayKlassOop but all vtables start at + // the same place. The cast is to avoid virtual call and assertion. + InstanceKlass* inst = (InstanceKlass*)recv_klass; + selected_method = inst->method_at_vtable(vtable_index); + } + } + return (jlong) (address) selected_method; + } + } + return (jlong) NULL; C2V_END C2V_VMENTRY(jboolean, hasFinalizableSubclass,(JNIEnv *, jobject, jlong metaspace_klass)) @@ -1058,58 +1123,59 @@ #define METASPACE_SYMBOL "J" JNINativeMethod CompilerToVM_methods[] = { - {CC"initializeBytecode", CC"("METASPACE_METHOD"[B)[B", FN_PTR(initializeBytecode)}, - {CC"exceptionTableStart", CC"("METASPACE_METHOD")J", FN_PTR(exceptionTableStart)}, - {CC"exceptionTableLength", CC"("METASPACE_METHOD")I", FN_PTR(exceptionTableLength)}, - {CC"hasBalancedMonitors", CC"("METASPACE_METHOD")Z", FN_PTR(hasBalancedMonitors)}, - {CC"findUniqueConcreteMethod", CC"("METASPACE_METHOD")"METASPACE_METHOD, FN_PTR(findUniqueConcreteMethod)}, - {CC"getKlassImplementor", CC"("METASPACE_KLASS")"METASPACE_KLASS, FN_PTR(getKlassImplementor)}, - {CC"getStackTraceElement", CC"("METASPACE_METHOD"I)"STACK_TRACE_ELEMENT, FN_PTR(getStackTraceElement)}, - {CC"methodIsIgnoredBySecurityStackWalk", CC"("METASPACE_METHOD")Z", FN_PTR(methodIsIgnoredBySecurityStackWalk)}, - {CC"doNotInlineOrCompile", CC"("METASPACE_METHOD")V", FN_PTR(doNotInlineOrCompile)}, - {CC"canInlineMethod", CC"("METASPACE_METHOD")Z", FN_PTR(canInlineMethod)}, - {CC"shouldInlineMethod", CC"("METASPACE_METHOD")Z", FN_PTR(shouldInlineMethod)}, - {CC"lookupType", CC"("STRING CLASS"Z)"METASPACE_KLASS, FN_PTR(lookupType)}, - {CC"resolveConstantInPool", CC"("METASPACE_CONSTANT_POOL"I)"OBJECT, FN_PTR(resolveConstantInPool)}, - {CC"resolvePossiblyCachedConstantInPool", CC"("METASPACE_CONSTANT_POOL"I)"OBJECT, FN_PTR(resolvePossiblyCachedConstantInPool)}, - {CC"lookupNameRefInPool", CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_SYMBOL, FN_PTR(lookupNameRefInPool)}, - {CC"lookupNameAndTypeRefIndexInPool", CC"("METASPACE_CONSTANT_POOL"I)I", FN_PTR(lookupNameAndTypeRefIndexInPool)}, - {CC"lookupSignatureRefInPool", CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_SYMBOL, FN_PTR(lookupSignatureRefInPool)}, - {CC"lookupKlassRefIndexInPool", CC"("METASPACE_CONSTANT_POOL"I)I", FN_PTR(lookupKlassRefIndexInPool)}, - {CC"constantPoolKlassAt", CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_KLASS, FN_PTR(constantPoolKlassAt)}, - {CC"lookupKlassInPool", CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_KLASS, FN_PTR(lookupKlassInPool)}, - {CC"lookupAppendixInPool", CC"("METASPACE_CONSTANT_POOL"I)"OBJECT, FN_PTR(lookupAppendixInPool)}, - {CC"lookupMethodInPool", CC"("METASPACE_CONSTANT_POOL"IB)"METASPACE_METHOD, FN_PTR(lookupMethodInPool)}, - {CC"constantPoolRemapInstructionOperandFromCache", CC"("METASPACE_CONSTANT_POOL"I)I", FN_PTR(constantPoolRemapInstructionOperandFromCache)}, - {CC"resolveField", CC"("METASPACE_CONSTANT_POOL"IB[J)"METASPACE_KLASS, FN_PTR(resolveField)}, - {CC"resolveInvokeDynamic", CC"("METASPACE_CONSTANT_POOL"I)V", FN_PTR(resolveInvokeDynamic)}, - {CC"resolveMethod", CC"("METASPACE_KLASS STRING STRING")"METASPACE_METHOD, FN_PTR(resolveMethod)}, - {CC"getClassInitializer", CC"("METASPACE_KLASS")"METASPACE_METHOD, FN_PTR(getClassInitializer)}, - {CC"hasFinalizableSubclass", CC"("METASPACE_KLASS")Z", FN_PTR(hasFinalizableSubclass)}, - {CC"getMaxCallTargetOffset", CC"(J)J", FN_PTR(getMaxCallTargetOffset)}, - {CC"getMetaspaceMethod", CC"("CLASS"I)"METASPACE_METHOD, FN_PTR(getMetaspaceMethod)}, - {CC"initializeConfiguration", CC"("HS_CONFIG")V", FN_PTR(initializeConfiguration)}, - {CC"installCode0", CC"("HS_COMPILED_CODE INSTALLED_CODE SPECULATION_LOG")I", FN_PTR(installCode0)}, - {CC"notifyCompilationStatistics", CC"(I"HS_RESOLVED_METHOD"ZIJJ"INSTALLED_CODE")V", FN_PTR(notifyCompilationStatistics)}, - {CC"printCompilationStatistics", CC"(ZZ)V", FN_PTR(printCompilationStatistics)}, - {CC"resetCompilationStatistics", CC"()V", FN_PTR(resetCompilationStatistics)}, - {CC"disassembleCodeBlob", CC"(J)"STRING, FN_PTR(disassembleCodeBlob)}, - {CC"executeCompiledMethodVarargs", CC"(["OBJECT INSTALLED_CODE")"OBJECT, FN_PTR(executeCompiledMethodVarargs)}, - {CC"getLineNumberTable", CC"("METASPACE_METHOD")[J", FN_PTR(getLineNumberTable)}, - {CC"getLocalVariableTableStart", CC"("METASPACE_METHOD")J", FN_PTR(getLocalVariableTableStart)}, - {CC"getLocalVariableTableLength", CC"("METASPACE_METHOD")I", FN_PTR(getLocalVariableTableLength)}, - {CC"reprofile", CC"("METASPACE_METHOD")V", FN_PTR(reprofile)}, - {CC"invalidateInstalledCode", CC"("INSTALLED_CODE")V", FN_PTR(invalidateInstalledCode)}, - {CC"getJavaMirror", CC"("METASPACE_KLASS")"CLASS, FN_PTR(getJavaMirror)}, - {CC"readUnsafeKlassPointer", CC"("OBJECT")J", FN_PTR(readUnsafeKlassPointer)}, - {CC"collectCounters", CC"()[J", FN_PTR(collectCounters)}, - {CC"getGPUs", CC"()"STRING, FN_PTR(getGPUs)}, - {CC"allocateCompileId", CC"("METASPACE_METHOD"I)I", FN_PTR(allocateCompileId)}, - {CC"isMature", CC"("METASPACE_METHOD_DATA")Z", FN_PTR(isMature)}, - {CC"hasCompiledCodeForOSR", CC"("METASPACE_METHOD"II)Z", FN_PTR(hasCompiledCodeForOSR)}, - {CC"getTimeStamp", CC"()J", FN_PTR(getTimeStamp)}, - {CC"getNextStackFrame", CC"("HS_STACK_FRAME_REF "[JI)"HS_STACK_FRAME_REF, FN_PTR(getNextStackFrame)}, - {CC"materializeVirtualObjects", CC"("HS_STACK_FRAME_REF"Z)V", FN_PTR(materializeVirtualObjects)}, + {CC"initializeBytecode", CC"("METASPACE_METHOD"[B)[B", FN_PTR(initializeBytecode)}, + {CC"exceptionTableStart", CC"("METASPACE_METHOD")J", FN_PTR(exceptionTableStart)}, + {CC"exceptionTableLength", CC"("METASPACE_METHOD")I", FN_PTR(exceptionTableLength)}, + {CC"hasBalancedMonitors", CC"("METASPACE_METHOD")Z", FN_PTR(hasBalancedMonitors)}, + {CC"findUniqueConcreteMethod", CC"("METASPACE_METHOD")"METASPACE_METHOD, FN_PTR(findUniqueConcreteMethod)}, + {CC"getKlassImplementor", CC"("METASPACE_KLASS")"METASPACE_KLASS, FN_PTR(getKlassImplementor)}, + {CC"getStackTraceElement", CC"("METASPACE_METHOD"I)"STACK_TRACE_ELEMENT, FN_PTR(getStackTraceElement)}, + {CC"methodIsIgnoredBySecurityStackWalk", CC"("METASPACE_METHOD")Z", FN_PTR(methodIsIgnoredBySecurityStackWalk)}, + {CC"doNotInlineOrCompile", CC"("METASPACE_METHOD")V", FN_PTR(doNotInlineOrCompile)}, + {CC"canInlineMethod", CC"("METASPACE_METHOD")Z", FN_PTR(canInlineMethod)}, + {CC"shouldInlineMethod", CC"("METASPACE_METHOD")Z", FN_PTR(shouldInlineMethod)}, + {CC"lookupType", CC"("STRING CLASS"Z)"METASPACE_KLASS, FN_PTR(lookupType)}, + {CC"resolveConstantInPool", CC"("METASPACE_CONSTANT_POOL"I)"OBJECT, FN_PTR(resolveConstantInPool)}, + {CC"resolvePossiblyCachedConstantInPool", CC"("METASPACE_CONSTANT_POOL"I)"OBJECT, FN_PTR(resolvePossiblyCachedConstantInPool)}, + {CC"lookupNameRefInPool", CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_SYMBOL, FN_PTR(lookupNameRefInPool)}, + {CC"lookupNameAndTypeRefIndexInPool", CC"("METASPACE_CONSTANT_POOL"I)I", FN_PTR(lookupNameAndTypeRefIndexInPool)}, + {CC"lookupSignatureRefInPool", CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_SYMBOL, FN_PTR(lookupSignatureRefInPool)}, + {CC"lookupKlassRefIndexInPool", CC"("METASPACE_CONSTANT_POOL"I)I", FN_PTR(lookupKlassRefIndexInPool)}, + {CC"constantPoolKlassAt", CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_KLASS, FN_PTR(constantPoolKlassAt)}, + {CC"lookupKlassInPool", CC"("METASPACE_CONSTANT_POOL"I)"METASPACE_KLASS, FN_PTR(lookupKlassInPool)}, + {CC"lookupAppendixInPool", CC"("METASPACE_CONSTANT_POOL"I)"OBJECT, FN_PTR(lookupAppendixInPool)}, + {CC"lookupMethodInPool", CC"("METASPACE_CONSTANT_POOL"IB)"METASPACE_METHOD, FN_PTR(lookupMethodInPool)}, + {CC"constantPoolRemapInstructionOperandFromCache", CC"("METASPACE_CONSTANT_POOL"I)I", FN_PTR(constantPoolRemapInstructionOperandFromCache)}, + {CC"resolveField", CC"("METASPACE_CONSTANT_POOL"IB[J)"METASPACE_KLASS, FN_PTR(resolveField)}, + {CC"resolveInvokeDynamic", CC"("METASPACE_CONSTANT_POOL"I)V", FN_PTR(resolveInvokeDynamic)}, + {CC"resolveMethod", CC"("METASPACE_KLASS METASPACE_METHOD METASPACE_KLASS")"METASPACE_METHOD, FN_PTR(resolveMethod)}, + {CC"getVtableIndexForInterface", CC"("METASPACE_KLASS METASPACE_METHOD")I", FN_PTR(getVtableIndexForInterface)}, + {CC"getClassInitializer", CC"("METASPACE_KLASS")"METASPACE_METHOD, FN_PTR(getClassInitializer)}, + {CC"hasFinalizableSubclass", CC"("METASPACE_KLASS")Z", FN_PTR(hasFinalizableSubclass)}, + {CC"getMaxCallTargetOffset", CC"(J)J", FN_PTR(getMaxCallTargetOffset)}, + {CC"getMetaspaceMethod", CC"("CLASS"I)"METASPACE_METHOD, FN_PTR(getMetaspaceMethod)}, + {CC"initializeConfiguration", CC"("HS_CONFIG")V", FN_PTR(initializeConfiguration)}, + {CC"installCode0", CC"("HS_COMPILED_CODE INSTALLED_CODE SPECULATION_LOG")I", FN_PTR(installCode0)}, + {CC"notifyCompilationStatistics", CC"(I"HS_RESOLVED_METHOD"ZIJJ"INSTALLED_CODE")V", FN_PTR(notifyCompilationStatistics)}, + {CC"printCompilationStatistics", CC"(ZZ)V", FN_PTR(printCompilationStatistics)}, + {CC"resetCompilationStatistics", CC"()V", FN_PTR(resetCompilationStatistics)}, + {CC"disassembleCodeBlob", CC"(J)"STRING, FN_PTR(disassembleCodeBlob)}, + {CC"executeCompiledMethodVarargs", CC"(["OBJECT INSTALLED_CODE")"OBJECT, FN_PTR(executeCompiledMethodVarargs)}, + {CC"getLineNumberTable", CC"("METASPACE_METHOD")[J", FN_PTR(getLineNumberTable)}, + {CC"getLocalVariableTableStart", CC"("METASPACE_METHOD")J", FN_PTR(getLocalVariableTableStart)}, + {CC"getLocalVariableTableLength", CC"("METASPACE_METHOD")I", FN_PTR(getLocalVariableTableLength)}, + {CC"reprofile", CC"("METASPACE_METHOD")V", FN_PTR(reprofile)}, + {CC"invalidateInstalledCode", CC"("INSTALLED_CODE")V", FN_PTR(invalidateInstalledCode)}, + {CC"getJavaMirror", CC"("METASPACE_KLASS")"CLASS, FN_PTR(getJavaMirror)}, + {CC"readUnsafeKlassPointer", CC"("OBJECT")J", FN_PTR(readUnsafeKlassPointer)}, + {CC"collectCounters", CC"()[J", FN_PTR(collectCounters)}, + {CC"getGPUs", CC"()"STRING, FN_PTR(getGPUs)}, + {CC"allocateCompileId", CC"("METASPACE_METHOD"I)I", FN_PTR(allocateCompileId)}, + {CC"isMature", CC"("METASPACE_METHOD_DATA")Z", FN_PTR(isMature)}, + {CC"hasCompiledCodeForOSR", CC"("METASPACE_METHOD"II)Z", FN_PTR(hasCompiledCodeForOSR)}, + {CC"getTimeStamp", CC"()J", FN_PTR(getTimeStamp)}, + {CC"getNextStackFrame", CC"("HS_STACK_FRAME_REF "[JI)"HS_STACK_FRAME_REF, FN_PTR(getNextStackFrame)}, + {CC"materializeVirtualObjects", CC"("HS_STACK_FRAME_REF"Z)V", FN_PTR(materializeVirtualObjects)}, }; int CompilerToVM_methods_count() { diff -r 8c34e2cc4add -r bdf260d8e163 src/share/vm/graal/vmStructs_graal.hpp --- a/src/share/vm/graal/vmStructs_graal.hpp Mon May 19 17:14:36 2014 -0700 +++ b/src/share/vm/graal/vmStructs_graal.hpp Mon May 19 17:21:30 2014 -0700 @@ -66,5 +66,7 @@ declare_constant(CodeInstaller::POLL_FAR) \ declare_constant(CodeInstaller::POLL_RETURN_FAR) \ declare_constant(CodeInstaller::INVOKE_INVALID) \ + \ + declare_constant(Method::invalid_vtable_index) \ #endif // SHARE_VM_GRAAL_VMSTRUCTS_GRAAL_HPP diff -r 8c34e2cc4add -r bdf260d8e163 src/share/vm/interpreter/linkResolver.hpp --- a/src/share/vm/interpreter/linkResolver.hpp Mon May 19 17:14:36 2014 -0700 +++ b/src/share/vm/interpreter/linkResolver.hpp Mon May 19 17:21:30 2014 -0700 @@ -125,7 +125,13 @@ private: static void lookup_method_in_klasses (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, bool checkpolymorphism, bool in_imethod_resolve, TRAPS); +#ifdef GRAAL + public: +#endif static void lookup_instance_method_in_klasses (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS); +#ifdef GRAAL + private: +#endif static void lookup_method_in_interfaces (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, TRAPS); static void lookup_polymorphic_method (methodHandle& result, KlassHandle klass, Symbol* name, Symbol* signature, KlassHandle current_klass, Handle *appendix_result_or_null, Handle *method_type_result, TRAPS); @@ -139,8 +145,14 @@ static void linktime_resolve_static_method (methodHandle& resolved_method, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, TRAPS); static void linktime_resolve_special_method (methodHandle& resolved_method, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, TRAPS); +#ifdef GRAAL + public: +#endif static void linktime_resolve_virtual_method (methodHandle &resolved_method, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature,KlassHandle current_klass, bool check_access, TRAPS); static void linktime_resolve_interface_method (methodHandle& resolved_method, KlassHandle resolved_klass, Symbol* method_name, Symbol* method_signature, KlassHandle current_klass, bool check_access, TRAPS); +#ifdef GRAAL + private: +#endif static void runtime_resolve_special_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, KlassHandle current_klass, bool check_access, TRAPS); static void runtime_resolve_virtual_method (CallInfo& result, methodHandle resolved_method, KlassHandle resolved_klass, Handle recv, KlassHandle recv_klass, bool check_null_and_abstract, TRAPS); diff -r 8c34e2cc4add -r bdf260d8e163 src/share/vm/runtime/deoptimization.cpp --- a/src/share/vm/runtime/deoptimization.cpp Mon May 19 17:14:36 2014 -0700 +++ b/src/share/vm/runtime/deoptimization.cpp Mon May 19 17:21:30 2014 -0700 @@ -1348,7 +1348,7 @@ ScopeDesc* trap_scope = cvf->scope(); if (TraceDeoptimization) { - tty->print_cr(" bci=%d pc=%d, relative_pc=%d, method=%s" GRAAL_ONLY(", debug_id=%d"), trap_scope->bci(), fr.pc(), fr.pc() - nm->code_begin(), trap_scope->method()->name_and_sig_as_C_string() + tty->print_cr(" bci=%d pc=" INTPTR_FORMAT ", relative_pc=%d, method=%s" GRAAL_ONLY(", debug_id=%d"), trap_scope->bci(), fr.pc(), fr.pc() - nm->code_begin(), trap_scope->method()->name_and_sig_as_C_string() #ifdef GRAAL , debug_id #endif diff -r 8c34e2cc4add -r bdf260d8e163 src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Mon May 19 17:14:36 2014 -0700 +++ b/src/share/vm/runtime/globals.hpp Mon May 19 17:21:30 2014 -0700 @@ -3058,7 +3058,7 @@ "If non-zero, maximum number of words that malloc/realloc can " \ "allocate (for testing only)") \ \ - product(intx, TypeProfileWidth, 2, \ + product_pd(intx, TypeProfileWidth, \ "Number of receiver types to record in call/cast profile") \ \ product_pd(intx, MethodProfileWidth, \ diff -r 8c34e2cc4add -r bdf260d8e163 src/share/vm/runtime/java.cpp --- a/src/share/vm/runtime/java.cpp Mon May 19 17:14:36 2014 -0700 +++ b/src/share/vm/runtime/java.cpp Mon May 19 17:21:30 2014 -0700 @@ -462,15 +462,6 @@ #define BEFORE_EXIT_DONE 2 static jint volatile _before_exit_status = BEFORE_EXIT_NOT_RUN; -#ifdef GRAAL -#ifdef COMPILERGRAAL - if (GraalCompiler::instance() != NULL) { - GraalCompiler::instance()->shutdown(); - } -#endif - VMToCompiler::shutdownRuntime(); -#endif - // Note: don't use a Mutex to guard the entire before_exit(), as // JVMTI post_thread_end_event and post_vm_death_event will run native code. // A CAS or OSMutex would work just fine but then we need to manipulate @@ -492,6 +483,15 @@ } } +#ifdef GRAAL +#ifdef COMPILERGRAAL + if (GraalCompiler::instance() != NULL) { + GraalCompiler::instance()->shutdown(); + } +#endif + VMToCompiler::shutdownRuntime(); +#endif + // The only difference between this and Win32's _onexit procs is that // this version is invoked before any threads get killed. ExitProc* current = exit_procs;