# HG changeset patch # User Thomas Wuerthinger # Date 1400665550 -7200 # Node ID a6eeb37502384dcfc3886467dc3c92c68bc6da51 # Parent a43ff5d18350adb4af26978fbcb7044451993dc1# Parent b5a993ed67ea44ac4227bb1ab93ca5d771980061 Merge. diff -r a43ff5d18350 -r a6eeb3750238 CHANGELOG.md --- a/CHANGELOG.md Tue May 13 19:19:27 2014 +0200 +++ b/CHANGELOG.md Wed May 21 11:45:50 2014 +0200 @@ -3,6 +3,9 @@ ## `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. +* ResolvedJavaType.resolveMethod now takes a context type used to perform access checks. It now works correctly regarding default methods. ### Truffle * `truffle.jar`: strip out build-time only dependency into a seperated JAR file (`truffle-dsl-processor.jar`) diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/Assumptions.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaField.java --- a/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaField.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaField.java Wed May 21 11:45:50 2014 +0200 @@ -164,6 +164,9 @@ public void testCoverage() { Set known = new HashSet<>(Arrays.asList(untestedApiMethods)); for (Method m : ResolvedJavaField.class.getDeclaredMethods()) { + if (m.isSynthetic()) { + continue; + } if (findTestMethod(m) == null) { assertTrue("test missing for " + m, known.contains(m.getName())); } else { diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaType.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaType.java Wed May 21 11:45:50 2014 +0200 @@ -211,10 +211,11 @@ * {@link #findUniqueConcreteMethod(ResolvedJavaMethod)}. * * @param method the method to select the implementation of + * @param callerType the caller or context type used to perform access checks * @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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/type/ObjectStamp.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/KernelTester.java --- a/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/KernelTester.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/KernelTester.java Wed May 21 11:45:50 2014 +0200 @@ -34,16 +34,19 @@ import java.util.logging.*; import com.amd.okra.*; +import com.oracle.graal.test.*; /** * Abstract class on which the HSAIL unit tests are built. Executes a method or lambda on both the * Java side and the Okra side and compares the results for fields that are annotated with - * {@link KernelTester.Result}. + * {@link Result}. */ -public abstract class KernelTester { +public abstract class KernelTester extends GraalTest { /** - * Denotes a field whose value is to be compared as part of computing the result of a test. + * Denotes a field whose value is to be + * {@linkplain KernelTester#assertResultFieldsEqual(KernelTester) compared} as part of computing + * the result of a test. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @@ -129,171 +132,27 @@ public abstract void runTest(); - // Default comparison is to compare all things marked @Result. - public boolean compareResults(KernelTester base) { + /** + * Asserts that the value of all {@link Result} annotated fields in this object and + * {@code other} are {@linkplain #assertDeepEquals(Object, Object) equal}. + * + * @throws AssertionError if the value of a result field in this and {@code other} are not equal + */ + public void assertResultFieldsEqual(KernelTester other) { Class clazz = this.getClass(); while (clazz != null && clazz != KernelTester.class) { for (Field f : clazz.getDeclaredFields()) { if (!Modifier.isStatic(f.getModifiers())) { Result annos = f.getAnnotation(Result.class); if (annos != null) { - logger.fine("@Result field = " + f); - Object myResult = getFieldFromObject(f, this); - Object otherResult = getFieldFromObject(f, base); - boolean same = compareObjects(myResult, otherResult); - logger.fine("comparing " + myResult + ", " + otherResult + ", match=" + same); - if (!same) { - logger.severe("mismatch comparing " + f + ", " + myResult + " vs. " + otherResult); - logSevere("FAILED!!! " + this.getClass()); - return false; - } + Object actualResult = getFieldFromObject(f, this); + Object expectedResult = getFieldFromObject(f, other); + assertDeepEquals(f.toString(), expectedResult, actualResult); } } } clazz = clazz.getSuperclass(); } - logInfo("PASSED: " + this.getClass()); - return true; - } - - private boolean compareObjects(Object first, Object second) { - if (first == null) { - return (second == null); - } - if (second == null) { - return (first == null); - } - Class clazz = first.getClass(); - if (clazz != second.getClass()) { - return false; - } - if (!clazz.isArray()) { - // Non arrays. - if (clazz.equals(float.class) || clazz.equals(double.class)) { - return isEqualsFP((double) first, (double) second); - } else { - return first.equals(second); - } - } else { - // Handle the case where Objects are arrays. - ArrayComparer comparer; - if (clazz.equals(float[].class) || clazz.equals(double[].class)) { - comparer = new FPArrayComparer(); - } else if (clazz.equals(long[].class) || clazz.equals(int[].class) || clazz.equals(byte[].class)) { - comparer = new IntArrayComparer(); - } else if (clazz.equals(boolean[].class)) { - comparer = new BooleanArrayComparer(); - } else { - comparer = new ObjArrayComparer(); - } - return comparer.compareArrays(first, second); - } - } - - static final int MISMATCHLIMIT = 10; - static final int ELEMENTDISPLAYLIMIT = 20; - - public int getMisMatchLimit() { - return MISMATCHLIMIT; - } - - public int getElementDisplayLimit() { - return ELEMENTDISPLAYLIMIT; - } - - abstract class ArrayComparer { - - abstract Object getElement(Object ary, int index); - - // Equality test, can be overridden - boolean isEquals(Object firstElement, Object secondElement) { - return firstElement.equals(secondElement); - } - - boolean compareArrays(Object first, Object second) { - int len = Array.getLength(first); - if (len != Array.getLength(second)) { - return false; - } - // If info logLevel, build string of first few elements from first array. - if (logLevel.intValue() <= Level.INFO.intValue()) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < Math.min(len, getElementDisplayLimit()); i++) { - sb.append(getElement(first, i)); - sb.append(", "); - } - logger.info(sb.toString()); - } - boolean success = true; - int mismatches = 0; - for (int i = 0; i < len; i++) { - Object firstElement = getElement(first, i); - Object secondElement = getElement(second, i); - if (!isEquals(firstElement, secondElement)) { - logSevere("mismatch at index " + i + ", expected " + secondElement + ", saw " + firstElement); - success = false; - mismatches++; - if (mismatches >= getMisMatchLimit()) { - logSevere("...Truncated"); - break; - } - } - } - return success; - } - } - - class FPArrayComparer extends ArrayComparer { - - @Override - Object getElement(Object ary, int index) { - return Array.getDouble(ary, index); - } - - @Override - boolean isEquals(Object firstElement, Object secondElement) { - return isEqualsFP((double) firstElement, (double) secondElement); - } - } - - class IntArrayComparer extends ArrayComparer { - - @Override - Object getElement(Object ary, int index) { - return Array.getLong(ary, index); - } - } - - class BooleanArrayComparer extends ArrayComparer { - - @Override - Object getElement(Object ary, int index) { - return Array.getBoolean(ary, index); - } - } - - class ObjArrayComparer extends ArrayComparer { - - @Override - Object getElement(Object ary, int index) { - return Array.get(ary, index); - } - - @Override - boolean isEquals(Object firstElement, Object secondElement) { - return compareObjects(firstElement, secondElement); - } - } - - /** - * Tests two floating point values for equality. - */ - public boolean isEqualsFP(double first, double second) { - // Special case for checking whether expected and actual values are both NaNs. - if (Double.isNaN(first) && Double.isNaN(second)) { - return true; - } - return first == second; } public void setDispatchMode(DispatchMode dispatchMode) { @@ -761,8 +620,8 @@ } } - private void compareOkraToSeq(HsailMode hsailModeToUse) { - compareOkraToSeq(hsailModeToUse, false); + private void assertOkraEqualsSeq(HsailMode hsailModeToUse) { + assertOkraEqualsSeq(hsailModeToUse, false); } /** @@ -770,7 +629,7 @@ * runOkraFirst flag controls which order they are done in. Note the compiler must use eager * resolving if Okra is done first. */ - private void compareOkraToSeq(HsailMode hsailModeToUse, boolean useLambda) { + private void assertOkraEqualsSeq(HsailMode hsailModeToUse, boolean useLambda) { KernelTester testerSeq; if (runOkraFirst) { runOkraInstance(hsailModeToUse, useLambda); @@ -779,7 +638,7 @@ testerSeq = runSeqInstance(); runOkraInstance(hsailModeToUse, useLambda); } - assertTrue("failed comparison to SEQ", compareResults(testerSeq)); + assertResultFieldsEqual(testerSeq); } private void runOkraInstance(HsailMode hsailModeToUse, boolean useLambda) { @@ -800,19 +659,19 @@ } public void testGeneratedHsail() { - compareOkraToSeq(HsailMode.COMPILED); + assertOkraEqualsSeq(HsailMode.COMPILED); } public void testGeneratedHsailUsingLambdaMethod() { - compareOkraToSeq(HsailMode.COMPILED, true); + assertOkraEqualsSeq(HsailMode.COMPILED, true); } public void testInjectedHsail() { - newInstance().compareOkraToSeq(HsailMode.INJECT_HSAIL); + newInstance().assertOkraEqualsSeq(HsailMode.INJECT_HSAIL); } public void testInjectedOpencl() { - newInstance().compareOkraToSeq(HsailMode.INJECT_OCL); + newInstance().assertOkraEqualsSeq(HsailMode.INJECT_OCL); } protected static Object getFieldFromObject(Field f, Object fromObj) { diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/BoundsCatchManyBase.java --- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/BoundsCatchManyBase.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/BoundsCatchManyBase.java Wed May 21 11:45:50 2014 +0200 @@ -33,11 +33,6 @@ return (gid < 4096 && gid % 512 == 1); } - @Override - public int getMisMatchLimit() { - return 1000; - } - public void run(int gid) { int outval = getOutval(gid); try { diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/EscapingNewStringConcatTest.java --- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/EscapingNewStringConcatTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/EscapingNewStringConcatTest.java Wed May 21 11:45:50 2014 +0200 @@ -24,7 +24,7 @@ import static com.oracle.graal.debug.Debug.*; -import org.junit.Test; +import org.junit.*; import com.oracle.graal.debug.*; @@ -51,7 +51,8 @@ } // Node implementing Lowerable not handled in HSAIL Backend: 6274|MonitorEnter - @Test(expected = com.oracle.graal.compiler.common.GraalInternalError.class) + @Ignore + @Test public void test() { try (DebugConfigScope s = disableIntercept()) { testGeneratedHsail(); diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatDivPrecisionTest.java --- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatDivPrecisionTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatDivPrecisionTest.java Wed May 21 11:45:50 2014 +0200 @@ -49,8 +49,8 @@ } @Override - public boolean isEqualsFP(double first, double second) { - return Math.abs(first - second) == 0; + protected double equalFloatsOrDoublesDelta() { + return 0.0D; } @Test diff -r a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/lambda/VirtualCallTest.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/BoxingEliminationTest.java Wed May 21 11:45:50 2014 +0200 @@ -300,7 +300,7 @@ final ValueNode getResult(String snippet) { processMethod(snippet); - assertEquals(1, graph.getNodes(ReturnNode.class).count()); + assertDeepEquals(1, graph.getNodes(ReturnNode.class).count()); return graph.getNodes(ReturnNode.class).first().result(); } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CompareCanonicalizerTest.java Wed May 21 11:45:50 2014 +0200 @@ -132,7 +132,7 @@ result = getResult(getCanonicalizedGraph("integerTestCanonicalization2")); assertTrue(result.isConstant() && result.asConstant().asLong() == 1); StructuredGraph graph = getCanonicalizedGraph("integerTestCanonicalization3"); - assertEquals(1, graph.getNodes(ReturnNode.class).count()); + assertDeepEquals(1, graph.getNodes(ReturnNode.class).count()); assertTrue(graph.getNodes(ReturnNode.class).first().result() instanceof ConditionalNode); } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ConditionalEliminationTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ConditionalEliminationTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ConditionalEliminationTest.java Wed May 21 11:45:50 2014 +0200 @@ -100,7 +100,7 @@ new ConditionalEliminationPhase(getMetaAccess()).apply(graph, context); canonicalizer.apply(graph, context); - assertEquals(1, graph.getNodes().filter(GuardNode.class).count()); + assertDeepEquals(1, graph.getNodes().filter(GuardNode.class).count()); } public static String testInstanceOfCheckCastSnippet(Object e) { @@ -123,7 +123,7 @@ new ConditionalEliminationPhase(getMetaAccess()).apply(graph, context); canonicalizer.apply(graph, context); - assertEquals(0, graph.getNodes().filter(GuardNode.class).count()); + assertDeepEquals(0, graph.getNodes().filter(GuardNode.class).count()); } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FlowSenReduTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FlowSenReduTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FlowSenReduTest.java Wed May 21 11:45:50 2014 +0200 @@ -61,7 +61,7 @@ @Test public void redundantCheckCastTest() { - assertEquals(i7, redundantCheckCastSnippet(i7)); + assertDeepEquals(i7, redundantCheckCastSnippet(i7)); StructuredGraph result = afterFlowSensitiveReduce("redundantCheckCastSnippet"); nodeCountEquals(result, CheckCastNode.class, 0); nodeCountEquals(result, InstanceOfNode.class, 1); @@ -79,7 +79,7 @@ @Test public void redundantInstanceOfTest01() { String snippet = "redundantInstanceOfSnippet01"; - assertEquals(true, redundantInstanceOfSnippet01(i7)); + assertDeepEquals(true, redundantInstanceOfSnippet01(i7)); nodeCountEquals(afterFlowSensitiveReduce(snippet), InstanceOfNode.class, 1); } @@ -100,9 +100,9 @@ @Test public void redundantInstanceOfTest02() { String snippet = "redundantInstanceOfSnippet02"; - assertEquals(i7, redundantInstanceOfSnippet02(i7)); + assertDeepEquals(i7, redundantInstanceOfSnippet02(i7)); int ioAfter = getNodes(afterFlowSensitiveReduce(snippet), InstanceOfNode.class).size(); - assertEquals(ioAfter, 1); + assertDeepEquals(ioAfter, 1); } /* @@ -121,18 +121,18 @@ @Test public void devirtualizationTest() { String snippet = "devirtualizationSnippet"; - assertEquals(i7, devirtualizationSnippet(i7, i7)); + assertDeepEquals(i7, devirtualizationSnippet(i7, i7)); nodeCountEquals(afterFlowSensitiveReduce(snippet), CheckCastNode.class, 0); StructuredGraph graph = afterFlowSensitiveReduce(snippet); - assertEquals(0, graph.getNodes().filter(CheckCastNode.class).count()); + assertDeepEquals(0, graph.getNodes().filter(CheckCastNode.class).count()); List invokeNodes = getNodes(afterFlowSensitiveReduce(snippet), InvokeNode.class); - assertEquals(1, invokeNodes.size()); + assertDeepEquals(1, invokeNodes.size()); MethodCallTargetNode target = (MethodCallTargetNode) invokeNodes.get(0).callTarget(); - assertEquals(MethodCallTargetNode.InvokeKind.Special, target.invokeKind()); - assertEquals("HotSpotMethod", target.targetMethod().toString()); + assertDeepEquals(MethodCallTargetNode.InvokeKind.Special, target.invokeKind()); + assertDeepEquals("HotSpotMethod", target.targetMethod().toString()); } /* @@ -154,7 +154,7 @@ @Test public void t5a() { String snippet = "t5Snippet"; - assertEquals(false, t5Snippet(null, true)); + assertDeepEquals(false, t5Snippet(null, true)); StructuredGraph resultGraph = canonicalize(afterFlowSensitiveReduce(snippet)); nodeCountEquals(resultGraph, ReturnNode.class, 2); @@ -164,8 +164,8 @@ ConstantNode c1 = (ConstantNode) iter.next().result(); ConstantNode c2 = (ConstantNode) iter.next().result(); - assertEquals(c1, c2); - assertEquals(0, c1.getValue().asInt()); + assertDeepEquals(c1, c2); + assertDeepEquals(0, c1.getValue().asInt()); } @Test @@ -215,16 +215,16 @@ StructuredGraph graph = afterFlowSensitiveReduce(snippet); graph = dce(canonicalize(graph)); // TODO how to simplify IfNode(false) - assertEquals(1, getNodes(graph, InstanceOfNode.class).size()); + assertDeepEquals(1, getNodes(graph, InstanceOfNode.class).size()); List returnNodes = getNodes(graph, ReturnNode.class); - assertEquals(2, returnNodes.size()); + assertDeepEquals(2, returnNodes.size()); Iterator iter = returnNodes.iterator(); ConstantNode c1 = (ConstantNode) iter.next().result(); ConstantNode c2 = (ConstantNode) iter.next().result(); - assertEquals(c1, c2); + assertDeepEquals(c1, c2); Assert.assertTrue(c1.getValue().isNull()); } @@ -253,14 +253,14 @@ String snippet = "devirtualizationSnippet02"; StructuredGraph graph = afterFlowSensitiveReduce(snippet); - assertEquals(1, getNodes(graph, InvokeNode.class).size()); + assertDeepEquals(1, getNodes(graph, InvokeNode.class).size()); List invokeNodes = getNodes(graph, InvokeNode.class); - assertEquals(1, invokeNodes.size()); + assertDeepEquals(1, invokeNodes.size()); MethodCallTargetNode target = (MethodCallTargetNode) invokeNodes.get(0).callTarget(); - assertEquals(MethodCallTargetNode.InvokeKind.Special, target.invokeKind()); - assertEquals("HotSpotMethod", target.targetMethod().toString()); + assertDeepEquals(MethodCallTargetNode.InvokeKind.Special, target.invokeKind()); + assertDeepEquals("HotSpotMethod", target.targetMethod().toString()); } /* @@ -312,7 +312,7 @@ dce(graph); List returnNodes = getNodes(graph, ReturnNode.class); - assertEquals(2, returnNodes.size()); + assertDeepEquals(2, returnNodes.size()); Iterator iter = returnNodes.iterator(); ValueNode c1 = GraphUtil.unproxify(iter.next().result()); @@ -339,7 +339,7 @@ String snippet = "deduplicateInstanceOfSnippet"; StructuredGraph graph = afterFlowSensitiveReduce(snippet); List ioNodes = getNodes(graph, InstanceOfNode.class); - assertEquals(1, ioNodes.size()); + assertDeepEquals(1, ioNodes.size()); } @@ -371,7 +371,7 @@ } public void nodeCountEquals(StructuredGraph graph, Class nodeClass, int expected) { - assertEquals(expected, getNodes(graph, nodeClass).size()); + assertDeepEquals(expected, getNodes(graph, nodeClass).size()); } public StructuredGraph afterFlowSensitiveReduce(String snippet) { diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FlowSensitiveReductionTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FlowSensitiveReductionTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/FlowSensitiveReductionTest.java Wed May 21 11:45:50 2014 +0200 @@ -209,7 +209,7 @@ new FlowSensitiveReductionPhase(getMetaAccess()).apply(graph, context); InvokeNode invoke = graph.getNodes().filter(InvokeNode.class).first(); - assertEquals(InvokeKind.Special, ((MethodCallTargetNode) invoke.callTarget()).invokeKind()); + assertDeepEquals(InvokeKind.Special, ((MethodCallTargetNode) invoke.callTarget()).invokeKind()); } public static void testTypeMergingSnippet(Object o, boolean b) { @@ -240,7 +240,7 @@ new FlowSensitiveReductionPhase(getMetaAccess()).apply(graph, context); new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), null)); - assertEquals(0, graph.getNodes().filter(StoreFieldNode.class).count()); + assertDeepEquals(0, graph.getNodes().filter(StoreFieldNode.class).count()); } public static String testInstanceOfCheckCastSnippet(Object e) { @@ -258,7 +258,7 @@ new FlowSensitiveReductionPhase(getMetaAccess()).apply(graph, context); new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), null)); - assertEquals(0, graph.getNodes().filter(CheckCastNode.class).count()); + assertDeepEquals(0, graph.getNodes().filter(CheckCastNode.class).count()); } public static int testDuplicateNullChecksSnippet(Object a) { @@ -287,7 +287,7 @@ new FlowSensitiveReductionPhase(getMetaAccess()).apply(graph, context); canonicalizer.apply(graph, context); - assertEquals(1, graph.getNodes().filter(GuardNode.class).count()); + assertDeepEquals(1, graph.getNodes().filter(GuardNode.class).count()); } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java Wed May 21 11:45:50 2014 +0200 @@ -528,7 +528,7 @@ actual.exception.printStackTrace(); Assert.fail("expected " + expect.returnValue + " but got an exception"); } - assertEquals(expect.returnValue, actual.returnValue); + assertDeepEquals(expect.returnValue, actual.returnValue); } } diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/IfCanonicalizerTest.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InfopointReasonTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InfopointReasonTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/InfopointReasonTest.java Wed May 21 11:45:50 2014 +0200 @@ -66,7 +66,7 @@ for (Infopoint sp : cr.getInfopoints()) { assertNotNull(sp.reason); if (sp instanceof Call) { - assertEquals(InfopointReason.CALL, sp.reason); + assertDeepEquals(InfopointReason.CALL, sp.reason); } } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LockEliminationTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LockEliminationTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/LockEliminationTest.java Wed May 21 11:45:50 2014 +0200 @@ -65,8 +65,8 @@ StructuredGraph graph = getGraph("testSynchronizedSnippet"); new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), null)); new LockEliminationPhase().apply(graph); - assertEquals(1, graph.getNodes().filter(MonitorEnterNode.class).count()); - assertEquals(1, graph.getNodes().filter(MonitorExitNode.class).count()); + assertDeepEquals(1, graph.getNodes().filter(MonitorEnterNode.class).count()); + assertDeepEquals(1, graph.getNodes().filter(MonitorExitNode.class).count()); } public static void testSynchronizedMethodSnippet(A x) { @@ -83,8 +83,8 @@ StructuredGraph graph = getGraph("testSynchronizedMethodSnippet"); new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), null)); new LockEliminationPhase().apply(graph); - assertEquals(1, graph.getNodes().filter(MonitorEnterNode.class).count()); - assertEquals(1, graph.getNodes().filter(MonitorExitNode.class).count()); + assertDeepEquals(1, graph.getNodes().filter(MonitorEnterNode.class).count()); + assertDeepEquals(1, graph.getNodes().filter(MonitorExitNode.class).count()); } private StructuredGraph getGraph(String snippet) { diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java Wed May 21 11:45:50 2014 +0200 @@ -164,7 +164,7 @@ @Test public void testLoop1() { SchedulePhase schedule = getFinalSchedule("testLoop1Snippet", TestMode.WITHOUT_FRAMESTATES); - assertEquals(6, schedule.getCFG().getBlocks().size()); + assertDeepEquals(6, schedule.getCFG().getBlocks().size()); assertReadWithinStartBlock(schedule, true); assertReadWithinAllReturnBlocks(schedule, false); } @@ -189,7 +189,7 @@ @Test public void testLoop2() { SchedulePhase schedule = getFinalSchedule("testLoop2Snippet", TestMode.WITHOUT_FRAMESTATES); - assertEquals(6, schedule.getCFG().getBlocks().size()); + assertDeepEquals(6, schedule.getCFG().getBlocks().size()); assertReadWithinStartBlock(schedule, false); assertReadWithinAllReturnBlocks(schedule, true); } @@ -211,7 +211,7 @@ @Test public void testLoop3() { SchedulePhase schedule = getFinalSchedule("testLoop3Snippet", TestMode.WITHOUT_FRAMESTATES); - assertEquals(6, schedule.getCFG().getBlocks().size()); + assertDeepEquals(6, schedule.getCFG().getBlocks().size()); assertReadWithinStartBlock(schedule, true); assertReadWithinAllReturnBlocks(schedule, false); } @@ -247,7 +247,7 @@ @Test public void testLoop5() { SchedulePhase schedule = getFinalSchedule("testLoop5Snippet", TestMode.WITHOUT_FRAMESTATES); - assertEquals(10, schedule.getCFG().getBlocks().size()); + assertDeepEquals(10, schedule.getCFG().getBlocks().size()); assertReadWithinStartBlock(schedule, false); assertReadWithinAllReturnBlocks(schedule, false); } @@ -264,10 +264,10 @@ public void testArrayCopy() { SchedulePhase schedule = getFinalSchedule("testArrayCopySnippet", TestMode.INLINED_WITHOUT_FRAMESTATES); StructuredGraph graph = schedule.getCFG().getStartBlock().getBeginNode().graph(); - assertEquals(1, graph.getNodes(ReturnNode.class).count()); + assertDeepEquals(1, graph.getNodes(ReturnNode.class).count()); ReturnNode ret = graph.getNodes(ReturnNode.class).first(); assertTrue(ret.result() + " should be a FloatingReadNode", ret.result() instanceof FloatingReadNode); - assertEquals(schedule.getCFG().blockFor(ret), schedule.getCFG().blockFor(ret.result())); + assertDeepEquals(schedule.getCFG().blockFor(ret), schedule.getCFG().blockFor(ret.result())); assertReadWithinAllReturnBlocks(schedule, true); } @@ -285,7 +285,7 @@ @Test public void testIfRead1() { SchedulePhase schedule = getFinalSchedule("testIfRead1Snippet", TestMode.WITHOUT_FRAMESTATES); - assertEquals(3, schedule.getCFG().getBlocks().size()); + assertDeepEquals(3, schedule.getCFG().getBlocks().size()); assertReadWithinStartBlock(schedule, true); assertReadAndWriteInSameBlock(schedule, false); } @@ -306,8 +306,8 @@ @Test public void testIfRead2() { SchedulePhase schedule = getFinalSchedule("testIfRead2Snippet", TestMode.WITHOUT_FRAMESTATES); - assertEquals(3, schedule.getCFG().getBlocks().size()); - assertEquals(1, schedule.getCFG().graph.getNodes().filter(FloatingReadNode.class).count()); + assertDeepEquals(3, schedule.getCFG().getBlocks().size()); + assertDeepEquals(1, schedule.getCFG().graph.getNodes().filter(FloatingReadNode.class).count()); assertReadWithinStartBlock(schedule, false); assertReadWithinAllReturnBlocks(schedule, false); assertReadAndWriteInSameBlock(schedule, false); @@ -328,7 +328,7 @@ @Test public void testIfRead3() { SchedulePhase schedule = getFinalSchedule("testIfRead3Snippet", TestMode.WITHOUT_FRAMESTATES); - assertEquals(4, schedule.getCFG().getBlocks().size()); + assertDeepEquals(4, schedule.getCFG().getBlocks().size()); assertReadWithinStartBlock(schedule, false); assertReadWithinAllReturnBlocks(schedule, true); } @@ -349,7 +349,7 @@ @Test public void testIfRead4() { SchedulePhase schedule = getFinalSchedule("testIfRead4Snippet", TestMode.WITHOUT_FRAMESTATES); - assertEquals(3, schedule.getCFG().getBlocks().size()); + assertDeepEquals(3, schedule.getCFG().getBlocks().size()); assertReadWithinStartBlock(schedule, false); assertReadWithinAllReturnBlocks(schedule, false); assertReadAndWriteInSameBlock(schedule, true); @@ -368,12 +368,36 @@ @Test public void testIfRead5() { SchedulePhase schedule = getFinalSchedule("testIfRead5Snippet", TestMode.WITHOUT_FRAMESTATES); - assertEquals(4, schedule.getCFG().getBlocks().size()); + assertDeepEquals(4, schedule.getCFG().getBlocks().size()); assertReadWithinStartBlock(schedule, false); assertReadWithinAllReturnBlocks(schedule, true); assertReadAndWriteInSameBlock(schedule, false); } + public static int testAntiDependencySnippet(int a) { + /* + * This read must not be scheduled after the following write. + */ + int res = container.a; + container.a = 10; + + /* + * Add some more basic blocks. + */ + if (a < 0) { + container.b = 20; + } + container.c = 30; + return res; + } + + @Test + public void testAntiDependency() { + SchedulePhase schedule = getFinalSchedule("testAntiDependencySnippet", TestMode.WITHOUT_FRAMESTATES); + assertDeepEquals(4, schedule.getCFG().getBlocks().size()); + assertReadBeforeAllWritesInStartBlock(schedule); + } + /** * testing scheduling within a block. */ @@ -397,9 +421,9 @@ StructuredGraph graph = schedule.getCFG().graph; NodeIterable writeNodes = graph.getNodes().filter(WriteNode.class); - assertEquals(1, schedule.getCFG().getBlocks().size()); - assertEquals(8, writeNodes.count()); - assertEquals(1, graph.getNodes().filter(FloatingReadNode.class).count()); + assertDeepEquals(1, schedule.getCFG().getBlocks().size()); + assertDeepEquals(8, writeNodes.count()); + assertDeepEquals(1, graph.getNodes().filter(FloatingReadNode.class).count()); FloatingReadNode read = graph.getNodes().filter(FloatingReadNode.class).first(); @@ -554,7 +578,7 @@ } returnBlocks++; } - assertEquals(withRead == returnBlocks, withinReturnBlock); + assertDeepEquals(withRead == returnBlocks, withinReturnBlock); } private void assertReadWithinStartBlock(SchedulePhase schedule, boolean withinStartBlock) { @@ -564,7 +588,7 @@ readEncountered = true; } } - assertEquals(withinStartBlock, readEncountered); + assertDeepEquals(withinStartBlock, readEncountered); } private static void assertReadAndWriteInSameBlock(SchedulePhase schedule, boolean inSame) { @@ -574,6 +598,20 @@ assertTrue(!(inSame ^ schedule.getCFG().blockFor(read) == schedule.getCFG().blockFor(write))); } + private static void assertReadBeforeAllWritesInStartBlock(SchedulePhase schedule) { + boolean writeNodeFound = false; + boolean readNodeFound = false; + for (Node node : schedule.nodesFor(schedule.getCFG().getStartBlock())) { + if (node instanceof FloatingReadNode) { + assertTrue(!writeNodeFound); + readNodeFound = true; + } else if (node instanceof WriteNode) { + writeNodeFound = true; + } + } + assertTrue(readNodeFound); + } + private SchedulePhase getFinalSchedule(final String snippet, final TestMode mode) { return getFinalSchedule(snippet, mode, MemoryScheduling.OPTIMAL); } @@ -617,7 +655,7 @@ SchedulePhase schedule = new SchedulePhase(schedulingStrategy, memsched); schedule.apply(graph); - assertEquals(1, graph.getNodes().filter(StartNode.class).count()); + assertDeepEquals(1, graph.getNodes().filter(StartNode.class).count()); return schedule; } } catch (Throwable e) { diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MergeCanonicalizerTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MergeCanonicalizerTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MergeCanonicalizerTest.java Wed May 21 11:45:50 2014 +0200 @@ -61,6 +61,6 @@ new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), new Assumptions(false))); new CanonicalizerPhase(true).apply(graph, new PhaseContext(getProviders(), new Assumptions(false))); Debug.dump(graph, "Graph"); - assertEquals(returnCount, graph.getNodes(ReturnNode.class).count()); + assertDeepEquals(returnCount, graph.getNodes(ReturnNode.class).count()); } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/SimpleCFGTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/SimpleCFGTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/SimpleCFGTest.java Wed May 21 11:45:50 2014 +0200 @@ -65,24 +65,24 @@ List blocks = cfg.getBlocks(); // check number of blocks - assertEquals(4, blocks.size()); + assertDeepEquals(4, blocks.size()); // check block - node assignment - assertEquals(blocks.get(0), cfg.blockFor(graph.start())); - assertEquals(blocks.get(0), cfg.blockFor(ifNode)); - assertEquals(blocks.get(1), cfg.blockFor(trueBegin)); - assertEquals(blocks.get(1), cfg.blockFor(trueEnd)); - assertEquals(blocks.get(2), cfg.blockFor(falseBegin)); - assertEquals(blocks.get(2), cfg.blockFor(falseEnd)); - assertEquals(blocks.get(3), cfg.blockFor(merge)); - assertEquals(blocks.get(3), cfg.blockFor(returnNode)); + assertDeepEquals(blocks.get(0), cfg.blockFor(graph.start())); + assertDeepEquals(blocks.get(0), cfg.blockFor(ifNode)); + assertDeepEquals(blocks.get(1), cfg.blockFor(trueBegin)); + assertDeepEquals(blocks.get(1), cfg.blockFor(trueEnd)); + assertDeepEquals(blocks.get(2), cfg.blockFor(falseBegin)); + assertDeepEquals(blocks.get(2), cfg.blockFor(falseEnd)); + assertDeepEquals(blocks.get(3), cfg.blockFor(merge)); + assertDeepEquals(blocks.get(3), cfg.blockFor(returnNode)); // check postOrder Iterator it = cfg.postOrder().iterator(); for (int i = blocks.size() - 1; i >= 0; i--) { assertTrue(it.hasNext()); Block b = it.next(); - assertEquals(blocks.get(i), b); + assertDeepEquals(blocks.get(i), b); } // check dominators diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EAMergingTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EAMergingTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EAMergingTest.java Wed May 21 11:45:50 2014 +0200 @@ -33,7 +33,7 @@ @Test public void testSimpleMerge() { testEscapeAnalysis("simpleMergeSnippet", null, false); - assertEquals(1, returnNodes.size()); + assertDeepEquals(1, returnNodes.size()); assertTrue(returnNodes.get(0).result() instanceof ValuePhiNode); PhiNode phi = (PhiNode) returnNodes.get(0).result(); assertTrue(phi.valueAt(0) instanceof ParameterNode); diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/IterativeInliningTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/IterativeInliningTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/IterativeInliningTest.java Wed May 21 11:45:50 2014 +0200 @@ -74,12 +74,12 @@ public void testSimple() { ValueNode result = getReturn("testSimpleSnippet").result(); assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty()); - assertEquals(graph.getParameter(0), result); + assertDeepEquals(graph.getParameter(0), result); } final ReturnNode getReturn(String snippet) { processMethod(snippet); - assertEquals(1, graph.getNodes(ReturnNode.class).count()); + assertDeepEquals(1, graph.getNodes(ReturnNode.class).count()); return graph.getNodes(ReturnNode.class).first(); } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PEAReadEliminationTest.java Wed May 21 11:45:50 2014 +0200 @@ -87,7 +87,7 @@ ValueNode result = getReturn("testSimpleSnippet").result(); assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty()); assertTrue(result.isConstant()); - assertEquals(2, result.asConstant().asInt()); + assertDeepEquals(2, result.asConstant().asInt()); } @SuppressWarnings("all") @@ -115,7 +115,7 @@ public void testParam() { ValueNode result = getReturn("testParamSnippet").result(); assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty()); - assertEquals(graph.getParameter(1), result); + assertDeepEquals(graph.getParameter(1), result); } @SuppressWarnings("all") @@ -129,7 +129,7 @@ public void testMaterialized() { ValueNode result = getReturn("testMaterializedSnippet").result(); assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty()); - assertEquals(graph.getParameter(0), result); + assertDeepEquals(graph.getParameter(0), result); } @SuppressWarnings("all") @@ -145,7 +145,7 @@ public void testSimpleLoop() { ValueNode result = getReturn("testSimpleLoopSnippet").result(); assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty()); - assertEquals(graph.getParameter(1), result); + assertDeepEquals(graph.getParameter(1), result); } @SuppressWarnings("all") @@ -162,7 +162,7 @@ @Test public void testBadLoop() { ValueNode result = getReturn("testBadLoopSnippet").result(); - assertEquals(0, graph.getNodes().filter(LoadFieldNode.class).count()); + assertDeepEquals(0, graph.getNodes().filter(LoadFieldNode.class).count()); assertTrue(result instanceof ProxyNode); assertTrue(((ProxyNode) result).value() instanceof ValuePhiNode); } @@ -180,7 +180,7 @@ @Test public void testBadLoop2() { ValueNode result = getReturn("testBadLoop2Snippet").result(); - assertEquals(1, graph.getNodes().filter(LoadFieldNode.class).count()); + assertDeepEquals(1, graph.getNodes().filter(LoadFieldNode.class).count()); assertTrue(result instanceof LoadFieldNode); } @@ -199,7 +199,7 @@ processMethod("testPhiSnippet"); assertTrue(graph.getNodes().filter(LoadFieldNode.class).isEmpty()); List returnNodes = graph.getNodes(ReturnNode.class).snapshot(); - assertEquals(2, returnNodes.size()); + assertDeepEquals(2, returnNodes.size()); assertTrue(returnNodes.get(0).predecessor() instanceof StoreFieldNode); assertTrue(returnNodes.get(1).predecessor() instanceof StoreFieldNode); assertTrue(returnNodes.get(0).result().isConstant()); @@ -215,7 +215,7 @@ @Test public void testSimpleStore() { processMethod("testSimpleStoreSnippet"); - assertEquals(1, graph.getNodes().filter(StoreFieldNode.class).count()); + assertDeepEquals(1, graph.getNodes().filter(StoreFieldNode.class).count()); } public static int testValueProxySnippet(boolean b, TestObject o) { @@ -233,12 +233,12 @@ @Test public void testValueProxy() { processMethod("testValueProxySnippet"); - assertEquals(2, graph.getNodes().filter(LoadFieldNode.class).count()); + assertDeepEquals(2, graph.getNodes().filter(LoadFieldNode.class).count()); } final ReturnNode getReturn(String snippet) { processMethod(snippet); - assertEquals(1, graph.getNodes(ReturnNode.class).count()); + assertDeepEquals(1, graph.getNodes(ReturnNode.class).count()); return graph.getNodes(ReturnNode.class).first(); } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java --- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java Wed May 21 11:45:50 2014 +0200 @@ -171,7 +171,7 @@ @Test public void testReference1() { prepareGraph("testReference1Snippet", false); - assertEquals(1, graph.getNodes().filter(NewInstanceNode.class).count()); + assertDeepEquals(1, graph.getNodes().filter(NewInstanceNode.class).count()); } @SafeVarargs diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchGenerator.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchPattern.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchProcessor.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchRuleRegistry.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatement.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/match/MatchStatementSet.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java Wed May 21 11:45:50 2014 +0200 @@ -197,4 +197,9 @@ public boolean contains(Node node) { return isMarked(node); } + + @Override + public String toString() { + return snapshot().toString(); + } } diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/AheadOfTimeCompilationTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/AheadOfTimeCompilationTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/AheadOfTimeCompilationTest.java Wed May 21 11:45:50 2014 +0200 @@ -66,19 +66,19 @@ @Test public void testStaticFinalObjectAOT() { StructuredGraph result = compile("getStaticFinalObject", true); - assertEquals(1, getConstantNodes(result).count()); - assertEquals(getCodeCache().getTarget().wordKind, getConstantNodes(result).first().getKind()); - assertEquals(2, result.getNodes(FloatingReadNode.class).count()); - assertEquals(0, result.getNodes().filter(ReadNode.class).count()); + assertDeepEquals(1, getConstantNodes(result).count()); + assertDeepEquals(getCodeCache().getTarget().wordKind, getConstantNodes(result).first().getKind()); + assertDeepEquals(2, result.getNodes(FloatingReadNode.class).count()); + assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count()); } @Test public void testStaticFinalObject() { StructuredGraph result = compile("getStaticFinalObject", false); - assertEquals(1, getConstantNodes(result).count()); - assertEquals(Kind.Object, getConstantNodes(result).first().getKind()); - assertEquals(0, result.getNodes(FloatingReadNode.class).count()); - assertEquals(0, result.getNodes().filter(ReadNode.class).count()); + assertDeepEquals(1, getConstantNodes(result).count()); + assertDeepEquals(Kind.Object, getConstantNodes(result).first().getKind()); + assertDeepEquals(0, result.getNodes(FloatingReadNode.class).count()); + assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count()); } public static Class getClassObject() { @@ -90,12 +90,12 @@ StructuredGraph result = compile("getClassObject", true); NodeIterable filter = getConstantNodes(result); - assertEquals(1, filter.count()); + assertDeepEquals(1, filter.count()); HotSpotResolvedObjectType type = (HotSpotResolvedObjectType) getMetaAccess().lookupJavaType(AheadOfTimeCompilationTest.class); - assertEquals(type.klass(), filter.first().asConstant()); + assertDeepEquals(type.klass(), filter.first().asConstant()); - assertEquals(1, result.getNodes(FloatingReadNode.class).count()); - assertEquals(0, result.getNodes().filter(ReadNode.class).count()); + assertDeepEquals(1, result.getNodes(FloatingReadNode.class).count()); + assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count()); } @Test @@ -103,13 +103,13 @@ StructuredGraph result = compile("getClassObject", false); NodeIterable filter = getConstantNodes(result); - assertEquals(1, filter.count()); + assertDeepEquals(1, filter.count()); Object mirror = HotSpotObjectConstant.asObject(filter.first().asConstant()); - assertEquals(Class.class, mirror.getClass()); - assertEquals(AheadOfTimeCompilationTest.class, mirror); + assertDeepEquals(Class.class, mirror.getClass()); + assertDeepEquals(AheadOfTimeCompilationTest.class, mirror); - assertEquals(0, result.getNodes(FloatingReadNode.class).count()); - assertEquals(0, result.getNodes().filter(ReadNode.class).count()); + assertDeepEquals(0, result.getNodes(FloatingReadNode.class).count()); + assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count()); } public static Class getPrimitiveClassObject() { @@ -120,24 +120,24 @@ public void testPrimitiveClassObjectAOT() { StructuredGraph result = compile("getPrimitiveClassObject", true); NodeIterable filter = getConstantNodes(result); - assertEquals(1, filter.count()); - assertEquals(getCodeCache().getTarget().wordKind, filter.first().getKind()); + assertDeepEquals(1, filter.count()); + assertDeepEquals(getCodeCache().getTarget().wordKind, filter.first().getKind()); - assertEquals(2, result.getNodes(FloatingReadNode.class).count()); - assertEquals(0, result.getNodes().filter(ReadNode.class).count()); + assertDeepEquals(2, result.getNodes(FloatingReadNode.class).count()); + assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count()); } @Test public void testPrimitiveClassObject() { StructuredGraph result = compile("getPrimitiveClassObject", false); NodeIterable filter = getConstantNodes(result); - assertEquals(1, filter.count()); + assertDeepEquals(1, filter.count()); Object mirror = HotSpotObjectConstant.asObject(filter.first().asConstant()); - assertEquals(Class.class, mirror.getClass()); - assertEquals(Integer.TYPE, mirror); + assertDeepEquals(Class.class, mirror.getClass()); + assertDeepEquals(Integer.TYPE, mirror); - assertEquals(0, result.getNodes(FloatingReadNode.class).count()); - assertEquals(0, result.getNodes().filter(ReadNode.class).count()); + assertDeepEquals(0, result.getNodes(FloatingReadNode.class).count()); + assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count()); } public static String getStringObject() { @@ -159,13 +159,13 @@ StructuredGraph result = compile("getStringObject", compileAOT); NodeIterable filter = getConstantNodes(result); - assertEquals(1, filter.count()); + assertDeepEquals(1, filter.count()); Object mirror = HotSpotObjectConstant.asObject(filter.first().asConstant()); - assertEquals(String.class, mirror.getClass()); - assertEquals("test string", mirror); + assertDeepEquals(String.class, mirror.getClass()); + assertDeepEquals("test string", mirror); - assertEquals(0, result.getNodes(FloatingReadNode.class).count()); - assertEquals(0, result.getNodes().filter(ReadNode.class).count()); + assertDeepEquals(0, result.getNodes(FloatingReadNode.class).count()); + assertDeepEquals(0, result.getNodes().filter(ReadNode.class).count()); } public static Boolean getBoxedBoolean() { @@ -177,23 +177,23 @@ public void testBoxedBooleanAOT() { StructuredGraph result = compile("getBoxedBoolean", true); - assertEquals(2, result.getNodes(FloatingReadNode.class).count()); - assertEquals(1, result.getNodes(PiNode.class).count()); - assertEquals(1, getConstantNodes(result).count()); + assertDeepEquals(2, result.getNodes(FloatingReadNode.class).count()); + assertDeepEquals(1, result.getNodes(PiNode.class).count()); + assertDeepEquals(1, getConstantNodes(result).count()); ConstantNode constant = getConstantNodes(result).first(); - assertEquals(Kind.Long, constant.getKind()); - assertEquals(((HotSpotResolvedObjectType) getMetaAccess().lookupJavaType(Boolean.class)).klass(), constant.asConstant()); + assertDeepEquals(Kind.Long, constant.getKind()); + assertDeepEquals(((HotSpotResolvedObjectType) getMetaAccess().lookupJavaType(Boolean.class)).klass(), constant.asConstant()); } @Test public void testBoxedBoolean() { StructuredGraph result = compile("getBoxedBoolean", false); - assertEquals(0, result.getNodes(FloatingReadNode.class).count()); - assertEquals(0, result.getNodes(PiNode.class).count()); - assertEquals(1, getConstantNodes(result).count()); + assertDeepEquals(0, result.getNodes(FloatingReadNode.class).count()); + assertDeepEquals(0, result.getNodes(PiNode.class).count()); + assertDeepEquals(1, getConstantNodes(result).count()); ConstantNode constant = getConstantNodes(result).first(); - assertEquals(Kind.Object, constant.getKind()); - assertEquals(Boolean.TRUE, HotSpotObjectConstant.asObject(constant.asConstant())); + assertDeepEquals(Kind.Object, constant.getKind()); + assertDeepEquals(Boolean.TRUE, HotSpotObjectConstant.asObject(constant.asConstant())); } private StructuredGraph compile(String test, boolean compileAOT) { diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ExplicitExceptionTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ExplicitExceptionTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ExplicitExceptionTest.java Wed May 21 11:45:50 2014 +0200 @@ -37,7 +37,7 @@ @Override protected InstalledCode getCode(ResolvedJavaMethod method, StructuredGraph graph) { InstalledCode installedCode = super.getCode(method, graph); - assertEquals(expectedForeignCallCount, graph.getNodes().filter(ForeignCallNode.class).count()); + assertDeepEquals(expectedForeignCallCount, graph.getNodes().filter(ForeignCallNode.class).count()); return installedCode; } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotMethodSubstitutionTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotMethodSubstitutionTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotMethodSubstitutionTest.java Wed May 21 11:45:50 2014 +0200 @@ -45,8 +45,8 @@ Object obj = new Object(); - assertEquals("a string".getClass(), ObjectSubstitutions.getClass("a string")); - assertEquals(obj.hashCode(), ObjectSubstitutions.hashCode(obj)); + assertDeepEquals("a string".getClass(), ObjectSubstitutions.getClass("a string")); + assertDeepEquals(obj.hashCode(), ObjectSubstitutions.hashCode(obj)); } @SuppressWarnings("all") @@ -75,14 +75,14 @@ test("getComponentType"); for (Class c : new Class[]{getClass(), Cloneable.class, int[].class, String[][].class}) { - assertEquals(c.getModifiers(), ClassSubstitutions.getModifiers(c)); - assertEquals(c.isInterface(), ClassSubstitutions.isInterface(c)); - assertEquals(c.isArray(), ClassSubstitutions.isArray(c)); - assertEquals(c.isPrimitive(), ClassSubstitutions.isPrimitive(c)); - assertEquals(c.getSuperclass(), ClassSubstitutions.getSuperclass(c)); - assertEquals(c.getComponentType(), ClassSubstitutions.getComponentType(c)); + assertDeepEquals(c.getModifiers(), ClassSubstitutions.getModifiers(c)); + assertDeepEquals(c.isInterface(), ClassSubstitutions.isInterface(c)); + assertDeepEquals(c.isArray(), ClassSubstitutions.isArray(c)); + assertDeepEquals(c.isPrimitive(), ClassSubstitutions.isPrimitive(c)); + assertDeepEquals(c.getSuperclass(), ClassSubstitutions.getSuperclass(c)); + assertDeepEquals(c.getComponentType(), ClassSubstitutions.getComponentType(c)); for (Object o : new Object[]{this, new int[5], new String[2][], new Object()}) { - assertEquals(c.isInstance(o), ClassSubstitutions.isInstance(c, o)); + assertDeepEquals(c.isInstance(o), ClassSubstitutions.isInstance(c, o)); } } } @@ -134,8 +134,8 @@ test("threadInterrupted"); Thread currentThread = Thread.currentThread(); - assertEquals(currentThread, ThreadSubstitutions.currentThread()); - assertEquals(currentThread.isInterrupted(), ThreadSubstitutions.isInterrupted(currentThread, false)); + assertDeepEquals(currentThread, ThreadSubstitutions.currentThread()); + assertDeepEquals(currentThread.isInterrupted(), ThreadSubstitutions.isInterrupted(currentThread, false)); } @SuppressWarnings("all") @@ -161,7 +161,7 @@ SystemSubstitutions.currentTimeMillis(); SystemSubstitutions.nanoTime(); for (Object o : new Object[]{this, new int[5], new String[2][], new Object()}) { - assertEquals(System.identityHashCode(o), SystemSubstitutions.identityHashCode(o)); + assertDeepEquals(System.identityHashCode(o), SystemSubstitutions.identityHashCode(o)); } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotMonitorValueTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotMonitorValueTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotMonitorValueTest.java Wed May 21 11:45:50 2014 +0200 @@ -51,8 +51,8 @@ BytecodeFrame caller = frame.caller(); assertNotNull(caller); assertNull(caller.caller()); - assertEquals(2, frame.numLocks); - assertEquals(2, caller.numLocks); + assertDeepEquals(2, frame.numLocks); + assertDeepEquals(2, caller.numLocks); HotSpotMonitorValue lock1 = (HotSpotMonitorValue) frame.getLockValue(0); HotSpotMonitorValue lock2 = (HotSpotMonitorValue) frame.getLockValue(1); HotSpotMonitorValue lock3 = (HotSpotMonitorValue) caller.getLockValue(0); @@ -67,7 +67,7 @@ } } } - assertEquals(lock3.getOwner(), lock4.getOwner()); + assertDeepEquals(lock3.getOwner(), lock4.getOwner()); assertThat(lock1.getOwner(), not(lock2.getOwner())); return super.addMethod(method, compResult); } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotNmethodTest.java --- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotNmethodTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotNmethodTest.java Wed May 21 11:45:50 2014 +0200 @@ -43,7 +43,7 @@ Object result; try { result = nmethod.executeVarargs(null, "b", "c"); - assertEquals(43, result); + assertDeepEquals(43, result); } catch (InvalidInstalledCodeException e) { Assert.fail("Code was invalidated"); } @@ -66,7 +66,7 @@ Object result; try { result = nmethod.executeVarargs(nmethod, null, null); - assertEquals(43, result); + assertDeepEquals(43, result); } catch (InvalidInstalledCodeException e) { Assert.fail("Code was invalidated"); } diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/WriteBarrierAdditionTest.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java Wed May 21 11:45:50 2014 +0200 @@ -30,6 +30,7 @@ import com.oracle.graal.compiler.common.*; import com.oracle.graal.hotspot.bridge.*; import com.oracle.graal.hotspot.meta.*; +import com.oracle.graal.hotspotvmconfig.*; /** * Used to access native configuration details. @@ -65,14 +66,21 @@ public final int maxFrameSize = 16 * 1024; HotSpotVMConfig(CompilerToVM compilerToVm) { + compilerToVm.initializeConfiguration(this); + assert verifyInitialization(); + + oopEncoding = new CompressEncoding(narrowOopBase, narrowOopShift, logMinObjAlignment()); + klassEncoding = new CompressEncoding(narrowKlassBase, narrowKlassShift, logKlassAlignment); + + assert check(); + } + + /** + * Check that the initialization produces the same result as the values captured through + * vmStructs. + */ + private boolean verifyInitialization() { /** These fields are set in {@link CompilerToVM#initializeConfiguration}. */ - gHotSpotVMStructs = 0; - gHotSpotVMTypes = 0; - gHotSpotVMIntConstants = 0; - gHotSpotVMLongConstants = 0; - - compilerToVm.initializeConfiguration(this); - assert gHotSpotVMStructs != 0; assert gHotSpotVMTypes != 0; assert gHotSpotVMIntConstants != 0; @@ -114,7 +122,7 @@ String type = annotation.type(); VMFields.Field entry = vmFields.get(name); if (entry == null) { - if (annotation.optional() || !isRequired(currentArch, annotation.archs())) { + if (!isRequired(currentArch, annotation.archs())) { continue; } throw new IllegalArgumentException("field not found: " + name); @@ -129,13 +137,13 @@ switch (annotation.get()) { case OFFSET: - setField(f, entry.getOffset()); + checkField(f, entry.getOffset()); break; case ADDRESS: - setField(f, entry.getAddress()); + checkField(f, entry.getAddress()); break; case VALUE: - setField(f, entry.getValue()); + checkField(f, entry.getValue()); break; default: throw GraalInternalError.shouldNotReachHere("unknown kind " + annotation.get()); @@ -149,7 +157,7 @@ } switch (annotation.get()) { case SIZE: - setField(f, entry.getSize()); + checkField(f, entry.getSize()); break; default: throw GraalInternalError.shouldNotReachHere("unknown kind " + annotation.get()); @@ -164,7 +172,7 @@ } throw new IllegalArgumentException("constant not found: " + name); } - setField(f, entry.getValue()); + checkField(f, entry.getValue()); } else if (f.isAnnotationPresent(HotSpotVMFlag.class)) { HotSpotVMFlag annotation = f.getAnnotation(HotSpotVMFlag.class); String name = annotation.name(); @@ -176,14 +184,10 @@ throw new IllegalArgumentException("flag not found: " + name); } - setField(f, entry.getValue()); + checkField(f, entry.getValue()); } } - - oopEncoding = new CompressEncoding(narrowOopBase, narrowOopShift, logMinObjAlignment()); - klassEncoding = new CompressEncoding(narrowKlassBase, narrowKlassShift, logKlassAlignment); - - assert check(); + return true; } private final CompressEncoding oopEncoding; @@ -197,29 +201,29 @@ return klassEncoding; } - private void setField(Field field, Object value) { + private void checkField(Field field, Object value) { try { Class fieldType = field.getType(); if (fieldType == boolean.class) { if (value instanceof String) { - field.setBoolean(this, Boolean.valueOf((String) value)); + assert field.getBoolean(this) == Boolean.valueOf((String) value) : field + " " + value + " " + field.getBoolean(this); } else if (value instanceof Boolean) { - field.setBoolean(this, (boolean) value); + assert field.getBoolean(this) == (boolean) value : field + " " + value + " " + field.getBoolean(this); } else if (value instanceof Long) { - field.setBoolean(this, ((long) value) != 0); + assert field.getBoolean(this) == (((long) value) != 0) : field + " " + value + " " + field.getBoolean(this); } else { GraalInternalError.shouldNotReachHere(value.getClass().getSimpleName()); } } else if (fieldType == int.class) { if (value instanceof Integer) { - field.setInt(this, (int) value); + assert field.getInt(this) == (int) value : field + " " + value + " " + field.getInt(this); } else if (value instanceof Long) { - field.setInt(this, (int) (long) value); + assert field.getInt(this) == (int) (long) value : field + " " + value + " " + field.getInt(this); } else { GraalInternalError.shouldNotReachHere(value.getClass().getSimpleName()); } } else if (fieldType == long.class) { - field.setLong(this, (long) value); + assert field.getLong(this) == (long) value : field + " " + value + " " + field.getLong(this); } else { GraalInternalError.shouldNotReachHere(field.toString()); } @@ -248,14 +252,14 @@ /** * VMStructEntry (see vmStructs.hpp). */ - private long gHotSpotVMStructs; - private long gHotSpotVMStructEntryTypeNameOffset; - private long gHotSpotVMStructEntryFieldNameOffset; - private long gHotSpotVMStructEntryTypeStringOffset; - private long gHotSpotVMStructEntryIsStaticOffset; - private long gHotSpotVMStructEntryOffsetOffset; - private long gHotSpotVMStructEntryAddressOffset; - private long gHotSpotVMStructEntryArrayStride; + @HotSpotVMValue(expression = "gHotSpotVMStructs", get = HotSpotVMValue.Type.ADDRESS) @Stable private long gHotSpotVMStructs; + @HotSpotVMValue(expression = "gHotSpotVMStructEntryTypeNameOffset") @Stable private long gHotSpotVMStructEntryTypeNameOffset; + @HotSpotVMValue(expression = "gHotSpotVMStructEntryFieldNameOffset") @Stable private long gHotSpotVMStructEntryFieldNameOffset; + @HotSpotVMValue(expression = "gHotSpotVMStructEntryTypeStringOffset") @Stable private long gHotSpotVMStructEntryTypeStringOffset; + @HotSpotVMValue(expression = "gHotSpotVMStructEntryIsStaticOffset") @Stable private long gHotSpotVMStructEntryIsStaticOffset; + @HotSpotVMValue(expression = "gHotSpotVMStructEntryOffsetOffset") @Stable private long gHotSpotVMStructEntryOffsetOffset; + @HotSpotVMValue(expression = "gHotSpotVMStructEntryAddressOffset") @Stable private long gHotSpotVMStructEntryAddressOffset; + @HotSpotVMValue(expression = "gHotSpotVMStructEntryArrayStride") @Stable private long gHotSpotVMStructEntryArrayStride; class VMFields implements Iterable { @@ -364,14 +368,14 @@ /** * VMTypeEntry (see vmStructs.hpp). */ - private long gHotSpotVMTypes; - private long gHotSpotVMTypeEntryTypeNameOffset; - private long gHotSpotVMTypeEntrySuperclassNameOffset; - private long gHotSpotVMTypeEntryIsOopTypeOffset; - private long gHotSpotVMTypeEntryIsIntegerTypeOffset; - private long gHotSpotVMTypeEntryIsUnsignedOffset; - private long gHotSpotVMTypeEntrySizeOffset; - private long gHotSpotVMTypeEntryArrayStride; + @HotSpotVMValue(expression = "gHotSpotVMTypes", get = HotSpotVMValue.Type.ADDRESS) @Stable private long gHotSpotVMTypes; + @HotSpotVMValue(expression = "gHotSpotVMTypeEntryTypeNameOffset") @Stable private long gHotSpotVMTypeEntryTypeNameOffset; + @HotSpotVMValue(expression = "gHotSpotVMTypeEntrySuperclassNameOffset") @Stable private long gHotSpotVMTypeEntrySuperclassNameOffset; + @HotSpotVMValue(expression = "gHotSpotVMTypeEntryIsOopTypeOffset") @Stable private long gHotSpotVMTypeEntryIsOopTypeOffset; + @HotSpotVMValue(expression = "gHotSpotVMTypeEntryIsIntegerTypeOffset") @Stable private long gHotSpotVMTypeEntryIsIntegerTypeOffset; + @HotSpotVMValue(expression = "gHotSpotVMTypeEntryIsUnsignedOffset") @Stable private long gHotSpotVMTypeEntryIsUnsignedOffset; + @HotSpotVMValue(expression = "gHotSpotVMTypeEntrySizeOffset") @Stable private long gHotSpotVMTypeEntrySizeOffset; + @HotSpotVMValue(expression = "gHotSpotVMTypeEntryArrayStride") @Stable private long gHotSpotVMTypeEntryArrayStride; class VMTypes implements Iterable { @@ -476,10 +480,10 @@ /** * VMIntConstantEntry (see vmStructs.hpp). */ - private long gHotSpotVMIntConstants; - private long gHotSpotVMIntConstantEntryNameOffset; - private long gHotSpotVMIntConstantEntryValueOffset; - private long gHotSpotVMIntConstantEntryArrayStride; + @HotSpotVMValue(expression = "gHotSpotVMIntConstants", get = HotSpotVMValue.Type.ADDRESS) @Stable private long gHotSpotVMIntConstants; + @HotSpotVMValue(expression = "gHotSpotVMIntConstantEntryNameOffset") @Stable private long gHotSpotVMIntConstantEntryNameOffset; + @HotSpotVMValue(expression = "gHotSpotVMIntConstantEntryValueOffset") @Stable private long gHotSpotVMIntConstantEntryValueOffset; + @HotSpotVMValue(expression = "gHotSpotVMIntConstantEntryArrayStride") @Stable private long gHotSpotVMIntConstantEntryArrayStride; class VMIntConstants implements Iterable { @@ -540,10 +544,10 @@ /** * VMLongConstantEntry (see vmStructs.hpp). */ - private long gHotSpotVMLongConstants; - private long gHotSpotVMLongConstantEntryNameOffset; - private long gHotSpotVMLongConstantEntryValueOffset; - private long gHotSpotVMLongConstantEntryArrayStride; + @HotSpotVMValue(expression = "gHotSpotVMLongConstants", get = HotSpotVMValue.Type.ADDRESS) @Stable private long gHotSpotVMLongConstants; + @HotSpotVMValue(expression = "gHotSpotVMLongConstantEntryNameOffset") @Stable private long gHotSpotVMLongConstantEntryNameOffset; + @HotSpotVMValue(expression = "gHotSpotVMLongConstantEntryValueOffset") @Stable private long gHotSpotVMLongConstantEntryValueOffset; + @HotSpotVMValue(expression = "gHotSpotVMLongConstantEntryArrayStride") @Stable private long gHotSpotVMLongConstantEntryArrayStride; class VMLongConstants implements Iterable { @@ -699,7 +703,7 @@ } // os information, register layout, code generation, ... - @HotSpotVMConstant(name = "ASSERT") @Stable public boolean cAssertions; + @HotSpotVMValue(expression = "DEBUG_ONLY(1) NOT_DEBUG(0)") @Stable public boolean cAssertions; public final boolean windowsOs = System.getProperty("os.name", "").startsWith("Windows"); @HotSpotVMFlag(name = "CodeEntryAlignment") @Stable public int codeEntryAlignment; @@ -708,8 +712,8 @@ @HotSpotVMFlag(name = "CITimeEach") @Stable public boolean ciTimeEach; @HotSpotVMFlag(name = "CompileThreshold") @Stable public long compileThreshold; @HotSpotVMFlag(name = "CompileTheWorld") @Stable public boolean compileTheWorld; - @HotSpotVMFlag(name = "CompileTheWorldStartAt") @Stable public int compileTheWorldStartAt; - @HotSpotVMFlag(name = "CompileTheWorldStopAt") @Stable public int compileTheWorldStopAt; + @HotSpotVMFlag(name = "CompileTheWorldStartAt", optional = true) @Stable public int compileTheWorldStartAt; + @HotSpotVMFlag(name = "CompileTheWorldStopAt", optional = true) @Stable public int compileTheWorldStopAt; @HotSpotVMFlag(name = "DontCompileHugeMethods") @Stable public boolean dontCompileHugeMethods; @HotSpotVMFlag(name = "HugeMethodLimit") @Stable public int hugeMethodLimit; @HotSpotVMFlag(name = "PrintCompilation") @Stable public boolean printCompilation; @@ -737,6 +741,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; @@ -846,12 +852,12 @@ @HotSpotVMType(name = "vtableEntry", get = HotSpotVMType.Type.SIZE) @Stable public int vtableEntrySize; @HotSpotVMField(name = "vtableEntry::_method", type = "Method*", get = HotSpotVMField.Type.OFFSET) @Stable public int vtableEntryMethodOffset; - @Stable public int instanceKlassVtableStartOffset; + @HotSpotVMValue(expression = "InstanceKlass::vtable_start_offset() * HeapWordSize") @Stable public int instanceKlassVtableStartOffset; /** * The offset of the array length word in an array object's header. */ - @Stable public int arrayLengthOffset; + @HotSpotVMValue(expression = "arrayOopDesc::length_offset_in_bytes()") @Stable public int arrayLengthOffset; @HotSpotVMField(name = "Array::_length", type = "int", get = HotSpotVMField.Type.OFFSET) @Stable public int arrayU1LengthOffset; @HotSpotVMField(name = "Array::_data", type = "", get = HotSpotVMField.Type.OFFSET) @Stable public int arrayU1DataOffset; @@ -893,7 +899,7 @@ @HotSpotVMField(name = "JavaThread::_is_method_handle_return", type = "int", get = HotSpotVMField.Type.OFFSET) @Stable public int threadIsMethodHandleReturnOffset; @HotSpotVMField(name = "JavaThread::_satb_mark_queue", type = "ObjPtrQueue", get = HotSpotVMField.Type.OFFSET) @Stable public int javaThreadSatbMarkQueueOffset; @HotSpotVMField(name = "JavaThread::_vm_result", type = "oop", get = HotSpotVMField.Type.OFFSET) @Stable public int threadObjectResultOffset; - @HotSpotVMField(name = "JavaThread::_graal_counters[0]", type = "jlong", get = HotSpotVMField.Type.OFFSET, optional = true) @Stable public int graalCountersThreadOffset; + @HotSpotVMValue(expression = "in_bytes(JavaThread::graal_counters_offset())") @Stable public int graalCountersThreadOffset; /** * An invalid value for {@link #rtldDefault}. @@ -907,7 +913,7 @@ * void* (const char *filename, char *ebuf, int ebuflen) * */ - @Stable public long dllLoad; + @HotSpotVMValue(expression = "os::dll_load", get = HotSpotVMValue.Type.ADDRESS) @Stable public long dllLoad; /** * Address of the library lookup routine. The C signature of this routine is: @@ -916,7 +922,7 @@ * void* (void* handle, const char* name) * */ - @Stable public long dllLookup; + @HotSpotVMValue(expression = "os::dll_lookup", get = HotSpotVMValue.Type.ADDRESS) @Stable public long dllLookup; /** * A pseudo-handle which when used as the first argument to {@link #dllLookup} means lookup will @@ -924,7 +930,7 @@ * this field is {@value #INVALID_RTLD_DEFAULT_HANDLE}, then this capability is not supported on * the current platform. */ - @Stable public long rtldDefault = INVALID_RTLD_DEFAULT_HANDLE; + @HotSpotVMValue(expression = "RTLD_DEFAULT", defines = {"TARGET_OS_FAMILY_bsd", "TARGET_OS_FAMILY_linux"}, get = HotSpotVMValue.Type.ADDRESS) @Stable public long rtldDefault = INVALID_RTLD_DEFAULT_HANDLE; /** * This field is used to pass exception objects into and out of the runtime system during @@ -1033,9 +1039,9 @@ @HotSpotVMField(name = "HSAILFrame::_pc_offset", type = "jint", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFramePcOffset; @HotSpotVMField(name = "HSAILFrame::_num_s_regs", type = "jbyte", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFrameNumSRegOffset; @HotSpotVMField(name = "HSAILFrame::_num_d_regs", type = "jbyte", get = HotSpotVMField.Type.OFFSET) @Stable public int hsailFrameNumDRegOffset; - @HotSpotVMConstant(name = "sizeof(HSAILFrame)") @Stable public int hsailFrameHeaderSize; - @HotSpotVMConstant(name = "sizeof(Hsail::HSAILKernelDeoptimization)") @Stable public int hsailKernelDeoptimizationHeaderSize; - @HotSpotVMConstant(name = "sizeof(Hsail::HSAILDeoptimizationInfo)") @Stable public int hsailDeoptimizationInfoHeaderSize; + @HotSpotVMType(name = "HSAILFrame", get = HotSpotVMType.Type.SIZE) @Stable public int hsailFrameHeaderSize; + @HotSpotVMType(name = "Hsail::HSAILKernelDeoptimization", get = HotSpotVMType.Type.SIZE) @Stable public int hsailKernelDeoptimizationHeaderSize; + @HotSpotVMType(name = "Hsail::HSAILDeoptimizationInfo", get = HotSpotVMType.Type.SIZE) @Stable public int hsailDeoptimizationInfoHeaderSize; /** * Mark word right shift to get identity hash code. @@ -1064,6 +1070,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; @@ -1074,7 +1081,7 @@ /** * Value of Method::extra_stack_entries(). */ - @Stable public int extraStackEntries; + @HotSpotVMValue(expression = "Method::extra_stack_entries()") @Stable public int extraStackEntries; @HotSpotVMField(name = "ConstMethod::_constants", type = "ConstantPool*", get = HotSpotVMField.Type.OFFSET) @Stable public int constMethodConstantsOffset; @HotSpotVMField(name = "ConstMethod::_flags", type = "u2", get = HotSpotVMField.Type.OFFSET) @Stable public int constMethodFlagsOffset; @@ -1155,8 +1162,8 @@ @HotSpotVMField(name = "Universe::_non_oop_bits", type = "intptr_t", get = HotSpotVMField.Type.VALUE) @Stable public long nonOopBits; @HotSpotVMField(name = "StubRoutines::_verify_oop_count", type = "jint", get = HotSpotVMField.Type.ADDRESS) @Stable public long verifyOopCounterAddress; - @Stable public long verifyOopMask; - @Stable public long verifyOopBits; + @HotSpotVMValue(expression = "Universe::verify_oop_mask()") @Stable public long verifyOopMask; + @HotSpotVMValue(expression = "Universe::verify_oop_bits()") @Stable public long verifyOopBits; @HotSpotVMField(name = "CollectedHeap::_barrier_set", type = "BarrierSet*", get = HotSpotVMField.Type.OFFSET) @Stable public int collectedHeapBarrierSetOffset; @@ -1246,13 +1253,13 @@ @HotSpotVMType(name = "BasicLock", get = HotSpotVMType.Type.SIZE) @Stable public int basicLockSize; @HotSpotVMField(name = "BasicLock::_displaced_header", type = "markOop", get = HotSpotVMField.Type.OFFSET) @Stable public int basicLockDisplacedHeaderOffset; - @Stable public long heapEndAddress; - @Stable public long heapTopAddress; + @HotSpotVMValue(expression = "Universe::heap()->end_addr()", get = HotSpotVMValue.Type.ADDRESS) @Stable public long heapEndAddress; + @HotSpotVMValue(expression = "Universe::heap()->top_addr()", get = HotSpotVMValue.Type.ADDRESS) @Stable public long heapTopAddress; @HotSpotVMField(name = "Thread::_allocated_bytes", type = "jlong", get = HotSpotVMField.Type.OFFSET) @Stable public int threadAllocatedBytesOffset; @HotSpotVMFlag(name = "TLABWasteIncrement") @Stable public int tlabRefillWasteIncrement; - @Stable public int tlabAlignmentReserve; + @HotSpotVMValue(expression = "ThreadLocalAllocBuffer::alignment_reserve()") @Stable public int tlabAlignmentReserve; @HotSpotVMField(name = "ThreadLocalAllocBuffer::_start", type = "HeapWord*", get = HotSpotVMField.Type.OFFSET) @Stable private int threadLocalAllocBufferStartOffset; @HotSpotVMField(name = "ThreadLocalAllocBuffer::_end", type = "HeapWord*", get = HotSpotVMField.Type.OFFSET) @Stable private int threadLocalAllocBufferEndOffset; @@ -1301,7 +1308,7 @@ } @HotSpotVMFlag(name = "TLABStats") @Stable public boolean tlabStats; - @Stable public boolean inlineContiguousAllocationSupported; + @HotSpotVMValue(expression = " !CMSIncrementalMode && Universe::heap()->supports_inline_contig_alloc()") @Stable public boolean inlineContiguousAllocationSupported; /** * The DataLayout header size is the same as the cell size. @@ -1393,41 +1400,43 @@ @HotSpotVMField(name = "StubRoutines::_unsafe_arraycopy", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long unsafeArraycopy; @HotSpotVMField(name = "StubRoutines::_generic_arraycopy", type = "address", get = HotSpotVMField.Type.VALUE) @Stable public long genericArraycopy; - @Stable public long newInstanceAddress; - @Stable public long newArrayAddress; - @Stable public long newMultiArrayAddress; - @Stable public long dynamicNewArrayAddress; - @Stable public long dynamicNewInstanceAddress; - @Stable public long registerFinalizerAddress; - @Stable public long threadIsInterruptedAddress; - @Stable public long vmMessageAddress; - @Stable public long identityHashCodeAddress; - @Stable public long exceptionHandlerForPcAddress; - @Stable public long exceptionHandlerForReturnAddressAddress; - @Stable public long osrMigrationEndAddress; - @Stable public long monitorenterAddress; - @Stable public long monitorexitAddress; - @Stable public long createNullPointerExceptionAddress; - @Stable public long createOutOfBoundsExceptionAddress; - @Stable public long logPrimitiveAddress; - @Stable public long logObjectAddress; - @Stable public long logPrintfAddress; - @Stable public long vmErrorAddress; - @Stable public long writeBarrierPreAddress; - @Stable public long writeBarrierPostAddress; - @Stable public long validateObject; - @Stable public long javaTimeMillisAddress; - @Stable public long javaTimeNanosAddress; - @Stable public long arithmeticSinAddress; - @Stable public long arithmeticCosAddress; - @Stable public long arithmeticTanAddress; - @Stable public long loadAndClearExceptionAddress; + @HotSpotVMValue(expression = "GraalRuntime::new_instance", get = HotSpotVMValue.Type.ADDRESS) @Stable public long newInstanceAddress; + @HotSpotVMValue(expression = "GraalRuntime::new_array", get = HotSpotVMValue.Type.ADDRESS) @Stable public long newArrayAddress; + @HotSpotVMValue(expression = "GraalRuntime::new_multi_array", get = HotSpotVMValue.Type.ADDRESS) @Stable public long newMultiArrayAddress; + @HotSpotVMValue(expression = "GraalRuntime::dynamic_new_array", get = HotSpotVMValue.Type.ADDRESS) @Stable public long dynamicNewArrayAddress; + @HotSpotVMValue(expression = "GraalRuntime::dynamic_new_instance", get = HotSpotVMValue.Type.ADDRESS) @Stable public long dynamicNewInstanceAddress; + @HotSpotVMValue(expression = "GraalRuntime::thread_is_interrupted", get = HotSpotVMValue.Type.ADDRESS) @Stable public long threadIsInterruptedAddress; + @HotSpotVMValue(expression = "GraalRuntime::vm_message", get = HotSpotVMValue.Type.ADDRESS) @Stable public long vmMessageAddress; + @HotSpotVMValue(expression = "GraalRuntime::identity_hash_code", get = HotSpotVMValue.Type.ADDRESS) @Stable public long identityHashCodeAddress; + @HotSpotVMValue(expression = "GraalRuntime::exception_handler_for_pc", get = HotSpotVMValue.Type.ADDRESS) @Stable public long exceptionHandlerForPcAddress; + @HotSpotVMValue(expression = "GraalRuntime::monitorenter", get = HotSpotVMValue.Type.ADDRESS) @Stable public long monitorenterAddress; + @HotSpotVMValue(expression = "GraalRuntime::monitorexit", get = HotSpotVMValue.Type.ADDRESS) @Stable public long monitorexitAddress; + @HotSpotVMValue(expression = "GraalRuntime::create_null_exception", get = HotSpotVMValue.Type.ADDRESS) @Stable public long createNullPointerExceptionAddress; + @HotSpotVMValue(expression = "GraalRuntime::create_out_of_bounds_exception", get = HotSpotVMValue.Type.ADDRESS) @Stable public long createOutOfBoundsExceptionAddress; + @HotSpotVMValue(expression = "GraalRuntime::log_primitive", get = HotSpotVMValue.Type.ADDRESS) @Stable public long logPrimitiveAddress; + @HotSpotVMValue(expression = "GraalRuntime::log_object", get = HotSpotVMValue.Type.ADDRESS) @Stable public long logObjectAddress; + @HotSpotVMValue(expression = "GraalRuntime::log_printf", get = HotSpotVMValue.Type.ADDRESS) @Stable public long logPrintfAddress; + @HotSpotVMValue(expression = "GraalRuntime::vm_error", get = HotSpotVMValue.Type.ADDRESS) @Stable public long vmErrorAddress; + @HotSpotVMValue(expression = "GraalRuntime::load_and_clear_exception", get = HotSpotVMValue.Type.ADDRESS) @Stable public long loadAndClearExceptionAddress; + @HotSpotVMValue(expression = "GraalRuntime::write_barrier_pre", get = HotSpotVMValue.Type.ADDRESS) @Stable public long writeBarrierPreAddress; + @HotSpotVMValue(expression = "GraalRuntime::write_barrier_post", get = HotSpotVMValue.Type.ADDRESS) @Stable public long writeBarrierPostAddress; + @HotSpotVMValue(expression = "GraalRuntime::validate_object", get = HotSpotVMValue.Type.ADDRESS) @Stable public long validateObject; - @Stable public int graalCountersSize; + @HotSpotVMValue(expression = "SharedRuntime::register_finalizer", get = HotSpotVMValue.Type.ADDRESS) @Stable public long registerFinalizerAddress; + @HotSpotVMValue(expression = "SharedRuntime::exception_handler_for_return_address", get = HotSpotVMValue.Type.ADDRESS) @Stable public long exceptionHandlerForReturnAddressAddress; + @HotSpotVMValue(expression = "SharedRuntime::OSR_migration_end", get = HotSpotVMValue.Type.ADDRESS) @Stable public long osrMigrationEndAddress; - @Stable public long deoptimizationFetchUnrollInfo; - @Stable public long deoptimizationUncommonTrap; - @Stable public long deoptimizationUnpackFrames; + @HotSpotVMValue(expression = "os::javaTimeMillis", get = HotSpotVMValue.Type.ADDRESS) @Stable public long javaTimeMillisAddress; + @HotSpotVMValue(expression = "os::javaTimeNanos", get = HotSpotVMValue.Type.ADDRESS) @Stable public long javaTimeNanosAddress; + @HotSpotVMValue(expression = "SharedRuntime::dsin", get = HotSpotVMValue.Type.ADDRESS) @Stable public long arithmeticSinAddress; + @HotSpotVMValue(expression = "SharedRuntime::dcos", get = HotSpotVMValue.Type.ADDRESS) @Stable public long arithmeticCosAddress; + @HotSpotVMValue(expression = "SharedRuntime::dtan", get = HotSpotVMValue.Type.ADDRESS) @Stable public long arithmeticTanAddress; + + @HotSpotVMValue(expression = "(jint) GraalCounterSize") @Stable public int graalCountersSize; + + @HotSpotVMValue(expression = "Deoptimization::fetch_unroll_info", get = HotSpotVMValue.Type.ADDRESS) @Stable public long deoptimizationFetchUnrollInfo; + @HotSpotVMValue(expression = "Deoptimization::uncommon_trap", get = HotSpotVMValue.Type.ADDRESS) @Stable public long deoptimizationUncommonTrap; + @HotSpotVMValue(expression = "Deoptimization::unpack_frames", get = HotSpotVMValue.Type.ADDRESS) @Stable public long deoptimizationUnpackFrames; @HotSpotVMConstant(name = "Deoptimization::Reason_none") @Stable public int deoptReasonNone; @HotSpotVMConstant(name = "Deoptimization::Reason_null_check") @Stable public int deoptReasonNullCheck; diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConstant.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConstant.java Tue May 13 19:19:27 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +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.hotspot; - -import java.lang.annotation.*; - -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface HotSpotVMConstant { - - String name(); - - /** - * List of architectures where this constant is required. Names are derived from - * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is - * required on all architectures. - */ - String[] archs() default {}; - -} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMField.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMField.java Tue May 13 19:19:27 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +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.hotspot; - -import java.lang.annotation.*; - -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface HotSpotVMField { - - enum Type { - OFFSET, - ADDRESS, - VALUE; - } - - String name(); - - String type(); - - Type get(); - - /** - * List of architectures where this constant is required. Names are derived from - * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is - * required on all architectures. - */ - String[] archs() default {}; - - boolean optional() default false; -} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMFlag.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMFlag.java Tue May 13 19:19:27 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +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.hotspot; - -import java.lang.annotation.*; - -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface HotSpotVMFlag { - - String name(); - - /** - * List of architectures where this constant is required. Names are derived from - * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is - * required on all architectures. - */ - String[] archs() default {}; - - boolean optional() default false; -} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMType.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMType.java Tue May 13 19:19:27 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +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.hotspot; - -import java.lang.annotation.*; - -@Target(ElementType.FIELD) -@Retention(RetentionPolicy.RUNTIME) -public @interface HotSpotVMType { - - enum Type { - SIZE; - } - - String name(); - - Type get(); -} diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java Wed May 21 11:45:50 2014 +0200 @@ -35,13 +35,12 @@ public interface CompilerToVM { /** - * Copies the original bytecode of a given method into a given byte array. + * Copies the original bytecode of a given method into a new byte array and returns it. * * @param metaspaceMethod the metaspace Method object - * @param code the array into which to copy the original bytecode - * @return the value of {@code code} + * @return a new byte array containing the original bytecode */ - byte[] initializeBytecode(long metaspaceMethod, byte[] code); + byte[] initializeBytecode(long metaspaceMethod); int exceptionTableLength(long metaspaceMethod); @@ -248,7 +247,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 +352,6 @@ void materializeVirtualObjects(HotSpotStackFrameReference stackFrame, boolean invalidate); void resolveInvokeDynamic(long metaspaceConstantPool, int index); + + int getVtableIndexForInterface(long metaspaceKlass, long metaspaceMethod); } diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java Wed May 21 11:45:50 2014 +0200 @@ -43,7 +43,7 @@ public native long getMetaspaceMethod(Class holder, int slot); @Override - public native byte[] initializeBytecode(long metaspaceMethod, byte[] code); + public native byte[] initializeBytecode(long metaspaceMethod); @Override public native int exceptionTableLength(long metaspaceMethod); @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/DefaultHotSpotLoweringProvider.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantPool.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java Wed May 21 11:45:50 2014 +0200 @@ -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.*; @@ -195,7 +195,7 @@ return null; } if (code == null && holder.isLinked()) { - code = runtime().getCompilerToVM().initializeBytecode(metaspaceMethod, new byte[getCodeSize()]); + code = runtime().getCompilerToVM().initializeBytecode(metaspaceMethod); assert code.length == getCodeSize() : "expected: " + getCodeSize() + ", actual: " + code.length; } return code; @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedPrimitiveType.java Wed May 21 11:45:50 2014 +0200 @@ -163,7 +163,7 @@ } @Override - public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method) { + public ResolvedJavaMethod resolveMethod(ResolvedJavaMethod method, ResolvedJavaType callerType) { return null; } diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotUnresolvedJavaType.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/NewArrayStub.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/StubUtil.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspotvmconfig/src/META-INF/services/javax.annotation.processing.Processor --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig/src/META-INF/services/javax.annotation.processing.Processor Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,1 @@ +com.oracle.graal.hotspotvmconfig.HotSpotVMConfigProcessor diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMConfigProcessor.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMConfigProcessor.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,417 @@ +/* + * 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.hotspotvmconfig; + +import java.io.*; +import java.lang.annotation.*; +import java.util.*; +import java.util.Map.Entry; + +import javax.annotation.processing.*; +import javax.lang.model.*; +import javax.lang.model.element.*; +import javax.tools.Diagnostic.Kind; +import javax.tools.*; + +import com.oracle.graal.compiler.common.*; + +@SupportedAnnotationTypes({"com.oracle.graal.hotspotvmconfig.HotSpotVMConstant", "com.oracle.graal.hotspotvmconfig.HotSpotVMFlag", "com.oracle.graal.hotspotvmconfig.HotSpotVMField", + "com.oracle.graal.hotspotvmconfig.HotSpotVMType", "com.oracle.graal.hotspotvmconfig.HotSpotVMValue"}) +public class HotSpotVMConfigProcessor extends AbstractProcessor { + + public HotSpotVMConfigProcessor() { + } + + @Override + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latest(); + } + + /** + * Set to true to enable logging to a local file during annotation processing. There's no normal + * channel for any debug messages and debugging annotation processors requires some special + * setup. + */ + private static final boolean DEBUG = true; + + private static final String LOGFILE = "/tmp/hotspotvmconfigprocessor.log"; + + private static PrintWriter log; + + /** + * Logging facility for the debugging the annotation processor. + */ + + private static synchronized PrintWriter getLog() { + if (log == null) { + try { + log = new PrintWriter(new FileWriter(LOGFILE, true)); + } catch (IOException e) { + // Do nothing + } + } + return log; + } + + private static synchronized void logMessage(String format, Object... args) { + if (!DEBUG) { + return; + } + PrintWriter bw = getLog(); + if (bw != null) { + bw.printf(format, args); + bw.flush(); + } + } + + private static synchronized void logException(Throwable t) { + if (!DEBUG) { + return; + } + PrintWriter bw = getLog(); + if (bw != null) { + t.printStackTrace(bw); + bw.flush(); + } + } + + /** + * Bugs in an annotation processor can cause silent failure so try to report any exception + * throws as errors. + */ + private void reportExceptionThrow(Element element, Throwable t) { + if (element != null) { + logMessage("throw for %s:\n", element); + } + logException(t); + processingEnv.getMessager().printMessage(Kind.ERROR, "Exception throw during processing: " + t.toString() + " " + Arrays.toString(Arrays.copyOf(t.getStackTrace(), 8)), element); + } + + //@formatter:off + String[] prologue = new String[]{ + "// The normal wrappers boolAt and intxAt skip constant flags", + "static bool boolAt(char* name, bool* value) {", + " Flag* result = Flag::find_flag(name, strlen(name), true, true);", + " if (result == NULL) return false;", + " if (!result->is_bool()) return false;", + " *value = result->get_bool();", + " return true;", + "}", + "", + "static bool intxAt(char* name, intx* value) {", + " Flag* result = Flag::find_flag(name, strlen(name), true, true);", + " if (result == NULL) return false;", + " if (!result->is_intx()) return false;", + " *value = result->get_intx();", + " return true;", + "}", + "", + "#define set_boolean(name, value) vmconfig_oop->bool_field_put(fs.offset(), value)", + "#define set_int(name, value) vmconfig_oop->int_field_put(fs.offset(), (int)value)", + "#define set_long(name, value) vmconfig_oop->long_field_put(fs.offset(), value)", + "#define set_address(name, value) do { set_long(name, (jlong) value); } while (0)", + "", + "#define set_optional_boolean_flag(varName, flagName) do { bool flagValue; if (boolAt((char*) flagName, &flagValue)) { set_boolean(varName, flagValue); } } while (0)", + "#define set_optional_int_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_int(varName, flagValue); } } while (0)", + "#define set_optional_long_flag(varName, flagName) do { intx flagValue; if (intxAt((char*) flagName, &flagValue)) { set_long(varName, flagValue); } } while (0)", + "", + "void VMStructs::initHotSpotVMConfig(oop vmconfig_oop) {", + " InstanceKlass* vmconfig_klass = InstanceKlass::cast(vmconfig_oop->klass());", + "", + " for (JavaFieldStream fs(vmconfig_klass); !fs.done(); fs.next()) {", + }; + //@formatter:on + + String outputName = "HotSpotVMConfig.inline.hpp"; + String outputDirectory = "hotspot"; + + private void createFiles(Map annotations, Element element) { + + Filer filer = processingEnv.getFiler(); + try (PrintWriter out = createSourceFile(outputDirectory, outputName, filer, element)) { + + for (String line : prologue) { + out.println(line); + } + + out.println(); + + Set fieldTypes = new HashSet<>(); + for (String key : annotations.keySet()) { + fieldTypes.add(annotations.get(key).getType()); + } + // For each type of field, generate a switch on the length of the symbol and then do a + // direct compare. In general this reduces each operation to 2 tests plus a string + // compare. Being more prefect than that is probably not worth it. + for (String type : fieldTypes) { + String sigtype = type.equals("boolean") ? "bool" : type; + out.println(" if (fs.signature() == vmSymbols::" + sigtype + "_signature()) {"); + Set lengths = new HashSet<>(); + for (Entry entry : annotations.entrySet()) { + if (entry.getValue().getType().equals(type)) { + lengths.add(entry.getKey().length()); + } + } + out.println(" switch (fs.name()->utf8_length()) {"); + for (int len : lengths) { + out.println(" case " + len + ":"); + for (Entry entry : annotations.entrySet()) { + if (entry.getValue().getType().equals(type) && entry.getKey().length() == len) { + out.println(" if (fs.name()->equals(\"" + entry.getKey() + "\")) {"); + entry.getValue().emit(out); + out.println(" continue;"); + out.println(" }"); + } + } + out.println(" continue;"); + } + out.println(" } // switch"); + out.println(" continue;"); + out.println(" } // if"); + } + out.println(" } // for"); + out.println("}"); + } + } + + protected PrintWriter createSourceFile(String pkg, String relativeName, Filer filer, Element... originatingElements) { + try { + // Ensure Unix line endings to comply with Graal code style guide checked by Checkstyle + FileObject sourceFile = filer.createResource(StandardLocation.SOURCE_OUTPUT, pkg, relativeName, originatingElements); + logMessage("%s\n", sourceFile); + return new PrintWriter(sourceFile.openWriter()) { + + @Override + public void println() { + print("\n"); + } + }; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + class VMConfigField { + final VariableElement field; + final Annotation annotation; + + public VMConfigField(VariableElement field, Annotation value) { + super(); + this.field = field; + this.annotation = value; + } + + public String getType() { + return field.asType().toString(); + } + + private String archDefine(String arch) { + switch (arch) { + case "amd64": + return "defined(AMD64)"; + case "sparcv9": + return "(defined(SPARC) && defined(_LP64))"; + case "sparc": + return "defined(SPARC)"; + default: + throw new GraalInternalError("unexpected arch: " + arch); + } + } + + private String archDefines(String[] archs) { + if (archs.length == 0) { + return null; + } + if (archs.length == 1) { + return archDefine(archs[0]); + } + String[] defs = new String[archs.length]; + int i = 0; + for (String arch : archs) { + defs[i++] = archDefine(arch); + } + return String.join(" ||", defs); + } + + public void emit(PrintWriter out) { + if (annotation instanceof HotSpotVMField) { + emitField(out, (HotSpotVMField) annotation); + } else if (annotation instanceof HotSpotVMType) { + emitType(out, (HotSpotVMType) annotation); + } else if (annotation instanceof HotSpotVMFlag) { + emitFlag(out, (HotSpotVMFlag) annotation); + } else if (annotation instanceof HotSpotVMConstant) { + emitConstant(out, (HotSpotVMConstant) annotation); + } else if (annotation instanceof HotSpotVMValue) { + emitValue(out, (HotSpotVMValue) annotation); + } else { + throw new InternalError(annotation.toString()); + } + + } + + private void emitField(PrintWriter out, HotSpotVMField value) { + String type = field.asType().toString(); + String define = archDefines(value.archs()); + if (define != null) { + out.printf("#if %s\n", define); + } + + String name = value.name(); + int i = name.lastIndexOf("::"); + switch (value.get()) { + case OFFSET: + out.printf(" set_%s(\"%s\", offset_of(%s, %s));\n", type, field.getSimpleName(), name.substring(0, i), name.substring(i + 2)); + break; + case ADDRESS: + out.printf(" set_address(\"%s\", &%s);\n", field.getSimpleName(), name); + break; + case VALUE: + out.printf(" set_%s(\"%s\", (%s) (intptr_t) %s);\n", type, field.getSimpleName(), type, name); + break; + } + if (define != null) { + out.printf("#endif\n"); + } + } + + private void emitType(PrintWriter out, HotSpotVMType value) { + String type = field.asType().toString(); + out.printf(" set_%s(\"%s\", sizeof(%s));\n", type, field.getSimpleName(), value.name()); + } + + private void emitValue(PrintWriter out, HotSpotVMValue value) { + String type = field.asType().toString(); + int length = value.defines().length; + if (length != 0) { + out.printf("#if "); + for (int i = 0; i < length; i++) { + out.printf("defined(%s)", value.defines()[i]); + if (i + 1 < length) { + out.printf(" || "); + } + } + out.println(); + } + if (value.get() == HotSpotVMValue.Type.ADDRESS) { + out.printf(" set_address(\"%s\", %s);\n", field.getSimpleName(), value.expression()); + } else { + out.printf(" set_%s(\"%s\", %s);\n", type, field.getSimpleName(), value.expression()); + } + if (length != 0) { + out.println("#endif"); + } + } + + private void emitConstant(PrintWriter out, HotSpotVMConstant value) { + String define = archDefines(value.archs()); + if (define != null) { + out.printf("#if %s\n", define); + } + String type = field.asType().toString(); + out.printf(" set_%s(\"%s\", %s);\n", type, field.getSimpleName(), value.name()); + if (define != null) { + out.printf("#endif\n"); + } + } + + private void emitFlag(PrintWriter out, HotSpotVMFlag value) { + String type = field.asType().toString(); + + String define = archDefines(value.archs()); + if (define != null) { + out.printf("#if %s\n", define); + } + if (value.optional()) { + out.printf(" set_optional_%s_flag(\"%s\", \"%s\");\n", type, field.getSimpleName(), value.name()); + } else { + out.printf(" set_%s(\"%s\", %s);\n", type, field.getSimpleName(), value.name()); + } + if (define != null) { + out.printf("#endif\n"); + } + } + + } + + private void collectAnnotations(RoundEnvironment roundEnv, Map annotationMap, Class annotationClass) { + for (Element element : roundEnv.getElementsAnnotatedWith(annotationClass)) { + Annotation constant = element.getAnnotation(annotationClass); + if (element.getKind() != ElementKind.FIELD) { + errorMessage(element, "%s annotations may only be on fields", annotationClass.getSimpleName()); + } + if (annotationClass == HotSpotVMValue.class) { + HotSpotVMValue value = (HotSpotVMValue) constant; + if (value.get() == HotSpotVMValue.Type.ADDRESS && !element.asType().toString().equals("long")) { + errorMessage(element, "HotSpotVMValue with get == ADDRESS must be of type long, but found %s", element.asType()); + } + } + if (currentTypeElement == null) { + currentTypeElement = element.getEnclosingElement(); + } else { + if (!currentTypeElement.equals(element.getEnclosingElement())) { + errorMessage(element, "Multiple types encountered. Only HotSpotVMConfig is supported"); + } + } + annotationMap.put(element.getSimpleName().toString(), new VMConfigField((VariableElement) element, constant)); + } + } + + private void errorMessage(Element element, String format, Object... args) { + processingEnv.getMessager().printMessage(Kind.ERROR, String.format(format, args), element); + } + + Element currentTypeElement = null; + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (roundEnv.processingOver()) { + return true; + } + logMessage("Starting round %s %s\n", roundEnv, annotations); + try { + + currentTypeElement = null; + + // First collect all the annotations. + Map annotationMap = new HashMap<>(); + collectAnnotations(roundEnv, annotationMap, HotSpotVMConstant.class); + collectAnnotations(roundEnv, annotationMap, HotSpotVMFlag.class); + collectAnnotations(roundEnv, annotationMap, HotSpotVMField.class); + collectAnnotations(roundEnv, annotationMap, HotSpotVMType.class); + collectAnnotations(roundEnv, annotationMap, HotSpotVMValue.class); + + if (annotationMap.isEmpty()) { + return true; + } + + logMessage("type element %s\n", currentTypeElement); + createFiles(annotationMap, currentTypeElement); + + } catch (Throwable t) { + reportExceptionThrow(null, t); + } + + return true; + } +} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMConstant.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMConstant.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,52 @@ +/* + * 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.hotspotvmconfig; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Refers to a C++ constant in the VM. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface HotSpotVMConstant { + + /** + * Returns the name of the constant. + * + * @return name of constant + */ + String name(); + + /** + * List of architectures where this constant is required. Names are derived from + * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is + * required on all architectures. + */ + @SuppressWarnings("javadoc") + String[] archs() default {}; + +} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMField.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMField.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,85 @@ +/* + * 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.hotspotvmconfig; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Refers to a C++ field in the VM. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface HotSpotVMField { + + /** + * Types of information this annotation can return. + */ + enum Type { + /** + * Returns the offset of this field within the type. Only valid for instance fields. + */ + OFFSET, + + /** + * Returns the absolute address of this field. Only valid for static fields. + */ + ADDRESS, + + /** + * Returns the value of this field. Only valid for static fields. + */ + VALUE; + } + + /** + * Specifies what type of information to return. + * + * @see Type + */ + Type get(); + + /** + * Returns the type name containing this field. + * + * @return name of containing type + */ + String type(); + + /** + * Returns the name of this field. + * + * @return name of field + */ + String name(); + + /** + * List of architectures where this constant is required. Names are derived from + * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is + * required on all architectures. + */ + @SuppressWarnings("javadoc") + String[] archs() default {}; +} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMFlag.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMFlag.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,53 @@ +/* + * 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.hotspotvmconfig; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Refers to a C++ flag in the VM. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface HotSpotVMFlag { + + /** + * Returns the name of this flag. + * + * @return name of flag. + */ + String name(); + + /** + * List of architectures where this constant is required. Names are derived from + * {@link HotSpotVMConfig#getHostArchitectureName()}. An empty list implies that the constant is + * required on all architectures. + */ + @SuppressWarnings("javadoc") + String[] archs() default {}; + + boolean optional() default false; +} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMType.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMType.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,57 @@ +/* + * 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.hotspotvmconfig; + +import java.lang.annotation.*; + +/** + * Refers to a C++ type in the VM. + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface HotSpotVMType { + + /** + * Types of information this annotation can return. + */ + enum Type { + /** + * Returns the size of the type (C++ {@code sizeof()}). + */ + SIZE; + } + + /** + * Specifies what type of information to return. + * + * @see Type + */ + Type get(); + + /** + * Returns the name of the type. + * + * @return name of type + */ + String name(); +} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMValue.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.hotspotvmconfig/src/com/oracle/graal/hotspotvmconfig/HotSpotVMValue.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,55 @@ +/* + * 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.hotspotvmconfig; + +import java.lang.annotation.*; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface HotSpotVMValue { + + /** + * A C++ expression to be evaluated and assigned to the field. + */ + String expression(); + + enum Type { + /** + * A C++ address which might require extra casts to be safely assigned to a Java field. + */ + ADDRESS, + + /** + * A simple value which can be assigned to a regular Java field. + */ + VALUE + } + + Type get() default Type.VALUE; + + /** + * List of preprocessor symbols that should guard initialization of this value. + */ + String[] defines() default {}; + +} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/JTTTest.java --- a/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/JTTTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/JTTTest.java Wed May 21 11:45:50 2014 +0200 @@ -80,11 +80,11 @@ Double delta; @Override - protected void assertEquals(Object expected, Object actual) { + protected void assertDeepEquals(Object expected, Object actual) { if (delta != null) { Assert.assertEquals(((Number) expected).doubleValue(), ((Number) actual).doubleValue(), delta); } else { - super.assertEquals(expected, actual); + super.assertDeepEquals(expected, actual); } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/lang/Class_getInterfaces01.java --- a/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/lang/Class_getInterfaces01.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/lang/Class_getInterfaces01.java Wed May 21 11:45:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 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 @@ -30,36 +30,8 @@ */ public final class Class_getInterfaces01 extends JTTTest { - public static String test(int i) { - switch (i) { - case 0: - return toString(I1.class); - case 1: - return toString(I2.class); - case 2: - return toString(C1.class); - case 3: - return toString(C2.class); - case 4: - return toString(C12.class); - default: - return null; - } - } - - private static String toString(Class klass) { - final Class[] classes = klass.getInterfaces(); - final StringBuilder sb = new StringBuilder(); - boolean first = true; - for (Class c : classes) { - if (!first) { - sb.append(' '); - } else { - first = false; - } - sb.append(c.getName()); - } - return sb.toString(); + public static Class[] test(Class clazz) { + return clazz.getInterfaces(); } interface I1 { @@ -84,27 +56,26 @@ @Test public void run0() throws Throwable { - runTest("test", 0); + runTest("test", I1.class); } @Test public void run1() throws Throwable { - runTest("test", 1); + runTest("test", I2.class); } @Test public void run2() throws Throwable { - runTest("test", 2); + runTest("test", C1.class); } @Test public void run3() throws Throwable { - runTest("test", 3); + runTest("test", C2.class); } @Test public void run4() throws Throwable { - runTest("test", 4); + runTest("test", C12.class); } - } diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Arithmetic.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64Compare.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/gen/ArithmeticLIRGenerator.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes.test/src/com/oracle/graal/nodes/test/ObjectStampJoinTest.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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.assertEquals(StampFactory.declared(getType(Object.class)), 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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardedValueNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardingPiNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/IfNode.java Wed May 21 11:45:50 2014 +0200 @@ -188,6 +188,10 @@ } while (false); } + if (checkForUnsignedCompare(tool)) { + return; + } + if (condition() instanceof LogicConstantNode) { LogicConstantNode c = (LogicConstantNode) condition(); if (c.getValue()) { @@ -220,7 +224,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); @@ -244,6 +248,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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/Invoke.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValuePhiNode.java Wed May 21 11:45:50 2014 +0200 @@ -54,10 +54,6 @@ @Override public boolean inferStamp() { - return inferPhiStamp(); - } - - public boolean inferPhiStamp() { return updateStamp(StampTool.meet(values())); } } diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConditionalNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerConvertNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerRemNode.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerRemNode.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerRemNode.java Wed May 21 11:45:50 2014 +0200 @@ -28,6 +28,7 @@ import com.oracle.graal.graph.spi.*; import com.oracle.graal.nodes.*; import com.oracle.graal.nodes.spi.*; +import com.oracle.graal.nodes.type.*; @NodeInfo(shortName = "%") public class IntegerRemNode extends FixedBinaryNode implements Canonicalizable, Lowerable, LIRLowerable { @@ -37,6 +38,11 @@ } @Override + public boolean inferStamp() { + return updateStamp(StampTool.rem(x().stamp(), y().stamp())); + } + + @Override public Node canonical(CanonicalizerTool tool) { if (x().isConstant() && y().isConstant()) { long y = y().asConstant().asLong(); diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadMethodNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MethodCallTargetNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampTool.java --- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampTool.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/type/StampTool.java Wed May 21 11:45:50 2014 +0200 @@ -105,6 +105,37 @@ return stamp1.unrestricted(); } + public static Stamp rem(Stamp stamp1, Stamp stamp2) { + if (stamp1 instanceof IntegerStamp && stamp2 instanceof IntegerStamp) { + return rem((IntegerStamp) stamp1, (IntegerStamp) stamp2); + } + return StampFactory.illegal(); + } + + public static Stamp rem(IntegerStamp stamp1, IntegerStamp stamp2) { + assert stamp1.getBits() == stamp2.getBits(); + long magnitude; // the maximum absolute value of the result + if (stamp2.lowerBound() == IntegerStamp.defaultMinValue(stamp2.getBits())) { + // Math.abs(...) - 1 does not work in this case + magnitude = IntegerStamp.defaultMaxValue(stamp2.getBits()); + } else { + magnitude = Math.max(Math.abs(stamp2.lowerBound()), Math.abs(stamp2.upperBound())) - 1; + } + long lowerBound = Math.max(stamp1.lowerBound(), -magnitude); + if (stamp1.upperBound() > magnitude) { + // if the result can wrap around at the upper bound, it can reach any value between 0 + // and magnitude + lowerBound = Math.min(lowerBound, 0); + } + long upperBound = Math.min(stamp1.upperBound(), magnitude); + if (stamp1.lowerBound() < -magnitude) { + // if the result can wrap around at the lower bound, it can reach any value between + // -magnitude and 0 + upperBound = Math.max(upperBound, 0); + } + return StampFactory.forInteger(stamp1.getBits(), lowerBound, upperBound); + } + private static boolean addOverflowsPositively(long x, long y, int bits) { long result = x + y; if (bits == 64) { diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ConditionalEliminationPhase.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeoptimizationGroupingPhase.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java Wed May 21 11:45:50 2014 +0200 @@ -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,111 @@ 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; + private static void tryUseTrappingNullCheck(MetaAccessProvider metaAccessProvider, DynamicDeoptimizeNode deopt) { + Node predecessor = deopt.predecessor(); + if (predecessor instanceof MergeNode) { + MergeNode merge = (MergeNode) predecessor; + + // Process each predecessor at the merge, unpacking the reasons and speculations as + // needed. + ValueNode reason = deopt.getActionAndReason(); + ValuePhiNode reasonPhi = null; + List reasons = null; + int expectedPhis = 0; + + if (reason instanceof ValuePhiNode) { + reasonPhi = (ValuePhiNode) reason; + if (reasonPhi.merge() != merge) { + return; + } + reasons = reasonPhi.values().snapshot(); + expectedPhis++; + } else if (!reason.isConstant()) { + return; + } + + ValueNode speculation = deopt.getSpeculation(); + ValuePhiNode speculationPhi = null; + List speculations = null; + if (speculation instanceof ValuePhiNode) { + speculationPhi = (ValuePhiNode) speculation; + if (speculationPhi.merge() != merge) { + return; + } + speculations = speculationPhi.values().snapshot(); + expectedPhis++; + } + + if (merge.phis().count() != expectedPhis) { + return; + } + + int index = 0; + for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) { + ValueNode thisReason = reasons != null ? reasons.get(index) : reason; + ValueNode thisSpeculation = speculations != null ? speculations.get(index++) : speculation; + if (!thisReason.isConstant() || !thisSpeculation.isConstant() || !thisSpeculation.asConstant().equals(Constant.NULL_OBJECT)) { + continue; + } + DeoptimizationReason deoptimizationReason = metaAccessProvider.decodeDeoptReason(thisReason.asConstant()); + tryUseTrappingNullCheck(deopt, end.predecessor(), deoptimizationReason, null); + } } - if (deopt.getSpeculation() != null && !deopt.getSpeculation().equals(Constant.NULL_OBJECT)) { + } + + private static void tryUseTrappingNullCheck(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason, Constant speculation) { + if (deoptimizationReason != DeoptimizationReason.NullCheckException && deoptimizationReason != DeoptimizationReason.UnreachedCode) { return; } - Node predecessor = deopt.predecessor(); + 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 +161,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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/FlowSensitiveReduction.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/cfs/State.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ /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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ /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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningPhase.java Wed May 21 11:45:50 2014 +0200 @@ -22,37 +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.compiler.common.type.*; -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") @@ -66,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); } @@ -93,38 +71,79 @@ return inliningCount; } + /** + *

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

    + *
  • + * 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 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.
  • + *
+ *

+ * + *

+ * The bottom-most element in the stack consists of: + *

    + *
  • + * a single {@link CallsiteHolder} (the root one, for the method on which inlining was called)
  • + *
  • + * a single {@link MethodInvocation} (the + * {@link com.oracle.graal.phases.common.inlining.walker.MethodInvocation#isRoot} one, ie the + * unknown caller of the root graph)
  • + *
+ * + *

+ * + *

+ * The stack grows and shrinks as choices are made among the alternatives below: + *

    + *
  1. + * not worth inlining: pop any remaining graphs not yet delved into, pop the current invocation. + *
  2. + *
  3. + * process next invoke: delve into one of the callsites hosted in the current candidate graph, + * determine whether any inlining should be performed in it
  4. + *
  5. + * 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 com.oracle.graal.phases.common.inlining.walker.InliningData}, and then + * remove such callsite.
  6. + *
+ *

+ * + *

+ * Some facts about the alternatives above: + *

    + *
  • + * the first step amounts to backtracking, the 2nd one to delving, and the 3rd one also involves + * 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 ones aren't picked
  • + *
  • + * as part of trying-to-inline, {@link InliningPolicy#isWorthInlining} again sees use, but + * that's another story.
  • + *
+ *

+ * + */ @Override protected void run(final StructuredGraph graph, final HighTierContext context) { - final InliningData data = new InliningData(graph, context.getAssumptions()); - ToDoubleFunction probabilities = new FixedNodeProbabilityCache(); + final InliningData data = new InliningData(graph, context, maxMethodPerInlining, canonicalizer, inliningPolicy); while (data.hasUnprocessedGraphs()) { - final MethodInvocation currentInvocation = data.currentInvocation(); - GraphInfo graphInfo = data.currentGraph(); - 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 (graphInfo.hasRemainingInvokes() && inliningPolicy.continueInlining(graphInfo.graph())) { - processNextInvoke(data, graphInfo, 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++; } } @@ -132,645 +151,4 @@ assert data.graphCount() == 0; } - /** - * Process the next invoke and enqueue all its graphs for processing. - */ - private void processNextInvoke(InliningData data, GraphInfo graphInfo, HighTierContext context) { - Invoke invoke = graphInfo.popInvoke(); - MethodInvocation callerInvocation = data.currentInvocation(); - Assumptions parentAssumptions = callerInvocation.assumptions(); - InlineInfo info = InliningUtil.getInlineInfo(data, invoke, maxMethodPerInlining, context.getReplacements(), parentAssumptions, context.getOptimisticOptimizations()); - - if (info != null) { - double invokeProbability = graphInfo.invokeProbability(invoke); - double invokeRelevance = graphInfo.invokeRelevance(invoke); - MethodInvocation calleeInvocation = data.pushInvocation(info, parentAssumptions, invokeProbability, invokeRelevance); - - for (int i = 0; i < info.numberOfMethods(); i++) { - Inlineable elem = getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeInvocation.assumptions())); - info.setInlinableElement(i, elem); - if (elem instanceof InlineableGraph) { - data.pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i)); - } else { - assert elem instanceof InlineableMacroNode; - data.pushDummyGraph(); - } - } - } - } - - 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 Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context) { - Class macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method); - if (macroNodeClass != null) { - return new InlineableMacroNode(macroNodeClass); - } else { - return new InlineableGraph(buildGraph(method, invoke, context)); - } - } - - private StructuredGraph buildGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context) { - 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 (Scope s = Debug.scope("InlineGraph", newGraph)) { - if (parseBytecodes) { - parseBytecodes(newGraph, context); - } - - 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; - } - - private StructuredGraph parseBytecodes(StructuredGraph newGraph, HighTierContext context) { - 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; - } - - 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 int maxGraphs; - - public InliningData(StructuredGraph rootGraph, Assumptions rootAssumptions) { - this.graphQueue = new ArrayDeque<>(); - this.invocationQueue = new ArrayDeque<>(); - this.maxGraphs = 1; - - invocationQueue.push(new MethodInvocation(null, rootAssumptions, 1.0, 1.0)); - pushGraph(rootGraph, 1.0, 1.0); - } - - 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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java Wed May 21 11:45:50 2014 +0200 @@ -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; @@ -51,15 +46,10 @@ import com.oracle.graal.nodes.spi.*; 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.*; 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,66 +58,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(); - - Iterable getInvokes(); - } - - public static class InlineableGraph implements Inlineable { - - private final StructuredGraph graph; - - public InlineableGraph(StructuredGraph graph) { - this.graph = graph; - } - - @Override - public int getNodeCount() { - return graph.getNodeCount(); - } - - @Override - public Iterable getInvokes() { - return graph.getInvokes(); - } - - public StructuredGraph getGraph() { - return graph; - } - } - - public static class InlineableMacroNode implements Inlineable { - - private final Class macroNodeClass; - - public InlineableMacroNode(Class macroNodeClass) { - this.macroNodeClass = macroNodeClass; - } - - @Override - public int getNodeCount() { - return 1; - } - - @Override - public Iterable getInvokes() { - return Collections.emptyList(); - } - - public Class getMacroNodeClass() { - return macroNodeClass; - } - } - /** * Print a HotSpot-style inlining message to the console. */ @@ -155,22 +85,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 +111,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 +173,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,45 +188,32 @@ 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) { - // 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 is dead code"; + } + if (!(invoke.callTarget() instanceof MethodCallTargetNode)) { + return "the invoke has already been lowered, or has been created as a low-level node"; } - } - - private static boolean checkTargetConditions(InliningData data, Replacements replacements, Invoke invoke, ResolvedJavaMethod method, OptimisticOptimizations optimisticOpts) { - if (method == null) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "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"); - } else if (method.isAbstract()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "it is an abstract method"); - } else if (!method.getDeclaringClass().isInitialized()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the method's class is not initialized"); - } else if (!method.canBeInlined()) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "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"); - } else if (new OptimisticOptimizations(method.getProfilingInfo()).lessOptimisticThan(optimisticOpts)) { - return logNotInlinedMethodAndReturnFalse(invoke, data.inliningDepth(), method, "the callee uses less optimistic optimizations than caller"); - } else { - return true; + 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 "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; } /** diff -r a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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.info.elem.Inlineable; +import com.oracle.graal.phases.common.inlining.info.elem.InlineableMacroNode; +import com.oracle.graal.phases.common.inlining.info.elem.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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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.common.inlining.info.elem.Inlineable; +import com.oracle.graal.phases.util.Providers; + +/** + * 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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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.info.elem.Inlineable; +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); + + 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(); +} diff -r a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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.info.elem.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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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.common.inlining.info.elem.Inlineable; +import com.oracle.graal.phases.util.Providers; +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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/elem/Inlineable.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/elem/Inlineable.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.phases.common.inlining.info.elem; + +import com.oracle.graal.api.meta.ResolvedJavaMethod; +import com.oracle.graal.nodes.FixedWithNextNode; +import com.oracle.graal.nodes.Invoke; +import com.oracle.graal.phases.common.CanonicalizerPhase; +import com.oracle.graal.phases.common.inlining.InliningUtil; +import com.oracle.graal.phases.tiers.HighTierContext; + +public interface Inlineable { + + static Inlineable getInlineableElement(final ResolvedJavaMethod method, Invoke invoke, HighTierContext context, CanonicalizerPhase canonicalizer) { + Class macroNodeClass = InliningUtil.getMacroNodeClass(context.getReplacements(), method); + if (macroNodeClass != null) { + return new InlineableMacroNode(macroNodeClass); + } else { + return new InlineableGraph(method, invoke, context, canonicalizer); + } + } + + int getNodeCount(); + + Iterable getInvokes(); +} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/elem/InlineableGraph.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/elem/InlineableGraph.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.phases.common.inlining.info.elem; + +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; + +public class InlineableGraph implements Inlineable { + + private final StructuredGraph graph; + + public InlineableGraph(final ResolvedJavaMethod method, final Invoke invoke, final HighTierContext context, CanonicalizerPhase canonicalizer) { + this.graph = 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; + } + + @Override + public int getNodeCount() { + return graph.getNodeCount(); + } + + @Override + public Iterable getInvokes() { + return graph.getInvokes(); + } + + public StructuredGraph getGraph() { + return graph; + } +} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/info/elem/InlineableMacroNode.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/elem/InlineableMacroNode.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.phases.common.inlining.info.elem; + +import com.oracle.graal.nodes.FixedWithNextNode; +import com.oracle.graal.nodes.Invoke; + +import java.util.Collections; + +public class InlineableMacroNode implements Inlineable { + + private final Class macroNodeClass; + + public InlineableMacroNode(Class macroNodeClass) { + this.macroNodeClass = macroNodeClass; + } + + @Override + public int getNodeCount() { + return 1; + } + + @Override + public Iterable getInvokes() { + return Collections.emptyList(); + } + + public Class getMacroNodeClass() { + return macroNodeClass; + } +} diff -r a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,127 @@ +/* + * 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 com.oracle.graal.phases.common.inlining.info.elem.Inlineable; + +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++) { + 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; + } +} diff -r a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,596 @@ +/* + * 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.info.elem.Inlineable; +import com.oracle.graal.phases.common.inlining.info.elem.InlineableGraph; +import com.oracle.graal.phases.common.inlining.info.elem.InlineableMacroNode; +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.*; + +/** + * 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); + } + + private String checkTargetConditionsHelper(ResolvedJavaMethod method) { + if (method == null) { + return "the method is not resolved"; + } else if (method.isNative() && (!Intrinsify.getValue() || !InliningUtil.canIntrinsify(context.getReplacements(), method))) { + return "it is a non-intrinsic native method"; + } else if (method.isAbstract()) { + return "it is an abstract method"; + } else if (!method.getDeclaringClass().isInitialized()) { + return "the method's class is not initialized"; + } else if (!method.canBeInlined()) { + return "it is marked non-inlinable"; + } else if (countRecursiveInlining(method) > MaximumRecursiveInlining.getValue()) { + return "it exceeds the maximum recursive inlining depth"; + } else if (new OptimisticOptimizations(method.getProfilingInfo()).lessOptimisticThan(context.getOptimisticOptimizations())) { + return "the callee uses less optimistic optimizations than caller"; + } else { + return null; + } + } + + private boolean checkTargetConditions(Invoke invoke, ResolvedJavaMethod method) { + final String failureMessage = checkTargetConditionsHelper(method); + if (failureMessage == null) { + return true; + } else { + InliningUtil.logNotInlined(invoke, inliningDepth(), method, failureMessage); + return false; + } + } + + /** + * 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 (!checkTargetConditions(invoke, concrete)) { + 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 (!checkTargetConditions(invoke, concrete)) { + 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 (!checkTargetConditions(invoke, concrete)) { + return null; + } + return new AssumptionInlineInfo(invoke, concrete, takenAssumption); + } + + private InlineInfo getExactInlineInfo(Invoke invoke, ResolvedJavaMethod targetMethod) { + assert !targetMethod.isAbstract(); + if (!checkTargetConditions(invoke, targetMethod)) { + return null; + } + return new ExactInlineInfo(invoke, targetMethod); + } + + private void doInline(CallsiteHolder callerCallsiteHolder, MethodInvocation calleeInvocation, Assumptions callerAssumptions) { + StructuredGraph callerGraph = callerCallsiteHolder.graph(); + Graph.Mark markBeforeInlining = callerGraph.getMark(); + InlineInfo calleeInfo = calleeInvocation.callee(); + try { + try (Debug.Scope scope = Debug.scope("doInline", callerGraph)) { + List invokeUsages = calleeInfo.invoke().asNode().usages().snapshot(); + calleeInfo.inline(new Providers(context), callerAssumptions); + callerAssumptions.record(calleeInvocation.assumptions()); + metricInliningRuns.increment(); + Debug.dump(callerGraph, "after %s", calleeInfo); + + 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(calleeInfo.toString()); + } catch (GraalInternalError e) { + throw e.addContext(calleeInfo.toString()); + } + } + + /** + * @return true iff inlining was actually performed + */ + private boolean tryToInline(CallsiteHolder callerCallsiteHolder, MethodInvocation calleeInvocation, MethodInvocation parentInvocation, int inliningDepth) { + InlineInfo calleeInfo = calleeInvocation.callee(); + Assumptions callerAssumptions = parentInvocation.assumptions(); + metricInliningConsidered.increment(); + + if (inliningPolicy.isWorthInlining(probabilities, context.getReplacements(), calleeInfo, inliningDepth, calleeInvocation.probability(), calleeInvocation.relevance(), true)) { + doInline(callerCallsiteHolder, calleeInvocation, callerAssumptions); + return true; + } + + if (context.getOptimisticOptimizations().devirtualizeInvokes()) { + calleeInfo.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(); + Assumptions calleeAssumptions = new Assumptions(parentAssumptions.useOptimisticAssumptions()); + InlineInfo info = populateInlineInfo(invoke, parentAssumptions, calleeAssumptions); + + if (info != null) { + double invokeProbability = callsiteHolder.invokeProbability(invoke); + double invokeRelevance = callsiteHolder.invokeRelevance(invoke); + MethodInvocation methodInvocation = new MethodInvocation(info, calleeAssumptions, invokeProbability, invokeRelevance); + pushInvocation(methodInvocation); + + for (int i = 0; i < info.numberOfMethods(); i++) { + Inlineable elem = info.inlineableElementAt(i); + if (elem instanceof InlineableGraph) { + pushGraph(((InlineableGraph) elem).getGraph(), invokeProbability * info.probabilityAt(i), invokeRelevance * info.relevanceAt(i)); + } else { + assert elem instanceof InlineableMacroNode; + pushDummyGraph(); + } + } + } + } + + private InlineInfo populateInlineInfo(Invoke invoke, Assumptions parentAssumptions, Assumptions calleeAssumptions) { + InlineInfo info = getInlineInfo(invoke, parentAssumptions); + if (info == null) { + return null; + } + for (int i = 0; i < info.numberOfMethods(); i++) { + Inlineable elem = Inlineable.getInlineableElement(info.methodAt(i), info.invoke(), context.replaceAssumptions(calleeAssumptions), canonicalizer); + info.setInlinableElement(i, elem); + } + return info; + } + + 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 void pushInvocation(MethodInvocation methodInvocation) { + invocationQueue.addFirst(methodInvocation); + maxGraphs += methodInvocation.callee().numberOfMethods(); + assert graphQueue.size() <= maxGraphs; + } + + 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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/BasePhase.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/SinglePassNodeIterator.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java --- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java Wed May 21 11:45:50 2014 +0200 @@ -235,18 +235,17 @@ if (!foundExcludeNode && node == excludeNode) { foundExcludeNode = true; } - if (node == startNode) { - continue; + if (node != startNode) { + if (node instanceof MemoryCheckpoint.Single) { + LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity(); + accm.add(identity); + } else if (node instanceof MemoryCheckpoint.Multi) { + for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) { + accm.add(identity); + } + } + assert MemoryCheckpoint.TypeAssertion.correctType(node); } - if (node instanceof MemoryCheckpoint.Single) { - LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity(); - accm.add(identity); - } else if (node instanceof MemoryCheckpoint.Multi) { - for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) { - accm.add(identity); - } - } - assert MemoryCheckpoint.TypeAssertion.correctType(node); if (foundExcludeNode) { accm = set; diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/GraphOrder.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ArraysSubstitutionsTest.java Wed May 21 11:45:50 2014 +0200 @@ -79,9 +79,9 @@ Object arg1 = args1[i]; Object arg2 = args2[i]; // Verify that the original method and the substitution produce the same value - assertEquals(invokeSafe(testMethod, null, arg1, arg2), invokeSafe(realMethod, null, arg1, arg2)); + assertDeepEquals(invokeSafe(testMethod, null, arg1, arg2), invokeSafe(realMethod, null, arg1, arg2)); // Verify that the generated code and the original produce the same value - assertEquals(executeVarargsSafe(code, arg1, arg2), invokeSafe(realMethod, null, arg1, arg2)); + assertDeepEquals(executeVarargsSafe(code, arg1, arg2), invokeSafe(realMethod, null, arg1, arg2)); } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/NewArrayTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/NewArrayTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/NewArrayTest.java Wed May 21 11:45:50 2014 +0200 @@ -32,10 +32,10 @@ public class NewArrayTest extends GraalCompilerTest { @Override - protected void assertEquals(Object expected, Object actual) { + protected void assertDeepEquals(Object expected, Object actual) { Assert.assertTrue(expected != null); Assert.assertTrue(actual != null); - super.assertEquals(expected.getClass(), actual.getClass()); + super.assertDeepEquals(expected.getClass(), actual.getClass()); if (expected instanceof int[]) { Assert.assertArrayEquals((int[]) expected, (int[]) actual); } else if (expected instanceof byte[]) { diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/NewInstanceTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/NewInstanceTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/NewInstanceTest.java Wed May 21 11:45:50 2014 +0200 @@ -34,10 +34,10 @@ public class NewInstanceTest extends GraalCompilerTest { @Override - protected void assertEquals(Object expected, Object actual) { + protected void assertDeepEquals(Object expected, Object actual) { Assert.assertTrue(expected != null); Assert.assertTrue(actual != null); - super.assertEquals(expected.getClass(), actual.getClass()); + super.assertDeepEquals(expected.getClass(), actual.getClass()); if (expected instanceof Object[]) { Assert.assertTrue(actual instanceof Object[]); @@ -45,12 +45,12 @@ Object[] aArr = (Object[]) actual; Assert.assertTrue(eArr.length == aArr.length); for (int i = 0; i < eArr.length; i++) { - assertEquals(eArr[i], aArr[i]); + assertDeepEquals(eArr[i], aArr[i]); } } else if (expected.getClass() != Object.class) { try { expected.getClass().getDeclaredMethod("equals", Object.class); - super.assertEquals(expected, actual); + super.assertDeepEquals(expected, actual); } catch (Exception e) { } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StandardMethodSubstitutionsTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StandardMethodSubstitutionsTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StandardMethodSubstitutionsTest.java Wed May 21 11:45:50 2014 +0200 @@ -44,12 +44,12 @@ test("math"); double value = 34567.891D; - assertEquals(Math.sqrt(value), MathSubstitutionsX86.sqrt(value)); - assertEquals(Math.log(value), MathSubstitutionsX86.log(value)); - assertEquals(Math.log10(value), MathSubstitutionsX86.log10(value)); - assertEquals(Math.sin(value), MathSubstitutionsX86.sin(value)); - assertEquals(Math.cos(value), MathSubstitutionsX86.cos(value)); - assertEquals(Math.tan(value), MathSubstitutionsX86.tan(value)); + assertDeepEquals(Math.sqrt(value), MathSubstitutionsX86.sqrt(value)); + assertDeepEquals(Math.log(value), MathSubstitutionsX86.log(value)); + assertDeepEquals(Math.log10(value), MathSubstitutionsX86.log10(value)); + assertDeepEquals(Math.sin(value), MathSubstitutionsX86.sin(value)); + assertDeepEquals(Math.cos(value), MathSubstitutionsX86.cos(value)); + assertDeepEquals(Math.tan(value), MathSubstitutionsX86.tan(value)); } @SuppressWarnings("all") @@ -98,9 +98,9 @@ assert optional || code != null; for (Object l : args) { // Verify that the original method and the substitution produce the same value - assertEquals(invokeSafe(testMethod, l), invokeSafe(realMethod, l)); + assertDeepEquals(invokeSafe(testMethod, l), invokeSafe(realMethod, l)); // Verify that the generated code and the original produce the same value - assertEquals(executeVarargsSafe(code, l), invokeSafe(realMethod, l)); + assertDeepEquals(executeVarargsSafe(code, l), invokeSafe(realMethod, l)); } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StringSubstitutionsTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StringSubstitutionsTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/StringSubstitutionsTest.java Wed May 21 11:45:50 2014 +0200 @@ -73,9 +73,9 @@ Object arg1 = args1[i]; Object arg2 = args2[i]; // Verify that the original method and the substitution produce the same value - assertEquals(invokeSafe(testMethod, null, arg1, arg2), invokeSafe(realMethod, arg1, arg2)); + assertDeepEquals(invokeSafe(testMethod, null, arg1, arg2), invokeSafe(realMethod, arg1, arg2)); // Verify that the generated code and the original produce the same value - assertEquals(executeVarargsSafe(code, arg1, arg2), invokeSafe(realMethod, arg1, arg2)); + assertDeepEquals(executeVarargsSafe(code, arg1, arg2), invokeSafe(realMethod, arg1, arg2)); } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/UnsafeSubstitutionsTest.java --- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/UnsafeSubstitutionsTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/UnsafeSubstitutionsTest.java Wed May 21 11:45:50 2014 +0200 @@ -69,14 +69,14 @@ { Object expected = invokeSafe(originalMethod, receiver, args1); Object actual = invokeSafe(testMethod, null, args2); - assertEquals(expected, actual); + assertDeepEquals(expected, actual); } // Verify that the generated code and the original produce the same value { Object expected = invokeSafe(originalMethod, receiver, args1); Object actual = executeVarargsSafe(code, args2); - assertEquals(expected, actual); + assertDeepEquals(expected, actual); } } @@ -201,18 +201,18 @@ AtomicInteger a1 = new AtomicInteger(42); AtomicInteger a2 = new AtomicInteger(42); - assertEquals(unsafe.compareAndSwapInt(a1, off(a1, "value"), 42, 53), compareAndSwapInt(unsafe, a2, off(a2, "value"), 42, 53)); - assertEquals(a1.get(), a2.get()); + assertDeepEquals(unsafe.compareAndSwapInt(a1, off(a1, "value"), 42, 53), compareAndSwapInt(unsafe, a2, off(a2, "value"), 42, 53)); + assertDeepEquals(a1.get(), a2.get()); AtomicLong l1 = new AtomicLong(42); AtomicLong l2 = new AtomicLong(42); - assertEquals(unsafe.compareAndSwapLong(l1, off(l1, "value"), 42, 53), compareAndSwapLong(unsafe, l2, off(l2, "value"), 42, 53)); - assertEquals(l1.get(), l2.get()); + assertDeepEquals(unsafe.compareAndSwapLong(l1, off(l1, "value"), 42, 53), compareAndSwapLong(unsafe, l2, off(l2, "value"), 42, 53)); + assertDeepEquals(l1.get(), l2.get()); AtomicReference o1 = new AtomicReference<>("42"); AtomicReference o2 = new AtomicReference<>("42"); - assertEquals(unsafe.compareAndSwapObject(o1, off(o1, "value"), "42", "53"), compareAndSwapObject(unsafe, o2, off(o2, "value"), "42", "53")); - assertEquals(o1.get(), o2.get()); + assertDeepEquals(unsafe.compareAndSwapObject(o1, off(o1, "value"), "42", "53"), compareAndSwapObject(unsafe, o2, off(o2, "value"), "42", "53")); + assertDeepEquals(o1.get(), o2.get()); Foo f1 = new Foo(); f1.test("z", "Boolean", Boolean.TRUE); diff -r a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalJUnitCore.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalTest.java Wed May 21 11:45:50 2014 +0200 @@ -76,37 +76,83 @@ * Compares two given objects for {@linkplain Assert#assertEquals(Object, Object) equality}. * Does a deep copy equality comparison if {@code expected} is an array. */ - protected void assertEquals(Object expected, Object actual) { - if (expected != null && expected.getClass().isArray()) { - Assert.assertTrue(expected != null); - Assert.assertTrue(actual != null); - Assert.assertEquals(expected.getClass(), actual.getClass()); - if (expected instanceof int[]) { - Assert.assertArrayEquals((int[]) expected, (int[]) actual); - } else if (expected instanceof byte[]) { - Assert.assertArrayEquals((byte[]) expected, (byte[]) actual); - } else if (expected instanceof char[]) { - Assert.assertArrayEquals((char[]) expected, (char[]) actual); - } else if (expected instanceof short[]) { - Assert.assertArrayEquals((short[]) expected, (short[]) actual); - } else if (expected instanceof float[]) { - Assert.assertArrayEquals((float[]) expected, (float[]) actual, 0.0f); - } else if (expected instanceof long[]) { - Assert.assertArrayEquals((long[]) expected, (long[]) actual); - } else if (expected instanceof double[]) { - Assert.assertArrayEquals((double[]) expected, (double[]) actual, 0.0d); - } else if (expected instanceof boolean[]) { - new ExactComparisonCriteria().arrayEquals(null, expected, actual); - } else if (expected instanceof Object[]) { - Assert.assertArrayEquals((Object[]) expected, (Object[]) actual); + protected void assertDeepEquals(Object expected, Object actual) { + assertDeepEquals(null, expected, actual); + } + + /** + * Compares two given objects for {@linkplain Assert#assertEquals(Object, Object) equality}. + * Does a deep copy equality comparison if {@code expected} is an array. + * + * @param message the identifying message for the {@link AssertionError} + */ + protected void assertDeepEquals(String message, Object expected, Object actual) { + assertDeepEquals(message, expected, actual, equalFloatsOrDoublesDelta()); + } + + /** + * Compares two given values for equality, doing a recursive test if both values are arrays of + * the same type. + * + * @param message the identifying message for the {@link AssertionError} + * @param delta the maximum delta between two doubles or floats for which both numbers are still + * considered equal. + */ + protected void assertDeepEquals(String message, Object expected, Object actual, double delta) { + if (expected != null && actual != null) { + Class expectedClass = expected.getClass(); + Class actualClass = actual.getClass(); + if (expectedClass.isArray()) { + Assert.assertTrue(message, expected != null); + Assert.assertTrue(message, actual != null); + Assert.assertEquals(message, expectedClass, actual.getClass()); + if (expected instanceof int[]) { + Assert.assertArrayEquals(message, (int[]) expected, (int[]) actual); + } else if (expected instanceof byte[]) { + Assert.assertArrayEquals(message, (byte[]) expected, (byte[]) actual); + } else if (expected instanceof char[]) { + Assert.assertArrayEquals(message, (char[]) expected, (char[]) actual); + } else if (expected instanceof short[]) { + Assert.assertArrayEquals(message, (short[]) expected, (short[]) actual); + } else if (expected instanceof float[]) { + Assert.assertArrayEquals(message, (float[]) expected, (float[]) actual, (float) delta); + } else if (expected instanceof long[]) { + Assert.assertArrayEquals(message, (long[]) expected, (long[]) actual); + } else if (expected instanceof double[]) { + Assert.assertArrayEquals(message, (double[]) expected, (double[]) actual, delta); + } else if (expected instanceof boolean[]) { + new ExactComparisonCriteria().arrayEquals(message, expected, actual); + } else if (expected instanceof Object[]) { + new ComparisonCriteria() { + @Override + protected void assertElementsEqual(Object e, Object a) { + assertDeepEquals(message, e, a, delta); + } + }.arrayEquals(message, expected, actual); + } else { + Assert.fail((message == null ? "" : message) + "non-array value encountered: " + expected); + } + } else if (expectedClass.equals(double.class) && actualClass.equals(double.class)) { + 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.fail("non-array value encountered: " + expected); + Assert.assertEquals(message, expected, actual); } } else { - Assert.assertEquals(expected, actual); + Assert.assertEquals(message, expected, actual); } } + /** + * Gets the value used by {@link #assertDeepEquals(Object, Object)} and + * {@link #assertDeepEquals(String, Object, Object)} for the maximum delta between two doubles + * or floats for which both numbers are still considered equal. + */ + protected double equalFloatsOrDoublesDelta() { + return 0.0D; + } + @SuppressWarnings("serial") public static class MultiCauseAssertionError extends AssertionError { diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.test/src/com/oracle/graal/test/GraalVerboseTextListener.java Wed May 21 11:45:50 2014 +0200 @@ -65,8 +65,7 @@ @Override public void testFailed(Failure failure) { - getWriter().println("FAILED"); - failure.getException().printStackTrace(getWriter()); + getWriter().print("FAILED"); } @Override diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/AssumptionPartialEvaluationTest.java --- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/AssumptionPartialEvaluationTest.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/AssumptionPartialEvaluationTest.java Wed May 21 11:45:50 2014 +0200 @@ -46,7 +46,7 @@ InstalledCode installedCode = assertPartialEvalEquals("constant42", rootNode); Assert.assertTrue(installedCode.isValid()); try { - assertEquals(42, installedCode.executeVarargs(null, null, null)); + assertDeepEquals(42, installedCode.executeVarargs(null, null, null)); } catch (InvalidInstalledCodeException e) { Assert.fail("Code must not have been invalidated."); } diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ExactMathTest.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/ExactMathSubstitutions.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/VirtualObjectState.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeRewriterPhase.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.graal.word/src/com/oracle/graal/word/phases/WordTypeVerificationPhase.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExactMath.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/ExecutionContext.java Wed May 21 11:45:50 2014 +0200 @@ -27,31 +27,131 @@ import java.util.*; import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.impl.*; import com.oracle.truffle.api.source.*; /** * Access to information and basic services in the runtime context for a Truffle-implemented guest * language. *

- * Disclaimer: this interface is under development and will change. + * Disclaimer: this class is under development and will change. */ -public interface ExecutionContext { +public abstract class ExecutionContext { + + private final ProbeManager probeManager = new ProbeManager(); + private final SourceManager sourceManager = new SourceManager(); + private final List sourceListeners = new ArrayList<>(); + private Visualizer visualizer = new DefaultVisualizer(); + + protected ExecutionContext() { + } + + public void initialize() { + setSourceCallback(new SourceCallback() { + + public void startLoading(Source source) { + for (SourceListener listener : sourceListeners) { + listener.loadStarting(source); + } + } + + public void endLoading(Source source) { + for (SourceListener listener : sourceListeners) { + listener.loadEnding(source); + } + } + }); + } + + /** + * Gets access to source management services. + */ + public final SourceManager getSourceManager() { + return sourceManager; + } + + /** + * Registers a tool interested in being notified about the loading of {@link Source}s. + */ + public final void addSourceListener(SourceListener listener) { + assert listener != null; + sourceListeners.add(listener); + } + + /** + * Registers a tool interested in being notified about the insertion of a newly created + * {@link Probe} into a Truffle AST. + */ + public final void addProbeListener(ProbeListener listener) { + probeManager.addProbeListener(listener); + } + + /** + * Return the (possibly newly created) {@link Probe} uniquely associated with a particular + * source code location. A newly created probe carries no tags. + * + * @return a probe uniquely associated with an extent of guest language source code. + */ + public final Probe getProbe(SourceSection sourceSection) { + return probeManager.getProbe(sourceSection); + } + + /** + * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty + * collection if no probes found. + */ + public final Collection findProbesTaggedAs(PhylumTag tag) { + return probeManager.findProbesTaggedAs(tag); + } + + /** + * Returns all existing probes with first character on a specified line; empty collection if no + * probes found. + */ + public final Collection findProbesByLine(SourceLineLocation lineLocation) { + return probeManager.findProbesByLine(lineLocation); + } + + /** + * Sets a trap that will make a callback at any AST location where a existing probe holds a + * specified tag; only one trap may be set at a time. + * + * @throws IllegalStateException if a trap is already set + */ + public final void setPhylumTrap(PhylumTrap trap) throws IllegalStateException { + // TODO (mlvdv) consider allowing multiple traps (without inhibiting Truffle inlining) + probeManager.setPhylumTrap(trap); + } + + /** + * Clears a trap that will halt execution; only one trap may be set at a time. + * + * @throws IllegalStateException if no trap is set. + */ + public final void clearPhylumTrap() { + probeManager.clearPhylumTrap(); + } + + /** + * Access to information visualization services for the specific language. + */ + public final Visualizer getVisualizer() { + return visualizer; + } + + /** + * Assign guest language-specific visualization support for tools. This must be assigned outside + * the implementation context to avoid build circularities. + */ + public final void setVisualizer(Visualizer visualizer) { + this.visualizer = visualizer; + } /** * Gets the name of the language, possibly with version number. in short enough form that it * might be used for an interactive prompt. */ - String getLanguageShortName(); - - /** - * Gets access to source management services. - */ - SourceManager getSourceManager(); - - /** - * Registers a tool interested in being notified of events related to the loading of sources. - */ - void addSourceListener(SourceListener listener); + public abstract String getLanguageShortName(); /** * Add instrumentation to subsequently constructed Truffle ASTs for the guest language; every @@ -60,51 +160,19 @@ * @throws IllegalStateException if AST instrumentation not enabled * @throws IllegalArgumentException if prober not usable for the guest language implementation. */ - void addNodeProber(ASTNodeProber nodeProber) throws IllegalStateException, IllegalArgumentException; - - /** - * Registers a tool interested in being notified about the insertion of a newly created - * {@link Probe} into a Truffle AST. - */ - void addProbeListener(ProbeListener listener); - - /** - * Return the (possibly newly created) {@link Probe} uniquely associated with a particular - * source code location. A newly created probe carries no tags. - * - * @return a probe uniquely associated with an extent of guest language source code. - */ - Probe getProbe(SourceSection sourceSection); - - /** - * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty - * collection if no probes found. - */ - Collection findProbesTaggedAs(PhylumTag tag); + public abstract void addNodeProber(ASTNodeProber nodeProber) throws IllegalStateException, IllegalArgumentException; /** - * Returns all existing probes with first character on a specified line; empty collection if no - * probes found. + * Assigns a guest language-specific manager for using {@link ASTNodeProber}s added by tools to + * instrument ASTs with {@link Probe}s at specified nodes. This must be assigned outside the + * implementation context to avoid build circularities. It must also be set before any + * instrumentation probe implementations are assigned. */ - Collection findProbesByLine(SourceLineLocation lineLocation); + public abstract void setASTProber(ASTProber astProber); /** - * Sets a trap that will make a callback at any AST location where a existing probe holds a - * specified tag; only one trap may be set at a time. - * - * @throws IllegalStateException if a trap is already set + * Establishes source event reporting */ - void setTrap(PhylumTrap trap) throws IllegalStateException; + protected abstract void setSourceCallback(SourceCallback sourceCallback); - /** - * Clears a trap that will halt execution; only one trap may be set at a time. - * - * @throws IllegalStateException if no trap is set. - */ - void clearTrap() throws IllegalStateException; - - /** - * Access to information visualization services for the specific language. - */ - Visualizer getVisualizer(); } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java Wed May 21 11:45:50 2014 +0200 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * 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 @@ -26,6 +26,8 @@ import java.io.*; +import com.oracle.truffle.api.source.*; + /** * Represents a unit (typically a file) of guest language source code. */ @@ -97,4 +99,61 @@ */ int getLineLength(int lineNumber); + /** + * Creates a representation of a contiguous region of text in the source. Computes the + * {@code (startLine, startColumn)} values by building a {@linkplain TextMap map} of lines in + * the source. + *

+ * Checks the position arguments for consistency with the source. + *

+ * The resulting representation defines hash/equality around equivalent location, presuming that + * {@link Source} representations are cannonical. + * + * + * @param identifier terse description of the region + * @param charIndex 0-based position of the first character in the section + * @param length the number of characters in the section + * @return newly created object representing the specified region + * @throws IllegalArgumentException if either of the arguments are outside the text of the + * source + * @throws IllegalStateException if the source is one of the "null" instances + */ + SourceSection createSection(String identifier, int charIndex, int length) throws IllegalArgumentException, IllegalStateException; + + /** + * Creates a representation of a contiguous region of text in the source. Computes the + * {@code charIndex} value by building a {@linkplain TextMap map} of lines in the source. + *

+ * Checks the position arguments for consistency with the source. + *

+ * The resulting representation defines hash/equality around equivalent location, presuming that + * {@link Source} representations are cannonical. + * + * @param identifier terse description of the region + * @param startLine 1-based line number of the first character in the section + * @param startColumn 1-based column number of the first character in the section + * @param length the number of characters in the section + * @return newly created object representing the specified region + * @throws IllegalArgumentException if arguments are outside the text of the source + * @throws IllegalStateException if the source is one of the "null" instances + */ + SourceSection createSection(String identifier, int startLine, int startColumn, int length); + + /** + * Creates a representation of a contiguous region of text in the source. + *

+ * This method performs no checks on the validity of the arguments. + *

+ * The resulting representation defines hash/equality around equivalent location, presuming that + * {@link Source} representations are cannonical. + * + * @param identifier terse description of the region + * @param startLine 1-based line number of the first character in the section + * @param startColumn 1-based column number of the first character in the section + * @param charIndex the 0-based index of the first character of the section + * @param length the number of characters in the section + * @return newly created object representing the specified region + */ + SourceSection createSection(String identifier, int startLine, int startColumn, int charIndex, int length); + } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/SourceSection.java Wed May 21 11:45:50 2014 +0200 @@ -25,46 +25,62 @@ package com.oracle.truffle.api; /** - * Description of contiguous text section within the source code of a guest language program. + * Description of contiguous section of text within a {@link Source} of program code. + * + * The starting location of the section can be described using two different coordinates: + *

    + *
  • {@code (startLine, startColumn)}: rows and columns are 1-based, so the first character in a + * source file is at position {@code (1,1)}. {@code Tab} characters are counted as occupying one + * column.
  • + *
  • character index: 0-based offset of the character from the beginning of the source, so + * the first character in a file is at index {@code 0}.
  • + *
+ * The {@code Newline} that terminates each line counts as a single character for the purpose of a + * character index and when counting the length of text. The {@code (line,column)} coordinates of a + * {@code Newline} should never appear in a text section. + *

+ * If the final character of source is not a {@code Newline}, the final characters of the text are + * still considered to be a line ("unterminated"). + *

+ * + * @see Source#createSection(String, int, int, int, int) + * @see Source#createSection(String, int, int, int) + * @see Source#createSection(String, int, int) */ public interface SourceSection { + // TODO support alternate text representations/encodings + /** - * Returns the object representing the source program that contains this section. + * Representation of the source program that contains this section. * * @return the source object */ Source getSource(); /** - * Returns 1-based line number of the first character in this source section (inclusive). + * Returns 1-based line number of the first character in this section (inclusive). * * @return the starting line number */ int getStartLine(); /** - * Returns the 1-based column number of the first character in this source section (inclusive). + * Returns the 1-based column number of the first character in this section (inclusive). * * @return the starting column number */ int getStartColumn(); /** - * Returns the 0-based index of the first character in this source section. - *

- * The complete text of the source that contains this section can be retrieved via - * {@link Source#getCode()}. + * Returns the 0-based index of the first character in this section. * * @return the starting character index */ int getCharIndex(); /** - * Returns the length of this source section in characters. - *

- * The complete text of the source that contains this section can be retrieved via - * {@link Source#getCode()}. + * Returns the length of this section in characters. * * @return the number of characters in the section */ @@ -79,14 +95,14 @@ int getCharEndIndex(); /** - * Returns the identifier of this source section that is used for printing the section. + * Returns terse text describing this source section, typically used for printing the section. * * @return the identifier of the section */ String getIdentifier(); /** - * Returns text of the code represented by this source section. + * Returns text described by this section. * * @return the code as a String object */ diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleRuntime.java Wed May 21 11:45:50 2014 +0200 @@ -106,7 +106,8 @@ /** * Accesses the current stack, i.e., the contents of the {@link Frame}s and the associated - * {@link CallTarget}s. + * {@link CallTarget}s. Iteration starts at the caller frame, i.e., it does not include the + * current frame. * * @return a lazy collection of {@link FrameInstance}. */ diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/AbstractExecutionContext.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/AbstractExecutionContext.java Tue May 13 19:19:27 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,217 +0,0 @@ -/* - * 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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.truffle.api.impl; - -import java.util.*; - -import com.oracle.truffle.api.*; -import com.oracle.truffle.api.instrument.*; -import com.oracle.truffle.api.instrument.impl.*; -import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeCallback; -import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeImpl; -import com.oracle.truffle.api.source.*; - -public abstract class AbstractExecutionContext implements ExecutionContext { - - // TODO (mlvdv) use weak references. - /** - * Map: SourceSection ==> probe associated with that source section in an AST. - */ - private final Map srcToProbe = new HashMap<>(); - - // TODO (mlvdv) use weak references. - /** - * Map: Source line ==> probes associated with source sections starting on the line. - */ - private final Map> lineToProbes = new HashMap<>(); - - private final SourceManager sourceManager = new SourceManager(); - private final List sourceListeners = new ArrayList<>(); - private final List probeListeners = new ArrayList<>(); - - private final SourceCallback sourceCallback = new SourceCallback() { - - public void startLoading(Source source) { - for (SourceListener listener : sourceListeners) { - listener.loadStarting(source); - } - } - - public void endLoading(Source source) { - for (SourceListener listener : sourceListeners) { - listener.loadEnding(source); - } - } - }; - - private final ProbeCallback probeCallback = new ProbeCallback() { - /** - * Receives (from the {@link Probe} implementation) and distributes notification that a - * {@link Probe} has acquired a new {@linkplain PhylumTag tag}. - */ - public void newTagAdded(ProbeImpl probe, PhylumTag tag) { - for (ProbeListener listener : probeListeners) { - listener.probeTaggedAs(probe, tag); - } - if (trap != null && tag == trap.getTag()) { - probe.setTrap(trap); - } - } - }; - - private Visualizer visualizer = new DefaultVisualizer(); - - /** - * When non-null, "enter" events with matching tags will trigger a callback. - */ - private PhylumTrap trap = null; - - protected AbstractExecutionContext() { - } - - public void initialize() { - setSourceCallback(sourceCallback); - } - - public final SourceManager getSourceManager() { - return sourceManager; - } - - public void addSourceListener(SourceListener listener) { - assert listener != null; - sourceListeners.add(listener); - } - - public void addProbeListener(ProbeListener listener) { - assert listener != null; - probeListeners.add(listener); - } - - public Probe getProbe(SourceSection sourceSection) { - assert sourceSection != null; - - ProbeImpl probe = srcToProbe.get(sourceSection); - - if (probe != null) { - return probe; - } - probe = InstrumentationNode.createProbe(sourceSection, probeCallback); - - // Register new probe by unique SourceSection - srcToProbe.put(sourceSection, probe); - - // Register new probe by source line, there may be more than one - // Create line location for map key - final SourceLineLocation lineLocation = new SourceLineLocation(sourceSection.getSource(), sourceSection.getStartLine()); - - Collection probes = lineToProbes.get(lineLocation); - if (probes == null) { - probes = new ArrayList<>(2); - lineToProbes.put(lineLocation, probes); - } - probes.add(probe); - - for (ProbeListener listener : probeListeners) { - listener.newProbeInserted(sourceSection, probe); - } - - return probe; - } - - /** - * Returns all existing probes with specific tag, or all probes if {@code tag = null}; empty - * collection if no probes found. - */ - public Collection findProbesTaggedAs(PhylumTag tag) { - final List probes = new ArrayList<>(); - for (Probe probe : srcToProbe.values()) { - if (tag == null || probe.isTaggedAs(tag)) { - probes.add(probe); - } - } - return probes; - } - - public Collection findProbesByLine(SourceLineLocation lineLocation) { - final Collection probes = lineToProbes.get(lineLocation); - if (probes == null) { - return Collections.emptyList(); - } - return new ArrayList<>(probes); - } - - // TODO (mlvdv) consider allowing multiple traps (without inhibiting Truffle inlining) - public void setTrap(PhylumTrap trap) { - assert trap != null; - if (this.trap != null) { - throw new IllegalStateException("trap already set"); - } - this.trap = trap; - - for (ProbeImpl probe : srcToProbe.values()) { - if (probe.isTaggedAs(trap.getTag())) { - probe.setTrap(trap); - } - } - } - - public void clearTrap() { - if (this.trap == null) { - throw new IllegalStateException("no trap set"); - } - for (ProbeImpl probe : srcToProbe.values()) { - if (probe.isTaggedAs(trap.getTag())) { - probe.setTrap(null); - } - } - trap = null; - } - - public Visualizer getVisualizer() { - return visualizer; - } - - /** - * Assign guest language-specific visualization support for tools. This must be assigned outside - * the implementation context to avoid build circularities. - */ - public void setVisualizer(Visualizer visualizer) { - this.visualizer = visualizer; - } - - /** - * Assigns a guest language-specific manager for using {@link ASTNodeProber}s added by tools to - * instrument ASTs with {@link Probe}s at specified nodes. This must be assigned outside the - * implementation context to avoid build circularities. It must also be set before any - * instrumentation probe implementations are assigned. - */ - public abstract void setASTProber(ASTProber astProber); - - /** - * Establishes source event reporting - */ - protected abstract void setSourceCallback(SourceCallback sourceCallback); - -} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultSourceSection.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultSourceSection.java Tue May 13 19:19:27 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,168 +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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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.truffle.api.impl; - -import com.oracle.truffle.api.*; - -/** - * Represents a contiguous text section within the source code of a guest language program. - */ -public class DefaultSourceSection implements SourceSection { - - private final Source source; - private final String identifier; - private final int startLine; - private final int startColumn; - private final int charIndex; - private final int charLength; - - /** - * Creates a new object representing a contiguous text section within the source code of a guest - * language program's text. - *

- * The starting location of the section is specified using two different coordinate: - *

    - *
  • (row, column): rows and columns are 1-based, so the first character in a source - * file is at position {@code (1,1)}.
  • - *
  • character index: 0-based offset of the character from the beginning of the source, - * so the first character in a file is at index {@code 0}.
  • - *
- * The newline that terminates each line counts as a single character for the purpose of - * a character index. The (row,column) coordinates of a newline character should never appear in - * a text section. - *

- * - * @param source object representing the complete source program that contains this section - * @param identifier an identifier used when printing the section - * @param startLine the 1-based number of the start line of the section - * @param startColumn the 1-based number of the start column of the section - * @param charIndex the 0-based index of the first character of the section - * @param charLength the length of the section in number of characters - */ - public DefaultSourceSection(Source source, String identifier, int startLine, int startColumn, int charIndex, int charLength) { - this.source = source; - this.identifier = identifier; - this.startLine = startLine; - this.startColumn = startColumn; - this.charIndex = charIndex; - this.charLength = charLength; - } - - public final Source getSource() { - return source; - } - - public final int getStartLine() { - return startLine; - } - - public final int getStartColumn() { - return startColumn; - } - - public final int getCharIndex() { - return charIndex; - } - - public final int getCharLength() { - return charLength; - } - - public final int getCharEndIndex() { - return charIndex + charLength; - } - - public final String getIdentifier() { - return identifier; - } - - public final String getCode() { - return getSource().getCode().substring(charIndex, charIndex + charLength); - } - - public final String getShortDescription() { - return String.format("%s:%d", source.getShortName(), startLine); - } - - @Override - public String toString() { - return String.format("%s:%d", source.getName(), startLine); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + charIndex; - result = prime * result + charLength; - result = prime * result + ((identifier == null) ? 0 : identifier.hashCode()); - result = prime * result + ((source == null) ? 0 : source.hashCode()); - result = prime * result + startColumn; - result = prime * result + startLine; - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (!(obj instanceof DefaultSourceSection)) { - return false; - } - DefaultSourceSection other = (DefaultSourceSection) obj; - if (charIndex != other.charIndex) { - return false; - } - if (charLength != other.charLength) { - return false; - } - if (identifier == null) { - if (other.identifier != null) { - return false; - } - } else if (!identifier.equals(other.identifier)) { - return false; - } - if (source == null) { - if (other.source != null) { - return false; - } - } else if (!source.equals(other.source)) { - return false; - } - if (startColumn != other.startColumn) { - return false; - } - if (startLine != other.startLine) { - return false; - } - return true; - } - -} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java Wed May 21 11:45:50 2014 +0200 @@ -31,29 +31,29 @@ import com.oracle.truffle.api.nodes.*; /** - * A receiver of Truffle AST {@link ExecutionEvents}, propagated from a {@link Probe} to which the - * instrument is attached, for the benefit of associated tools. + * A receiver of Truffle AST runtime {@link ExecutionEvents}, propagated from the {@link Probe} to + * which the instrument is attached, for the benefit of associated tools. *

- * Guidelines for implementing Instruments, with particular attention to avoiding undesired runtime + * Guidelines for implementing {@link Instrument}s, with particular attention to minimize runtime * performance overhead: *

    - *
  1. Extend {@link Instrument} and override only the event handling methods for which some action - * is needed.
  2. + *
  3. Extend this abstract class and override only the {@linkplain ExecutionEvents event handling + * methods} for which intervention is needed.
  4. *
  5. Instruments are Truffle {@link Node}s and should be coded as much as possible in the desired * Truffle style, documented more thoroughly elsewhere.
  6. *
  7. Maintain as little state as possible.
  8. - *
  9. If state is necessary, make it {@code final} if possible.
  10. - *
  11. If non-final state is necessary, annotate it as {@link CompilationFinal} and call - * {@linkplain InstrumentationNode#notifyProbeChanged(Instrument)} whenever it is modified.
  12. + *
  13. If state is necessary, make object fields {@code final} if at all possible.
  14. + *
  15. If non-final object-valued state is necessary, annotate it as {@link CompilationFinal} and + * call {@linkplain InstrumentationNode#notifyProbeChanged(Instrument)} whenever it is modified.
  16. *
  17. Never store a {@link Frame} value in a field.
  18. *
  19. Minimize computation in standard execution paths.
  20. - *
  21. Callbacks to tools should be made via callbacks provided at construction and stored in - * {@code final} fields.
  22. - *
  23. Tool callback methods should usually be annotated as {@link SlowPath} to prevent them from - * being inlined into fast execution paths.
  24. - *
  25. If computation is needed, and if performance is important, then the computation is best - * expressed as a guest language AST and evaluated using standard Truffle mechanisms so that - * standard Truffle optimizations can be applied.
  26. + *
  27. If runtime calls must be made back to a tool, construct the instrument with a callback stored + * in a {@code final} field.
  28. + *
  29. Tool methods called by the instrument should be annotated as {@link SlowPath} to prevent them + * from being inlined into fast execution paths.
  30. + *
  31. If computation in the execution path is needed, and if performance is important, then the + * computation is best expressed as a guest language AST and evaluated using standard Truffle + * mechanisms so that standard Truffle optimizations can be applied.
  32. *
*

* Guidelines for attachment to a {@link Probe}: @@ -65,6 +65,18 @@ * every copy, and so the Instrument will receive events corresponding to the intended syntactic * unit of code, independent of which AST copy is being executed. * + *

+ * Guidelines for handling {@link ExecutionEvents}: + *

    + *
  1. Separate event methods are defined for each kind of possible return: object-valued, + * primitive-valued, void-valued, and exceptional.
  2. + *
  3. Override "leave*" primitive methods if the language implementation returns primitives and the + * instrument should avoid boxing them.
  4. + *
  5. On the other hand, if boxing all primitives for instrumentation is desired, it is only + * necessary to override the object-valued return methods, since the default implementation of each + * primitive-valued return method is to box the value and forward it to the object-valued return + * method.
  6. + *
* *

* Disclaimer: experimental; under development. @@ -72,7 +84,7 @@ * @see Probe * @see ASTNodeProber */ -public class Instrument extends InstrumentationNode { +public abstract class Instrument extends InstrumentationNode { protected Instrument() { } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Probe.java Wed May 21 11:45:50 2014 +0200 @@ -25,8 +25,6 @@ package com.oracle.truffle.api.instrument; import com.oracle.truffle.api.*; -import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.nodes.*; /** * A collector of {@link ExecutionEvents} at a specific site (node) in a Truffle AST (generated by a @@ -54,7 +52,7 @@ * @see Instrument * @see Wrapper */ -public interface Probe extends PhylumTagged { +public interface Probe extends ExecutionEvents, PhylumTagged { /** * The source location with which this probe is (presumably uniquely) associated. @@ -77,66 +75,4 @@ */ void removeInstrument(Instrument oldInstrument); - // TODO (mlvdv) migrate the remaining methods to another interface. - - /** - * @see ExecutionEvents#enter(Node, VirtualFrame) - */ - void notifyEnter(Node astNode, VirtualFrame frame); - - /** - * @see ExecutionEvents#leave(Node, VirtualFrame) - */ - void notifyLeave(Node astNode, VirtualFrame frame); - - /** - * @see ExecutionEvents#leave(Node, VirtualFrame, boolean) - */ - void notifyLeave(Node astNode, VirtualFrame frame, boolean result); - - /** - * @see ExecutionEvents#leave(Node, VirtualFrame, byte) - */ - void notifyLeave(Node astNode, VirtualFrame frame, byte result); - - /** - * @see ExecutionEvents#leave(Node, VirtualFrame, short) - */ - void notifyLeave(Node astNode, VirtualFrame frame, short result); - - /** - * @see ExecutionEvents#leave(Node, VirtualFrame, int) - */ - void notifyLeave(Node astNode, VirtualFrame frame, int result); - - /** - * @see ExecutionEvents#leave(Node, VirtualFrame, long) - */ - void notifyLeave(Node astNode, VirtualFrame frame, long result); - - /** - * @see ExecutionEvents#leave(Node, VirtualFrame, char) - */ - void notifyLeave(Node astNode, VirtualFrame frame, char result); - - /** - * @see ExecutionEvents#leave(Node, VirtualFrame, float) - */ - void notifyLeave(Node astNode, VirtualFrame frame, float result); - - /** - * @see ExecutionEvents#leave(Node, VirtualFrame, double) - */ - void notifyLeave(Node astNode, VirtualFrame frame, double result); - - /** - * @see ExecutionEvents#leave(Node, VirtualFrame, Object) - */ - void notifyLeave(Node astNode, VirtualFrame frame, Object result); - - /** - * @see ExecutionEvents#leaveExceptional(Node, VirtualFrame, Exception) - */ - void notifyLeaveExceptional(Node astNode, VirtualFrame frame, Exception e); - } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceCallback.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/SourceCallback.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,37 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.truffle.api.instrument; + +import com.oracle.truffle.api.*; + +/** + * Instrumentation callback for guest language source-related events. + */ +public interface SourceCallback { + + public void startLoading(Source source); + + public void endLoading(Source source); +} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Visualizer.java Wed May 21 11:45:50 2014 +0200 @@ -61,7 +61,7 @@ /** * Converts a value in the guest language to a display string. */ - String displayValue(Object value); + String displayValue(ExecutionContext context, Object value); /** * Converts a slot identifier in the guest language to a display string. diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultASTPrinter.java Wed May 21 11:45:50 2014 +0200 @@ -77,13 +77,14 @@ } if (node instanceof PhylumTagged) { final PhylumTagged taggedNode = (PhylumTagged) node; + p.print("["); String prefix = ""; for (PhylumTag tag : taggedNode.getPhylumTags()) { p.print(prefix); prefix = ","; p.print(tag.toString()); } - + p.print("]"); } ArrayList childFields = new ArrayList<>(); diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/DefaultVisualizer.java Wed May 21 11:45:50 2014 +0200 @@ -69,7 +69,7 @@ return callTarget.toString(); } - public String displayValue(Object value) { + public String displayValue(ExecutionContext context, Object value) { return value.toString(); } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/InstrumentationNode.java Wed May 21 11:45:50 2014 +0200 @@ -39,7 +39,7 @@ */ public abstract class InstrumentationNode extends Node implements ExecutionEvents { - public interface ProbeCallback { + interface ProbeCallback { void newTagAdded(ProbeImpl probe, PhylumTag tag); } @@ -49,7 +49,7 @@ * * @return a new probe */ - public static ProbeImpl createProbe(SourceSection sourceSection, ProbeCallback probeCallback) { + static ProbeImpl createProbe(SourceSection sourceSection, ProbeCallback probeCallback) { return new ProbeImpl(sourceSection, probeCallback); } @@ -64,7 +64,7 @@ /** * @return the instance of {@link Probe} to which this instrument is attached. */ - public Probe getProbe() { + protected Probe getProbe() { final InstrumentationNode parent = (InstrumentationNode) getParent(); return parent == null ? null : parent.getProbe(); } @@ -199,7 +199,7 @@ * May be categorized by one or more {@linkplain PhylumTag tags}, signifying information useful * for instrumentation about its AST location(s). */ - public static final class ProbeImpl extends InstrumentationNode implements Probe { + static final class ProbeImpl extends InstrumentationNode implements Probe { private final ProbeCallback probeCallback; @@ -259,7 +259,7 @@ } @Override - public Probe getProbe() { + protected Probe getProbe() { return this; } @@ -271,14 +271,14 @@ } @SlowPath - public void setTrap(PhylumTrap trap) { + void setTrap(PhylumTrap trap) { assert trap == null || isTaggedAs(trap.getTag()); probeUnchanged.invalidate(); this.trap = trap; probeUnchanged = Truffle.getRuntime().createAssumption(); } - public void notifyEnter(Node astNode, VirtualFrame frame) { + public void enter(Node astNode, VirtualFrame frame) { if (trap != null || next != null) { if (!probeUnchanged.isValid()) { CompilerDirectives.transferToInterpreter(); @@ -292,7 +292,7 @@ } } - public void notifyLeave(Node astNode, VirtualFrame frame) { + public void leave(Node astNode, VirtualFrame frame) { if (next != null) { if (!probeUnchanged.isValid()) { CompilerDirectives.transferToInterpreter(); @@ -301,16 +301,7 @@ } } - public void notifyLeave(Node astNode, VirtualFrame frame, boolean result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void notifyLeave(Node astNode, VirtualFrame frame, byte result) { + public void leave(Node astNode, VirtualFrame frame, boolean result) { if (next != null) { if (!probeUnchanged.isValid()) { CompilerDirectives.transferToInterpreter(); @@ -319,16 +310,7 @@ } } - public void notifyLeave(Node astNode, VirtualFrame frame, short result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void notifyLeave(Node astNode, VirtualFrame frame, int result) { + public void leave(Node astNode, VirtualFrame frame, byte result) { if (next != null) { if (!probeUnchanged.isValid()) { CompilerDirectives.transferToInterpreter(); @@ -337,7 +319,7 @@ } } - public void notifyLeave(Node astNode, VirtualFrame frame, long result) { + public void leave(Node astNode, VirtualFrame frame, short result) { if (next != null) { if (!probeUnchanged.isValid()) { CompilerDirectives.transferToInterpreter(); @@ -346,16 +328,7 @@ } } - public void notifyLeave(Node astNode, VirtualFrame frame, char result) { - if (next != null) { - if (!probeUnchanged.isValid()) { - CompilerDirectives.transferToInterpreter(); - } - next.internalLeave(astNode, frame, result); - } - } - - public void notifyLeave(Node astNode, VirtualFrame frame, float result) { + public void leave(Node astNode, VirtualFrame frame, int result) { if (next != null) { if (!probeUnchanged.isValid()) { CompilerDirectives.transferToInterpreter(); @@ -364,7 +337,16 @@ } } - public void notifyLeave(Node astNode, VirtualFrame frame, double result) { + public void leave(Node astNode, VirtualFrame frame, long result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void leave(Node astNode, VirtualFrame frame, char result) { if (next != null) { if (!probeUnchanged.isValid()) { CompilerDirectives.transferToInterpreter(); @@ -373,7 +355,7 @@ } } - public void notifyLeave(Node astNode, VirtualFrame frame, Object result) { + public void leave(Node astNode, VirtualFrame frame, float result) { if (next != null) { if (!probeUnchanged.isValid()) { CompilerDirectives.transferToInterpreter(); @@ -382,7 +364,25 @@ } } - public void notifyLeaveExceptional(Node astNode, VirtualFrame frame, Exception e) { + public void leave(Node astNode, VirtualFrame frame, double result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void leave(Node astNode, VirtualFrame frame, Object result) { + if (next != null) { + if (!probeUnchanged.isValid()) { + CompilerDirectives.transferToInterpreter(); + } + next.internalLeave(astNode, frame, result); + } + } + + public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { if (next != null) { if (!probeUnchanged.isValid()) { CompilerDirectives.transferToInterpreter(); @@ -391,50 +391,6 @@ } } - public void enter(Node astNode, VirtualFrame frame) { - } - - public void leave(Node astNode, VirtualFrame frame) { - } - - public void leave(Node astNode, VirtualFrame frame, boolean result) { - leave(astNode, frame, (Object) result); - } - - public void leave(Node astNode, VirtualFrame frame, byte result) { - leave(astNode, frame, (Object) result); - } - - public void leave(Node astNode, VirtualFrame frame, short result) { - leave(astNode, frame, (Object) result); - } - - public void leave(Node astNode, VirtualFrame frame, int result) { - leave(astNode, frame, (Object) result); - } - - public void leave(Node astNode, VirtualFrame frame, long result) { - leave(astNode, frame, (Object) result); - } - - public void leave(Node astNode, VirtualFrame frame, char result) { - leave(astNode, frame, (Object) result); - } - - public void leave(Node astNode, VirtualFrame frame, float result) { - leave(astNode, frame, (Object) result); - } - - public void leave(Node astNode, VirtualFrame frame, double result) { - leave(astNode, frame, (Object) result); - } - - public void leave(Node astNode, VirtualFrame frame, Object result) { - } - - public void leaveExceptional(Node astNode, VirtualFrame frame, Exception e) { - } - } } diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/ProbeManager.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/ProbeManager.java Wed May 21 11:45:50 2014 +0200 @@ -0,0 +1,159 @@ +/* + * 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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.truffle.api.instrument.impl; + +import java.util.*; + +import com.oracle.truffle.api.*; +import com.oracle.truffle.api.instrument.*; +import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeCallback; +import com.oracle.truffle.api.instrument.impl.InstrumentationNode.ProbeImpl; +import com.oracle.truffle.api.source.*; + +/** + * Factory and services for AST {@link Probe}s + */ +public final class ProbeManager { + + // TODO (mlvdv) use weak references. + /** + * Map: SourceSection ==> probe associated with that source section in an AST. + */ + private final Map srcToProbe = new HashMap<>(); + + // TODO (mlvdv) use weak references. + /** + * Map: Source line ==> probes associated with source sections starting on the line. + */ + private final Map> lineToProbes = new HashMap<>(); + + private final List probeListeners = new ArrayList<>(); + + private final ProbeCallback probeCallback; + + /** + * When non-null, "enter" events with matching tags will trigger a callback. + */ + private PhylumTrap phylumTrap = null; + + public ProbeManager() { + this.probeCallback = new ProbeCallback() { + /** + * Receives (from the {@link Probe} implementation) and distributes notification that a + * {@link Probe} has acquired a new {@linkplain PhylumTag tag}. + */ + public void newTagAdded(ProbeImpl probe, PhylumTag tag) { + for (ProbeListener listener : probeListeners) { + listener.probeTaggedAs(probe, tag); + } + if (phylumTrap != null && tag == phylumTrap.getTag()) { + probe.setTrap(phylumTrap); + } + } + }; + } + + public void addProbeListener(ProbeListener listener) { + assert listener != null; + probeListeners.add(listener); + } + + public Probe getProbe(SourceSection sourceSection) { + assert sourceSection != null; + + ProbeImpl probe = srcToProbe.get(sourceSection); + + if (probe != null) { + return probe; + } + probe = InstrumentationNode.createProbe(sourceSection, probeCallback); + + // Register new probe by unique SourceSection + srcToProbe.put(sourceSection, probe); + + // Register new probe by source line, there may be more than one + // Create line location for map key + final SourceLineLocation lineLocation = new SourceLineLocation(sourceSection.getSource(), sourceSection.getStartLine()); + + Collection probes = lineToProbes.get(lineLocation); + if (probes == null) { + probes = new ArrayList<>(2); + lineToProbes.put(lineLocation, probes); + } + probes.add(probe); + + for (ProbeListener listener : probeListeners) { + listener.newProbeInserted(sourceSection, probe); + } + + return probe; + } + + public Collection findProbesTaggedAs(PhylumTag tag) { + final List probes = new ArrayList<>(); + for (Probe probe : srcToProbe.values()) { + if (tag == null || probe.isTaggedAs(tag)) { + probes.add(probe); + } + } + return probes; + } + + public Collection findProbesByLine(SourceLineLocation lineLocation) { + final Collection probes = lineToProbes.get(lineLocation); + if (probes == null) { + return Collections.emptyList(); + } + return new ArrayList<>(probes); + } + + public void setPhylumTrap(PhylumTrap trap) { + assert trap != null; + if (this.phylumTrap != null) { + throw new IllegalStateException("trap already set"); + } + this.phylumTrap = trap; + + PhylumTag tag = trap.getTag(); + for (ProbeImpl probe : srcToProbe.values()) { + if (probe.isTaggedAs(tag)) { + probe.setTrap(trap); + } + } + } + + public void clearPhylumTrap() { + if (this.phylumTrap == null) { + throw new IllegalStateException("no trap set"); + } + for (ProbeImpl probe : srcToProbe.values()) { + if (probe.isTaggedAs(phylumTrap.getTag())) { + probe.setTrap(null); + } + } + phylumTrap = null; + } + +} diff -r a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SourceCallback.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/impl/SourceCallback.java Tue May 13 19:19:27 2014 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/* - * 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. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * 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.truffle.api.instrument.impl; - -import com.oracle.truffle.api.*; - -/** - * Instrumentation callback for guest language source-related events. - */ -public interface SourceCallback { - - public void startLoading(Source source); - - public void endLoading(Source source); -} diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeInfo.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceManager.java Wed May 21 11:45:50 2014 +0200 @@ -199,6 +199,32 @@ return checkTextMap().lineLength(lineNumber); } + public final SourceSection createSection(String identifier, int startOffset, int sectionLength) throws IllegalArgumentException { + final int codeLength = getCode().length(); + if (!(startOffset >= 0 && sectionLength >= 0 && startOffset + sectionLength <= codeLength)) { + throw new IllegalArgumentException("text positions out of range"); + } + checkTextMap(); + final int startLine = getLineNumber(startOffset); + final int startColumn = startOffset - getLineStartOffset(startLine) + 1; + + return new SourceSectionImpl(this, identifier, startLine, startColumn, startOffset, sectionLength); + } + + public SourceSection createSection(String identifier, int startLine, int startColumn, int sectionLength) { + checkTextMap(); + final int lineStartOffset = textMap.lineStartOffset(startLine); + if (startColumn > textMap.lineLength(startLine)) { + throw new IllegalArgumentException("column out of range"); + } + final int startOffset = lineStartOffset + startColumn - 1; + return new SourceSectionImpl(this, identifier, startLine, startColumn, startOffset, sectionLength); + } + + public SourceSection createSection(String identifier, int startLine, int startColumn, int charIndex, int length) { + return new SourceSectionImpl(this, identifier, startLine, startColumn, charIndex, length); + } + private TextMap checkTextMap() { if (textMap == null) { final String code = getCode(); @@ -211,7 +237,7 @@ } } - public static class LiteralSourceImpl extends SourceImpl { + private static class LiteralSourceImpl extends SourceImpl { private final String name; // Name used originally to describe the source private final String code; @@ -337,4 +363,142 @@ } + private static class SourceSectionImpl implements SourceSection { + + private final Source source; + private final String identifier; + private final int startLine; + private final int startColumn; + private final int charIndex; + private final int charLength; + + /** + * Creates a new object representing a contiguous text section within the source code of a + * guest language program's text. + *

+ * The starting location of the section is specified using two different coordinate: + *

    + *
  • (row, column): rows and columns are 1-based, so the first character in a + * source file is at position {@code (1,1)}.
  • + *
  • character index: 0-based offset of the character from the beginning of the + * source, so the first character in a file is at index {@code 0}.
  • + *
+ * The newline that terminates each line counts as a single character for the purpose + * of a character index. The (row,column) coordinates of a newline character should never + * appear in a text section. + *

+ * + * @param source object representing the complete source program that contains this section + * @param identifier an identifier used when printing the section + * @param startLine the 1-based number of the start line of the section + * @param startColumn the 1-based number of the start column of the section + * @param charIndex the 0-based index of the first character of the section + * @param charLength the length of the section in number of characters + */ + public SourceSectionImpl(Source source, String identifier, int startLine, int startColumn, int charIndex, int charLength) { + this.source = source; + this.identifier = identifier; + this.startLine = startLine; + this.startColumn = startColumn; + this.charIndex = charIndex; + this.charLength = charLength; + } + + public final Source getSource() { + return source; + } + + public final int getStartLine() { + return startLine; + } + + public final int getStartColumn() { + return startColumn; + } + + public final int getCharIndex() { + return charIndex; + } + + public final int getCharLength() { + return charLength; + } + + public final int getCharEndIndex() { + return charIndex + charLength; + } + + public final String getIdentifier() { + return identifier; + } + + public final String getCode() { + return getSource().getCode().substring(charIndex, charIndex + charLength); + } + + public final String getShortDescription() { + return String.format("%s:%d", source.getShortName(), startLine); + } + + @Override + public String toString() { + return String.format("%s:%d", source.getName(), startLine); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + charIndex; + result = prime * result + charLength; + result = prime * result + ((identifier == null) ? 0 : identifier.hashCode()); + result = prime * result + ((source == null) ? 0 : source.hashCode()); + result = prime * result + startColumn; + result = prime * result + startLine; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof SourceSectionImpl)) { + return false; + } + SourceSectionImpl other = (SourceSectionImpl) obj; + if (charIndex != other.charIndex) { + return false; + } + if (charLength != other.charLength) { + return false; + } + if (identifier == null) { + if (other.identifier != null) { + return false; + } + } else if (!identifier.equals(other.identifier)) { + return false; + } + if (source == null) { + if (other.source != null) { + return false; + } + } else if (!source.equals(other.source)) { + return false; + } + if (startColumn != other.startColumn) { + return false; + } + if (startLine != other.startLine) { + return false; + } + return true; + } + + } + } diff -r a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/ast/CodeElement.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLExpressionNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLRootNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/SLStatementNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBlockNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLBreakNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLContinueNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLIfNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLReturnNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/controlflow/SLWhileNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 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 Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/expression/SLMulNode.java Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java --- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java Tue May 13 19:19:27 2014 +0200 +++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/parser/SLNodeFactory.java Wed May 21 11:45:50 2014 +0200 @@ -27,7 +27,6 @@ import com.oracle.truffle.api.*; import com.oracle.truffle.api.frame.*; -import com.oracle.truffle.api.impl.*; import com.oracle.truffle.api.nodes.*; import com.oracle.truffle.sl.nodes.*; import com.oracle.truffle.sl.nodes.call.*; @@ -243,7 +242,7 @@ int startLine = t.line; int startColumn = t.col; int charLength = t.val.length(); - SourceSection sourceSection = new DefaultSourceSection(source, functionName, startLine, startColumn, 0, charLength); + SourceSection sourceSection = source.createSection(functionName, startLine, startColumn, 0, charLength); node.assignSourceSection(sourceSection); return node; diff -r a43ff5d18350 -r a6eeb3750238 make/bsd/makefiles/vm.make --- a/make/bsd/makefiles/vm.make Tue May 13 19:19:27 2014 +0200 +++ b/make/bsd/makefiles/vm.make Wed May 21 11:45:50 2014 +0200 @@ -229,6 +229,7 @@ else GRAAL_SPECIFIC_FILES := GRAAL_SPECIFIC_GPU_FILES := + Src_Dirs_I += $(HS_COMMON_SRC)/../graal/com.oracle.graal.hotspot/src_gen/hotspot endif # Always exclude these. diff -r a43ff5d18350 -r a6eeb3750238 make/linux/makefiles/vm.make --- a/make/linux/makefiles/vm.make Tue May 13 19:19:27 2014 +0200 +++ b/make/linux/makefiles/vm.make Wed May 21 11:45:50 2014 +0200 @@ -211,6 +211,7 @@ else GRAAL_SPECIFIC_FILES := GRAAL_SPECIFIC_GPU_FILES := + Src_Dirs_I += $(HS_COMMON_SRC)/../graal/com.oracle.graal.hotspot/src_gen/hotspot endif # Always exclude these. diff -r a43ff5d18350 -r a6eeb3750238 make/solaris/makefiles/vm.make --- a/make/solaris/makefiles/vm.make Tue May 13 19:19:27 2014 +0200 +++ b/make/solaris/makefiles/vm.make Wed May 21 11:45:50 2014 +0200 @@ -229,6 +229,7 @@ else GRAAL_SPECIFIC_FILES := GRAAL_SPECIFIC_GPU_FILES := + Src_Dirs_I += $(HS_COMMON_SRC)/../graal/com.oracle.graal.hotspot/src_gen/hotspot endif # Always exclude these. diff -r a43ff5d18350 -r a6eeb3750238 make/windows/makefiles/projectcreator.make --- a/make/windows/makefiles/projectcreator.make Tue May 13 19:19:27 2014 +0200 +++ b/make/windows/makefiles/projectcreator.make Wed May 21 11:45:50 2014 +0200 @@ -57,6 +57,7 @@ -relativeInclude src\os_cpu\windows_$(Platform_arch)\vm \ -relativeInclude src\cpu\$(Platform_arch)\vm \ -relativeInclude src\gpu \ + -relativeInclude graal\com.oracle.graal.hotspot\src_gen\hotspot \ -absoluteInclude $(HOTSPOTBUILDSPACE)/%f/generated \ -relativeSrcInclude src \ -absoluteSrcInclude $(HOTSPOTBUILDSPACE) \ @@ -151,7 +152,7 @@ ProjectCreatorIDEOptionsIgnoreGraal=\ -ignorePath_TARGET src/share/vm/graal \ - -ignorePath_TARGET graal/generated \ + -ignorePath_TARGET graal\com.oracle.graal.hotspot\src_gen\hotspot \ -ignorePath_TARGET vm/graal ProjectCreatorIDEOptionsIgnoreCompiler2=\ @@ -175,7 +176,6 @@ ProjectCreatorIDEOptions=$(ProjectCreatorIDEOptions) \ -define_compiler1 COMPILER1 \ -define_compiler1 GRAAL \ - -ignorePath_compiler1 graal/generated \ $(ProjectCreatorIDEOptionsIgnoreCompiler2:TARGET=compiler1) ################################################## @@ -195,7 +195,6 @@ -define_compiler2 COMPILER1 \ -define_compiler2 COMPILER2 \ -define_compiler2 GRAAL \ - -ignorePath_compiler2 graal/generated \ -additionalFile_compiler2 $(Platform_arch_model).ad \ -additionalFile_compiler2 ad_$(Platform_arch_model).cpp \ -additionalFile_compiler2 ad_$(Platform_arch_model).hpp \ diff -r a43ff5d18350 -r a6eeb3750238 mx/mx_graal.py --- a/mx/mx_graal.py Tue May 13 19:19:27 2014 +0200 +++ b/mx/mx_graal.py Wed May 21 11:45:50 2014 +0200 @@ -690,7 +690,7 @@ mustBuild = False timestamp = os.path.getmtime(timestampFile) sources = [] - for d in ['src', 'make']: + for d in ['src', 'make', 'graal/com.oracle.graal.hotspot/src_gen/hotspot']: for root, dirnames, files in os.walk(join(_graal_home, d)): # ignore /src/share/tools if root == join(_graal_home, 'src', 'share'): @@ -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 a43ff5d18350 -r a6eeb3750238 mx/projects --- a/mx/projects Tue May 13 19:19:27 2014 +0200 +++ b/mx/projects Wed May 21 11:45:50 2014 +0200 @@ -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 @@ -33,12 +32,12 @@ library@DACAPO@sha1=2626a9546df09009f6da0df854e6dc1113ef7dd4 library@JACOCOAGENT@path=lib/jacocoagent.jar -library@JACOCOAGENT@urls=http://lafo.ssw.uni-linz.ac.at/jacoco/jacocoagent.jar -library@JACOCOAGENT@sha1=9e2c835289356d86afbf31840f05a0f9007c4e44 +library@JACOCOAGENT@urls=http://lafo.ssw.uni-linz.ac.at/jacoco/jacocoagent-0.7.1-1.jar +library@JACOCOAGENT@sha1=2f73a645b02e39290e577ce555f00b02004650b0 library@JACOCOREPORT@path=lib/jacocoreport.jar -library@JACOCOREPORT@urls=http://lafo.ssw.uni-linz.ac.at/jacoco/jacocoreport.jar -library@JACOCOREPORT@sha1=32fb5ba2f12d86c4feb74bcefc17a4e6fad8a323 +library@JACOCOREPORT@urls=http://lafo.ssw.uni-linz.ac.at/jacoco/jacocoreport-0.7.1-1.jar +library@JACOCOREPORT@sha1=0c5db714804416dd1df4d8110762136ce3d5c7dc library@DACAPO_SCALA@path=lib/dacapo-scala-0.1.0-20120216.jar library@DACAPO_SCALA@urls=http://repo.scalabench.org/snapshots/org/scalabench/benchmarks/scala-benchmark-suite/0.1.0-SNAPSHOT/scala-benchmark-suite-0.1.0-20120216.103539-3.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 @@ -182,15 +182,35 @@ project@com.oracle.graal.sparc@javaCompliance=1.8 project@com.oracle.graal.sparc@workingSets=Graal,SPARC +# graal.hotspotvmconfig +project@com.oracle.graal.hotspotvmconfig@subDir=graal +project@com.oracle.graal.hotspotvmconfig@sourceDirs=src +project@com.oracle.graal.hotspotvmconfig@dependencies=com.oracle.graal.compiler.common +project@com.oracle.graal.hotspotvmconfig@checkstyle=com.oracle.graal.graph +project@com.oracle.graal.hotspotvmconfig@annotationProcessors=com.oracle.graal.service.processor +project@com.oracle.graal.hotspotvmconfig@annotationProcessorForDependents=true +project@com.oracle.graal.hotspotvmconfig@javaCompliance=1.8 +project@com.oracle.graal.hotspotvmconfig@workingSets=Graal,HotSpot + # graal.hotspot project@com.oracle.graal.hotspot@subDir=graal project@com.oracle.graal.hotspot@sourceDirs=src -project@com.oracle.graal.hotspot@dependencies=com.oracle.graal.replacements,com.oracle.graal.runtime,com.oracle.graal.printer,com.oracle.graal.baseline +project@com.oracle.graal.hotspot@dependencies=com.oracle.graal.replacements,com.oracle.graal.runtime,com.oracle.graal.printer,com.oracle.graal.baseline,com.oracle.graal.hotspotvmconfig project@com.oracle.graal.hotspot@checkstyle=com.oracle.graal.graph project@com.oracle.graal.hotspot@annotationProcessors=com.oracle.graal.replacements.verifier,com.oracle.graal.service.processor 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 +588,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 @@ -652,7 +672,7 @@ # graal.compiler.hsail.test.infra - HSAIL compiler test infrastructure project@com.oracle.graal.compiler.hsail.test.infra@subDir=graal project@com.oracle.graal.compiler.hsail.test.infra@sourceDirs=src -project@com.oracle.graal.compiler.hsail.test.infra@dependencies=com.oracle.graal.hotspot.hsail,JUNIT,OKRA_WITH_SIM +project@com.oracle.graal.compiler.hsail.test.infra@dependencies=com.oracle.graal.test,com.oracle.graal.hotspot.hsail,OKRA_WITH_SIM project@com.oracle.graal.compiler.hsail.test.infra@checkstyle=com.oracle.graal.graph project@com.oracle.graal.compiler.hsail.test.infra@javaCompliance=1.8 diff -r a43ff5d18350 -r a6eeb3750238 mxtool/mx.py --- a/mxtool/mx.py Tue May 13 19:19:27 2014 +0200 +++ b/mxtool/mx.py Wed May 21 11:45:50 2014 +0200 @@ -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') @@ -1108,7 +1232,7 @@ entryPath = zi.filename yield zf, entryPath -def sorted_deps(projectNames=None, includeLibs=False, includeAnnotationProcessors=False): +def sorted_deps(projectNames=None, includeLibs=False, includeJreLibs=False, includeAnnotationProcessors=False): """ Gets projects and libraries sorted such that dependencies are before the projects that depend on them. Unless 'includeLibs' is @@ -1116,12 +1240,12 @@ """ projects = projects_from_names(projectNames) - return sorted_project_deps(projects, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors) - -def sorted_project_deps(projects, includeLibs=False, includeAnnotationProcessors=False): + return sorted_project_deps(projects, includeLibs=includeLibs, includeJreLibs=includeJreLibs, includeAnnotationProcessors=includeAnnotationProcessors) + +def sorted_project_deps(projects, includeLibs=False, includeJreLibs=False, includeAnnotationProcessors=False): deps = [] for p in projects: - p.all_deps(deps, includeLibs=includeLibs, includeAnnotationProcessors=includeAnnotationProcessors) + p.all_deps(deps, includeLibs=includeLibs, includeJreLibs=includeJreLibs, includeAnnotationProcessors=includeAnnotationProcessors) return deps def _handle_missing_java_home(): @@ -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) @@ -2123,6 +2248,7 @@ failed = [] for t in tasks: t.proc.join() + _removeSubprocess(t.sub) if t.proc.exitcode != 0: failed.append(t) return failed @@ -2133,7 +2259,6 @@ if t.proc.is_alive(): active.append(t) else: - _removeSubprocess(t.sub) if t.proc.exitcode != 0: return ([], joinTasks(tasks)) return (active, []) @@ -2164,6 +2289,7 @@ cpus = multiprocessing.cpu_count() worklist = sortWorklist(tasks.values()) active = [] + failed = [] while len(worklist) != 0: while True: active, failed = checkTasks(active) @@ -2176,7 +2302,12 @@ else: break + if len(failed) != 0: + break + def executeTask(task): + # Clear sub-process list cloned from parent process + del _currentSubprocesses[:] task.execute() def depsDone(task): @@ -2196,7 +2327,12 @@ break worklist = sortWorklist(worklist) - joinTasks(active) + + failed += joinTasks(active) + if len(failed): + for t in failed: + log('Compiling {} failed'.format(t.proj.name)) + abort('{} Java compilation tasks failed'.format(len(failed))) for dist in _dists.values(): archive(['@' + dist.name]) @@ -2622,9 +2758,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 = [] @@ -2816,7 +2950,7 @@ igv.close('properties') igv.open('graph', {'name' : 'dependencies'}) igv.open('nodes') - for p in sorted_deps(includeLibs=True): + for p in sorted_deps(includeLibs=True, includeJreLibs=True): ident = len(ids) ids[p.name] = str(ident) igv.open('node', {'id' : str(ident)}) @@ -2868,10 +3002,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 +3169,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 +3210,7 @@ libraryDeps -= set(dep.all_deps([], True)) else: libraryDeps.add(dep) - else: + elif dep.isProject(): projectDeps.add(dep) for dep in containerDeps: @@ -3087,8 +3219,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 +3376,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 +3685,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 +3726,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 +3861,6 @@ continue if dep.isLibrary(): - if not dep.mustExist: - continue path = dep.get_path(resolve=True) if path: if os.sep == '\\': @@ -3747,7 +3869,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 +3936,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 +3983,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 +4057,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 a43ff5d18350 -r a6eeb3750238 src/gpu/hsail/vm/vmStructs_hsail.hpp --- a/src/gpu/hsail/vm/vmStructs_hsail.hpp Tue May 13 19:19:27 2014 +0200 +++ b/src/gpu/hsail/vm/vmStructs_hsail.hpp Wed May 21 11:45:50 2014 +0200 @@ -53,9 +53,4 @@ declare_toplevel_type(Hsail::HSAILKernelDeoptimization) \ declare_toplevel_type(Hsail::HSAILDeoptimizationInfo) -#define VM_INT_CONSTANTS_GPU_HSAIL(declare_constant) \ - declare_constant(sizeof(HSAILFrame)) \ - declare_constant(sizeof(Hsail::HSAILKernelDeoptimization)) \ - declare_constant(sizeof(Hsail::HSAILDeoptimizationInfo)) \ - #endif // GPU_HSAIL_VM_VMSTRUCTS_HSAIL_HPP diff -r a43ff5d18350 -r a6eeb3750238 src/share/vm/classfile/systemDictionary.hpp --- a/src/share/vm/classfile/systemDictionary.hpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/classfile/systemDictionary.hpp Wed May 21 11:45:50 2014 +0200 @@ -203,7 +203,6 @@ do_klass(HotSpotCodeInfo_klass, com_oracle_graal_hotspot_meta_HotSpotCodeInfo, Opt) \ do_klass(HotSpotInstalledCode_klass, com_oracle_graal_hotspot_meta_HotSpotInstalledCode, Opt) \ do_klass(HotSpotNmethod_klass, com_oracle_graal_hotspot_meta_HotSpotNmethod, Opt) \ - do_klass(HotSpotJavaType_klass, com_oracle_graal_hotspot_meta_HotSpotJavaType, Opt) \ do_klass(HotSpotResolvedJavaMethod_klass, com_oracle_graal_hotspot_meta_HotSpotResolvedJavaMethod, Opt) \ do_klass(HotSpotResolvedObjectType_klass, com_oracle_graal_hotspot_meta_HotSpotResolvedObjectType, Opt) \ do_klass(HotSpotMonitorValue_klass, com_oracle_graal_hotspot_meta_HotSpotMonitorValue, Opt) \ diff -r a43ff5d18350 -r a6eeb3750238 src/share/vm/classfile/vmSymbols.hpp --- a/src/share/vm/classfile/vmSymbols.hpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/classfile/vmSymbols.hpp Wed May 21 11:45:50 2014 +0200 @@ -312,7 +312,6 @@ template(com_oracle_graal_hotspot_meta_HotSpotCodeInfo, "com/oracle/graal/hotspot/meta/HotSpotCodeInfo") \ template(com_oracle_graal_hotspot_meta_HotSpotInstalledCode, "com/oracle/graal/hotspot/meta/HotSpotInstalledCode") \ template(com_oracle_graal_hotspot_meta_HotSpotNmethod, "com/oracle/graal/hotspot/meta/HotSpotNmethod") \ - template(com_oracle_graal_hotspot_meta_HotSpotJavaType, "com/oracle/graal/hotspot/meta/HotSpotJavaType") \ template(com_oracle_graal_hotspot_meta_HotSpotResolvedJavaMethod, "com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod") \ template(com_oracle_graal_hotspot_meta_HotSpotResolvedObjectType, "com/oracle/graal/hotspot/meta/HotSpotResolvedObjectType") \ template(com_oracle_graal_hotspot_meta_HotSpotMonitorValue, "com/oracle/graal/hotspot/meta/HotSpotMonitorValue") \ @@ -323,12 +322,10 @@ template(com_oracle_graal_api_meta_Constant, "com/oracle/graal/api/meta/Constant") \ template(com_oracle_graal_api_meta_PrimitiveConstant, "com/oracle/graal/api/meta/PrimitiveConstant") \ template(com_oracle_graal_api_meta_NullConstant, "com/oracle/graal/api/meta/NullConstant") \ - template(com_oracle_graal_api_meta_ConstantPool, "com/oracle/graal/api/meta/ConstantPool") \ template(com_oracle_graal_api_meta_ExceptionHandler, "com/oracle/graal/api/meta/ExceptionHandler") \ template(com_oracle_graal_api_meta_JavaMethod, "com/oracle/graal/api/meta/JavaMethod") \ template(com_oracle_graal_api_meta_JavaType, "com/oracle/graal/api/meta/JavaType") \ template(com_oracle_graal_api_meta_Kind, "com/oracle/graal/api/meta/Kind") \ - template(com_oracle_graal_api_meta_ResolvedJavaField, "com/oracle/graal/api/meta/ResolvedJavaField") \ template(com_oracle_graal_api_meta_Value, "com/oracle/graal/api/meta/Value") \ /* graal.api.code */ \ template(com_oracle_graal_api_code_Assumptions, "com/oracle/graal/api/code/Assumptions") \ diff -r a43ff5d18350 -r a6eeb3750238 src/share/vm/graal/graalCodeInstaller.cpp --- a/src/share/vm/graal/graalCodeInstaller.cpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/graal/graalCodeInstaller.cpp Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 src/share/vm/graal/graalCompilerToVM.cpp --- a/src/share/vm/graal/graalCompilerToVM.cpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/graal/graalCompilerToVM.cpp Wed May 21 11:45:50 2014 +0200 @@ -53,12 +53,6 @@ TRACE_graal_3("CompilerToVM::" #name); \ GRAAL_VM_ENTRY_MARK; \ -// Entry to native method implementation that calls a JNI function -// and hence cannot transition current thread to '_thread_in_vm'. -#define C2V_ENTRY(result_type, name, signature) \ - JNIEXPORT result_type JNICALL c2v_ ## name signature { \ - TRACE_graal_3("CompilerToVM::" #name); \ - #define C2V_END } extern "C" { @@ -91,128 +85,11 @@ extern uint64_t gHotSpotVMLongConstantEntryArrayStride; } -// helpers used to set fields in the HotSpotVMConfig object -static jfieldID getFieldID(JNIEnv* env, jobject obj, const char* name, const char* sig) { - jfieldID id = env->GetFieldID(env->GetObjectClass(obj), name, sig); - if (id == NULL) { - fatal(err_msg("field not found: %s (%s)", name, sig)); - } - return id; -} - -C2V_ENTRY(void, initializeConfiguration, (JNIEnv *env, jobject, jobject config)) - -#define set_boolean(name, value) do { env->SetBooleanField(config, getFieldID(env, config, name, "Z"), value); } while (0) -#define set_int(name, value) do { env->SetIntField(config, getFieldID(env, config, name, "I"), value); } while (0) -#define set_long(name, value) do { env->SetLongField(config, getFieldID(env, config, name, "J"), value); } while (0) -#define set_address(name, value) do { set_long(name, (jlong) value); } while (0) - - guarantee(HeapWordSize == sizeof(char*), "Graal assumption that HeadWordSize == machine word size is wrong"); - - set_address("gHotSpotVMStructs", gHotSpotVMStructs); - set_long("gHotSpotVMStructEntryTypeNameOffset", gHotSpotVMStructEntryTypeNameOffset); - set_long("gHotSpotVMStructEntryFieldNameOffset", gHotSpotVMStructEntryFieldNameOffset); - set_long("gHotSpotVMStructEntryTypeStringOffset", gHotSpotVMStructEntryTypeStringOffset); - set_long("gHotSpotVMStructEntryIsStaticOffset", gHotSpotVMStructEntryIsStaticOffset); - set_long("gHotSpotVMStructEntryOffsetOffset", gHotSpotVMStructEntryOffsetOffset); - set_long("gHotSpotVMStructEntryAddressOffset", gHotSpotVMStructEntryAddressOffset); - set_long("gHotSpotVMStructEntryArrayStride", gHotSpotVMStructEntryArrayStride); - - set_address("gHotSpotVMTypes", gHotSpotVMTypes); - set_long("gHotSpotVMTypeEntryTypeNameOffset", gHotSpotVMTypeEntryTypeNameOffset); - set_long("gHotSpotVMTypeEntrySuperclassNameOffset", gHotSpotVMTypeEntrySuperclassNameOffset); - set_long("gHotSpotVMTypeEntryIsOopTypeOffset", gHotSpotVMTypeEntryIsOopTypeOffset); - set_long("gHotSpotVMTypeEntryIsIntegerTypeOffset", gHotSpotVMTypeEntryIsIntegerTypeOffset); - set_long("gHotSpotVMTypeEntryIsUnsignedOffset", gHotSpotVMTypeEntryIsUnsignedOffset); - set_long("gHotSpotVMTypeEntrySizeOffset", gHotSpotVMTypeEntrySizeOffset); - set_long("gHotSpotVMTypeEntryArrayStride", gHotSpotVMTypeEntryArrayStride); - - set_address("gHotSpotVMIntConstants", gHotSpotVMIntConstants); - set_long("gHotSpotVMIntConstantEntryNameOffset", gHotSpotVMIntConstantEntryNameOffset); - set_long("gHotSpotVMIntConstantEntryValueOffset", gHotSpotVMIntConstantEntryValueOffset); - set_long("gHotSpotVMIntConstantEntryArrayStride", gHotSpotVMIntConstantEntryArrayStride); - - set_address("gHotSpotVMLongConstants", gHotSpotVMLongConstants); - set_long("gHotSpotVMLongConstantEntryNameOffset", gHotSpotVMLongConstantEntryNameOffset); - set_long("gHotSpotVMLongConstantEntryValueOffset", gHotSpotVMLongConstantEntryValueOffset); - set_long("gHotSpotVMLongConstantEntryArrayStride", gHotSpotVMLongConstantEntryArrayStride); - - //------------------------------------------------------------------------------------------------ - - set_int("arrayLengthOffset", arrayOopDesc::length_offset_in_bytes()); - - set_int("extraStackEntries", Method::extra_stack_entries()); - - set_int("tlabAlignmentReserve", (int32_t)ThreadLocalAllocBuffer::alignment_reserve()); - set_long("heapTopAddress", (jlong)(address) Universe::heap()->top_addr()); - set_long("heapEndAddress", (jlong)(address) Universe::heap()->end_addr()); - - set_boolean("inlineContiguousAllocationSupported", !CMSIncrementalMode && Universe::heap()->supports_inline_contig_alloc()); - - set_long("verifyOopMask", Universe::verify_oop_mask()); - set_long("verifyOopBits", Universe::verify_oop_bits()); - - set_int("instanceKlassVtableStartOffset", InstanceKlass::vtable_start_offset() * HeapWordSize); - - //------------------------------------------------------------------------------------------------ - - set_address("registerFinalizerAddress", SharedRuntime::register_finalizer); - set_address("exceptionHandlerForReturnAddressAddress", SharedRuntime::exception_handler_for_return_address); - set_address("osrMigrationEndAddress", SharedRuntime::OSR_migration_end); - - set_address("javaTimeMillisAddress", CAST_FROM_FN_PTR(address, os::javaTimeMillis)); - set_address("javaTimeNanosAddress", CAST_FROM_FN_PTR(address, os::javaTimeNanos)); - set_address("arithmeticSinAddress", CAST_FROM_FN_PTR(address, SharedRuntime::dsin)); - set_address("arithmeticCosAddress", CAST_FROM_FN_PTR(address, SharedRuntime::dcos)); - set_address("arithmeticTanAddress", CAST_FROM_FN_PTR(address, SharedRuntime::dtan)); - - set_address("newInstanceAddress", GraalRuntime::new_instance); - set_address("newArrayAddress", GraalRuntime::new_array); - set_address("newMultiArrayAddress", GraalRuntime::new_multi_array); - set_address("dynamicNewArrayAddress", GraalRuntime::dynamic_new_array); - set_address("dynamicNewInstanceAddress", GraalRuntime::dynamic_new_instance); - set_address("threadIsInterruptedAddress", GraalRuntime::thread_is_interrupted); - set_address("vmMessageAddress", GraalRuntime::vm_message); - set_address("identityHashCodeAddress", GraalRuntime::identity_hash_code); - set_address("exceptionHandlerForPcAddress", GraalRuntime::exception_handler_for_pc); - set_address("monitorenterAddress", GraalRuntime::monitorenter); - set_address("monitorexitAddress", GraalRuntime::monitorexit); - set_address("createNullPointerExceptionAddress", GraalRuntime::create_null_exception); - set_address("createOutOfBoundsExceptionAddress", GraalRuntime::create_out_of_bounds_exception); - set_address("logPrimitiveAddress", GraalRuntime::log_primitive); - set_address("logObjectAddress", GraalRuntime::log_object); - set_address("logPrintfAddress", GraalRuntime::log_printf); - set_address("vmErrorAddress", GraalRuntime::vm_error); - set_address("loadAndClearExceptionAddress", GraalRuntime::load_and_clear_exception); - set_address("writeBarrierPreAddress", GraalRuntime::write_barrier_pre); - set_address("writeBarrierPostAddress", GraalRuntime::write_barrier_post); - set_address("validateObject", GraalRuntime::validate_object); - - set_address("deoptimizationFetchUnrollInfo", Deoptimization::fetch_unroll_info); - set_address("deoptimizationUncommonTrap", Deoptimization::uncommon_trap); - set_address("deoptimizationUnpackFrames", Deoptimization::unpack_frames); - - //------------------------------------------------------------------------------------------------ - - set_int("graalCountersThreadOffset", in_bytes(JavaThread::graal_counters_offset())); - set_int("graalCountersSize", (jint) GraalCounterSize); - - //------------------------------------------------------------------------------------------------ - - set_long("dllLoad", (jlong) os::dll_load); - set_long("dllLookup", (jlong) os::dll_lookup); - #if defined(TARGET_OS_FAMILY_bsd) || defined(TARGET_OS_FAMILY_linux) - set_long("rtldDefault", (jlong) RTLD_DEFAULT); - #endif - -#undef set_boolean -#undef set_int -#undef set_long -#undef set_address - +C2V_VMENTRY(void, initializeConfiguration, (JNIEnv *, jobject, jobject config)) + VMStructs::initHotSpotVMConfig(JNIHandles::resolve(config)); C2V_END -C2V_ENTRY(jbyteArray, initializeBytecode, (JNIEnv *env, jobject, jlong metaspace_method, jbyteArray result)) +C2V_VMENTRY(jbyteArray, initializeBytecode, (JNIEnv *, jobject, jlong metaspace_method)) methodHandle method = asMethod(metaspace_method); ResourceMark rm; @@ -279,9 +156,9 @@ } } - env->SetByteArrayRegion(result, 0, code_size, reconstituted_code); - - return result; + typeArrayOop result_array = oopFactory::new_byteArray(code_size, CHECK_NULL); + memcpy(result_array->byte_at_addr(0), reconstituted_code, code_size); + return (jbyteArray) JNIHandles::make_local(result_array); C2V_END C2V_VMENTRY(jint, exceptionTableLength, (JNIEnv *, jobject, jlong metaspace_method)) @@ -350,7 +227,7 @@ return CompilerOracle::should_inline(method) || method->force_inline(); C2V_END -C2V_VMENTRY(jlong, lookupType, (JNIEnv *env, jobject, jstring jname, jclass accessing_class, jboolean resolve)) +C2V_VMENTRY(jlong, lookupType, (JNIEnv*, jobject, jstring jname, jclass accessing_class, jboolean resolve)) ResourceMark rm; Handle name = JNIHandles::resolve(jname); Symbol* class_name = java_lang_String::as_symbol(name, THREAD); @@ -375,44 +252,44 @@ return (jlong) (address) resolved_klass; C2V_END -C2V_VMENTRY(jobject, resolveConstantInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index)) +C2V_VMENTRY(jobject, resolveConstantInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index)) ConstantPool* cp = (ConstantPool*) metaspace_constant_pool; oop result = cp->resolve_constant_at(index, CHECK_NULL); return JNIHandles::make_local(THREAD, result); C2V_END -C2V_VMENTRY(jobject, resolvePossiblyCachedConstantInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index)) +C2V_VMENTRY(jobject, resolvePossiblyCachedConstantInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index)) ConstantPool* cp = (ConstantPool*) metaspace_constant_pool; oop result = cp->resolve_possibly_cached_constant_at(index, CHECK_NULL); return JNIHandles::make_local(THREAD, result); C2V_END -C2V_VMENTRY(jint, lookupNameAndTypeRefIndexInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index)) +C2V_VMENTRY(jint, lookupNameAndTypeRefIndexInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index)) constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool; return cp->name_and_type_ref_index_at(index); C2V_END -C2V_VMENTRY(jlong, lookupNameRefInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index)) +C2V_VMENTRY(jlong, lookupNameRefInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index)) constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool; return (jlong) (address) cp->name_ref_at(index); C2V_END -C2V_VMENTRY(jlong, lookupSignatureRefInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index)) +C2V_VMENTRY(jlong, lookupSignatureRefInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index)) constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool; return (jlong) (address) cp->signature_ref_at(index); C2V_END -C2V_VMENTRY(jint, lookupKlassRefIndexInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index)) +C2V_VMENTRY(jint, lookupKlassRefIndexInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index)) constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool; return cp->klass_ref_index_at(index); C2V_END -C2V_VMENTRY(jlong, constantPoolKlassAt, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index)) +C2V_VMENTRY(jlong, constantPoolKlassAt, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index)) ConstantPool* cp = (ConstantPool*) metaspace_constant_pool; return (jlong) (address) cp->klass_at(index, THREAD); C2V_END -C2V_VMENTRY(jlong, lookupKlassInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode)) +C2V_VMENTRY(jlong, lookupKlassInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode)) constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool; KlassHandle loading_klass(cp->pool_holder()); bool is_accessible = false; @@ -436,13 +313,13 @@ return (jlong) CompilerToVM::tag_pointer(klass()); C2V_END -C2V_VMENTRY(jobject, lookupAppendixInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index)) +C2V_VMENTRY(jobject, lookupAppendixInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index)) constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool; oop appendix_oop = ConstantPool::appendix_at_if_loaded(cp, index); return JNIHandles::make_local(THREAD, appendix_oop); C2V_END -C2V_VMENTRY(jlong, lookupMethodInPool, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode)) +C2V_VMENTRY(jlong, lookupMethodInPool, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode)) constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool; instanceKlassHandle pool_holder(cp->pool_holder()); Bytecodes::Code bc = (Bytecodes::Code) (((int) opcode) & 0xFF); @@ -450,12 +327,12 @@ return (jlong) (address) method(); C2V_END -C2V_VMENTRY(jint, constantPoolRemapInstructionOperandFromCache, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index)) +C2V_VMENTRY(jint, constantPoolRemapInstructionOperandFromCache, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index)) ConstantPool* cp = (ConstantPool*) metaspace_constant_pool; return cp->remap_instruction_operand_from_cache(index); C2V_END -C2V_VMENTRY(jlong, resolveField, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode, jlongArray info_handle)) +C2V_VMENTRY(jlong, resolveField, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index, jbyte opcode, jlongArray info_handle)) ResourceMark rm; constantPoolHandle cp = (ConstantPool*) metaspace_constant_pool; Bytecodes::Code code = (Bytecodes::Code)(((int) opcode) & 0xFF); @@ -468,11 +345,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; + assert(!klass->is_interface(), ""); + 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)) @@ -486,7 +428,7 @@ return (jlong) (address) klass->class_initializer(); C2V_END -C2V_VMENTRY(jlong, getMaxCallTargetOffset, (JNIEnv *env, jobject, jlong addr)) +C2V_VMENTRY(jlong, getMaxCallTargetOffset, (JNIEnv*, jobject, jlong addr)) address target_addr = (address) addr; if (target_addr != 0x0) { int64_t off_low = (int64_t)target_addr - ((int64_t)CodeCache::low_bound() + sizeof(int)); @@ -622,7 +564,7 @@ return JNIHandles::make_local(result()); C2V_END -C2V_VMENTRY(jobject, getStackTraceElement, (JNIEnv *env, jobject, jlong metaspace_method, int bci)) +C2V_VMENTRY(jobject, getStackTraceElement, (JNIEnv*, jobject, jlong metaspace_method, int bci)) ResourceMark rm; HandleMark hm; @@ -631,7 +573,7 @@ return JNIHandles::make_local(element); C2V_END -C2V_VMENTRY(jobject, executeCompiledMethodVarargs, (JNIEnv *env, jobject, jobject args, jobject hotspotInstalledCode)) +C2V_VMENTRY(jobject, executeCompiledMethodVarargs, (JNIEnv*, jobject, jobject args, jobject hotspotInstalledCode)) ResourceMark rm; HandleMark hm; @@ -659,7 +601,7 @@ } C2V_END -C2V_ENTRY(jlongArray, getLineNumberTable, (JNIEnv *env, jobject, jlong metaspace_method)) +C2V_VMENTRY(jlongArray, getLineNumberTable, (JNIEnv *, jobject, jlong metaspace_method)) Method* method = (Method*) metaspace_method; if (!method->has_linenumber_table()) { return NULL; @@ -671,19 +613,19 @@ } CompressedLineNumberReadStream stream(method->compressed_linenumber_table()); - jlongArray result = env->NewLongArray(2 * num_entries); + typeArrayOop result = oopFactory::new_longArray(2 * num_entries, CHECK_NULL); int i = 0; jlong value; while (stream.read_pair()) { value = ((long) stream.bci()); - env->SetLongArrayRegion(result,i,1,&value); + result->long_at_put(i, value); value = ((long) stream.line()); - env->SetLongArrayRegion(result,i + 1,1,&value); + result->long_at_put(i + 1, value); i += 2; } - return result; + return (jlongArray) JNIHandles::make_local(result); C2V_END C2V_VMENTRY(jlong, getLocalVariableTableStart, (JNIEnv *, jobject, jlong metaspace_method)) @@ -701,7 +643,7 @@ return method->localvariable_table_length(); C2V_END -C2V_VMENTRY(void, reprofile, (JNIEnv *env, jobject, jlong metaspace_method)) +C2V_VMENTRY(void, reprofile, (JNIEnv*, jobject, jlong metaspace_method)) Method* method = asMethod(metaspace_method); MethodCounters* mcs = method->method_counters(); if (mcs != NULL) { @@ -725,7 +667,7 @@ C2V_END -C2V_VMENTRY(void, invalidateInstalledCode, (JNIEnv *env, jobject, jobject hotspotInstalledCode)) +C2V_VMENTRY(void, invalidateInstalledCode, (JNIEnv*, jobject, jobject hotspotInstalledCode)) jlong nativeMethod = InstalledCode::address(hotspotInstalledCode); nmethod* m = (nmethod*)nativeMethod; if (m != NULL && !m->is_not_entrant()) { @@ -736,32 +678,36 @@ InstalledCode::set_address(hotspotInstalledCode, 0); C2V_END -C2V_VMENTRY(jobject, getJavaMirror, (JNIEnv *env, jobject, jlong metaspace_klass)) +C2V_VMENTRY(jobject, getJavaMirror, (JNIEnv*, jobject, jlong metaspace_klass)) Klass* klass = asKlass(metaspace_klass); return JNIHandles::make_local(klass->java_mirror()); C2V_END -C2V_VMENTRY(jlong, readUnsafeKlassPointer, (JNIEnv *env, jobject, jobject o)) +C2V_VMENTRY(jlong, readUnsafeKlassPointer, (JNIEnv*, jobject, jobject o)) oop resolved_o = JNIHandles::resolve(o); jlong klass = (jlong)(address)resolved_o->klass(); return klass; C2V_END -C2V_VMENTRY(jlongArray, collectCounters, (JNIEnv *env, jobject)) +C2V_VMENTRY(jlongArray, collectCounters, (JNIEnv*, jobject)) typeArrayOop arrayOop = oopFactory::new_longArray(GraalCounterSize, CHECK_NULL); JavaThread::collect_counters(arrayOop); return (jlongArray) JNIHandles::make_local(arrayOop); C2V_END -C2V_ENTRY(jobject, getGPUs, (JNIEnv *env, jobject)) +// In general we should avoid using regular JNI methods to interact with the JVM but this +// particular case is just about registering JNI methods so it should be a regular native +// method. +JNIEXPORT jobject JNICALL c2v_getGPUs (JNIEnv* env, jobject) { + TRACE_graal_3("CompilerToVM::getGPUs" ); #if defined(TARGET_OS_FAMILY_bsd) || defined(TARGET_OS_FAMILY_linux) || defined(TARGET_OS_FAMILY_windows) return Gpu::probe_gpus(env); #else return env->NewStringUTF(""); #endif -C2V_END +} -C2V_VMENTRY(int, allocateCompileId, (JNIEnv *env, jobject, jlong metaspace_method, int entry_bci)) +C2V_VMENTRY(int, allocateCompileId, (JNIEnv*, jobject, jlong metaspace_method, int entry_bci)) HandleMark hm; ResourceMark rm; Method* method = (Method*) metaspace_method; @@ -769,17 +715,17 @@ C2V_END -C2V_VMENTRY(jboolean, isMature, (JNIEnv *env, jobject, jlong metaspace_method_data)) +C2V_VMENTRY(jboolean, isMature, (JNIEnv*, jobject, jlong metaspace_method_data)) MethodData* mdo = asMethodData(metaspace_method_data); return mdo != NULL && mdo->is_mature(); C2V_END -C2V_VMENTRY(jboolean, hasCompiledCodeForOSR, (JNIEnv *env, jobject, jlong metaspace_method, int entry_bci, int comp_level)) +C2V_VMENTRY(jboolean, hasCompiledCodeForOSR, (JNIEnv*, jobject, jlong metaspace_method, int entry_bci, int comp_level)) Method* method = asMethod(metaspace_method); return method->lookup_osr_nmethod_for(entry_bci, comp_level, true) != NULL; C2V_END -C2V_VMENTRY(jlong, getTimeStamp, (JNIEnv *env, jobject)) +C2V_VMENTRY(jlong, getTimeStamp, (JNIEnv*, jobject)) // tty->time_stamp is the time since VM start which should be used // for all HotSpot log output when a timestamp is required. return tty->time_stamp().milliseconds(); @@ -796,7 +742,7 @@ return false; } -C2V_VMENTRY(jobject, getNextStackFrame, (JNIEnv *env, jobject compilerToVM, jobject hs_frame, jlongArray methods, jint initialSkip)) +C2V_VMENTRY(jobject, getNextStackFrame, (JNIEnv*, jobject compilerToVM, jobject hs_frame, jlongArray methods, jint initialSkip)) ResourceMark rm; if (!thread->has_last_Java_frame()) return NULL; @@ -931,7 +877,7 @@ return NULL; C2V_END -C2V_VMENTRY(void, resolveInvokeDynamic, (JNIEnv *env, jobject, jlong metaspace_constant_pool, jint index)) +C2V_VMENTRY(void, resolveInvokeDynamic, (JNIEnv*, jobject, jlong metaspace_constant_pool, jint index)) ConstantPool* cp = (ConstantPool*)metaspace_constant_pool; CallInfo callInfo; LinkResolver::resolve_invokedynamic(callInfo, cp, index, CHECK); @@ -940,7 +886,7 @@ C2V_END // public native void materializeVirtualObjects(HotSpotStackFrameReference stackFrame, boolean invalidate); -C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv *env, jobject, jobject hs_frame, bool invalidate)) +C2V_VMENTRY(void, materializeVirtualObjects, (JNIEnv*, jobject, jobject hs_frame, bool invalidate)) ResourceMark rm; if (hs_frame == NULL) { @@ -1058,58 +1004,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", 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 a43ff5d18350 -r a6eeb3750238 src/share/vm/graal/graalJavaAccess.hpp --- a/src/share/vm/graal/graalJavaAccess.hpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/graal/graalJavaAccess.hpp Wed May 21 11:45:50 2014 +0200 @@ -52,13 +52,8 @@ oop_field(HotSpotResolvedObjectType, javaClass, "Ljava/lang/Class;") \ end_class \ start_class(HotSpotResolvedJavaMethod) \ - oop_field(HotSpotResolvedJavaMethod, name, "Ljava/lang/String;") \ - oop_field(HotSpotResolvedJavaMethod, holder, "Lcom/oracle/graal/hotspot/meta/HotSpotResolvedObjectType;") \ long_field(HotSpotResolvedJavaMethod, metaspaceMethod) \ end_class \ - start_class(HotSpotJavaType) \ - oop_field(HotSpotJavaType, name, "Ljava/lang/String;") \ - end_class \ start_class(InstalledCode) \ long_field(InstalledCode, address) \ long_field(InstalledCode, version) \ diff -r a43ff5d18350 -r a6eeb3750238 src/share/vm/graal/vmStructs_graal.hpp --- a/src/share/vm/graal/vmStructs_graal.hpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/graal/vmStructs_graal.hpp Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 src/share/vm/interpreter/linkResolver.hpp --- a/src/share/vm/interpreter/linkResolver.hpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/interpreter/linkResolver.hpp Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 src/share/vm/runtime/deoptimization.cpp --- a/src/share/vm/runtime/deoptimization.cpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/runtime/deoptimization.cpp Wed May 21 11:45:50 2014 +0200 @@ -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 a43ff5d18350 -r a6eeb3750238 src/share/vm/runtime/globals.cpp --- a/src/share/vm/runtime/globals.cpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/runtime/globals.cpp Wed May 21 11:45:50 2014 +0200 @@ -477,12 +477,12 @@ } // Search the flag table for a named flag -Flag* Flag::find_flag(const char* name, size_t length, bool allow_locked) { +Flag* Flag::find_flag(const char* name, size_t length, bool allow_locked, bool allow_constant) { for (Flag* current = &flagTable[0]; current->_name != NULL; current++) { if (str_equal(current->_name, name, length)) { // Found a matching entry. // Don't report notproduct and develop flags in product builds. - if (current->is_constant_in_binary()) { + if (current->is_constant_in_binary() && !allow_constant) { return NULL; } // Report locked flags only if allowed. diff -r a43ff5d18350 -r a6eeb3750238 src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/runtime/globals.hpp Wed May 21 11:45:50 2014 +0200 @@ -256,7 +256,7 @@ // number of flags static size_t numFlags; - static Flag* find_flag(const char* name, size_t length, bool allow_locked = false); + static Flag* find_flag(const char* name, size_t length, bool allow_locked = false, bool allow_constant = false); static Flag* fuzzy_match(const char* name, size_t length, bool allow_locked = false); void check_writable(); @@ -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 a43ff5d18350 -r a6eeb3750238 src/share/vm/runtime/java.cpp --- a/src/share/vm/runtime/java.cpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/runtime/java.cpp Wed May 21 11:45:50 2014 +0200 @@ -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; diff -r a43ff5d18350 -r a6eeb3750238 src/share/vm/runtime/vmStructs.cpp --- a/src/share/vm/runtime/vmStructs.cpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/runtime/vmStructs.cpp Wed May 21 11:45:50 2014 +0200 @@ -70,6 +70,9 @@ #include "oops/constMethod.hpp" #include "oops/constantPool.hpp" #include "oops/cpCache.hpp" +#ifdef GRAAL +#include "oops/fieldStreams.hpp" +#endif #include "oops/instanceClassLoaderKlass.hpp" #include "oops/instanceKlass.hpp" #include "oops/instanceMirrorKlass.hpp" @@ -105,6 +108,7 @@ #include "utilities/hashtable.hpp" #include "utilities/macros.hpp" #ifdef GRAAL +# include "graal/graalRuntime.hpp" # include "graal/vmStructs_graal.hpp" # include "hsail/vm/vmStructs_hsail.hpp" #endif @@ -3124,7 +3128,6 @@ VM_INT_CONSTANTS_GRAAL(GENERATE_VM_INT_CONSTANT_ENTRY, GENERATE_PREPROCESSOR_VM_INT_CONSTANT_ENTRY) - VM_INT_CONSTANTS_GPU_HSAIL(GENERATE_VM_INT_CONSTANT_ENTRY) #endif #if INCLUDE_ALL_GCS @@ -3480,3 +3483,11 @@ } } #endif + + +#ifdef GRAAL +// Emit intialization code for HotSpotVMConfig. It's placed here so +// it can take advantage of the relaxed access checking enjoyed by +// VMStructs. +#include "HotSpotVMConfig.inline.hpp" +#endif diff -r a43ff5d18350 -r a6eeb3750238 src/share/vm/runtime/vmStructs.hpp --- a/src/share/vm/runtime/vmStructs.hpp Tue May 13 19:19:27 2014 +0200 +++ b/src/share/vm/runtime/vmStructs.hpp Wed May 21 11:45:50 2014 +0200 @@ -126,6 +126,10 @@ static void test(); #endif +#ifdef GRAAL + static void initHotSpotVMConfig(oop config); +#endif + private: // Look up a type in localHotSpotVMTypes using strcmp() (debug build only). // Returns 1 if found, 0 if not.