8236867: Enhance Graal interface handling
Reviewed-by: never, dnsimon, kvn, ahgross, rhalade
This commit is contained in:
parent
0c58055bac
commit
45258a1799
@ -620,6 +620,39 @@ public final class HotSpotConstantPool implements ConstantPool, MetaspaceHandleO
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaType lookupReferencedType(int cpi, int opcode) {
|
||||||
|
int index;
|
||||||
|
switch (opcode) {
|
||||||
|
case Bytecodes.CHECKCAST:
|
||||||
|
case Bytecodes.INSTANCEOF:
|
||||||
|
case Bytecodes.NEW:
|
||||||
|
case Bytecodes.ANEWARRAY:
|
||||||
|
case Bytecodes.MULTIANEWARRAY:
|
||||||
|
case Bytecodes.LDC:
|
||||||
|
case Bytecodes.LDC_W:
|
||||||
|
case Bytecodes.LDC2_W:
|
||||||
|
index = cpi;
|
||||||
|
break;
|
||||||
|
case Bytecodes.GETSTATIC:
|
||||||
|
case Bytecodes.PUTSTATIC:
|
||||||
|
case Bytecodes.GETFIELD:
|
||||||
|
case Bytecodes.PUTFIELD:
|
||||||
|
case Bytecodes.INVOKEVIRTUAL:
|
||||||
|
case Bytecodes.INVOKESPECIAL:
|
||||||
|
case Bytecodes.INVOKESTATIC:
|
||||||
|
case Bytecodes.INVOKEINTERFACE: {
|
||||||
|
index = rawIndexToConstantPoolCacheIndex(cpi, opcode);
|
||||||
|
index = getKlassRefIndexAt(index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw JVMCIError.shouldNotReachHere("Unexpected opcode " + opcode);
|
||||||
|
}
|
||||||
|
final Object type = compilerToVM().lookupKlassInPool(this, index);
|
||||||
|
return getJavaType(type);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JavaField lookupField(int cpi, ResolvedJavaMethod method, int opcode) {
|
public JavaField lookupField(int cpi, ResolvedJavaMethod method, int opcode) {
|
||||||
final int index = rawIndexToConstantPoolCacheIndex(cpi, opcode);
|
final int index = rawIndexToConstantPoolCacheIndex(cpi, opcode);
|
||||||
|
@ -46,6 +46,16 @@ public interface ConstantPool {
|
|||||||
*/
|
*/
|
||||||
void loadReferencedType(int cpi, int opcode);
|
void loadReferencedType(int cpi, int opcode);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Looks up the type referenced by the constant pool entry at {@code cpi} as referenced by the
|
||||||
|
* {@code opcode} bytecode instruction.
|
||||||
|
*
|
||||||
|
* @param cpi the index of a constant pool entry that references a type
|
||||||
|
* @param opcode the opcode of the instruction with {@code cpi} as an operand
|
||||||
|
* @return a reference to the compiler interface type
|
||||||
|
*/
|
||||||
|
JavaType lookupReferencedType(int cpi, int opcode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up a reference to a field. If {@code opcode} is non-negative, then resolution checks
|
* Looks up a reference to a field. If {@code opcode} is non-negative, then resolution checks
|
||||||
* specific to the bytecode it denotes are performed if the field is already resolved. Checks
|
* specific to the bytecode it denotes are performed if the field is already resolved. Checks
|
||||||
|
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, 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 org.graalvm.compiler.core.test;
|
||||||
|
|
||||||
|
import jdk.vm.ci.meta.ResolvedJavaType;
|
||||||
|
import org.graalvm.compiler.nodes.CallTargetNode;
|
||||||
|
import org.graalvm.compiler.nodes.InvokeNode;
|
||||||
|
import org.graalvm.compiler.nodes.StructuredGraph;
|
||||||
|
import org.graalvm.compiler.nodes.java.InstanceOfNode;
|
||||||
|
import org.graalvm.compiler.phases.OptimisticOptimizations;
|
||||||
|
import org.graalvm.compiler.phases.tiers.HighTierContext;
|
||||||
|
import org.graalvm.compiler.serviceprovider.GraalServices;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class SingleImplementorInterfaceTest extends GraalCompilerTest {
|
||||||
|
|
||||||
|
public interface Interface0 {
|
||||||
|
void interfaceMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Interface1 extends Interface0 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Interface2 extends Interface1 {
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
public static class SingleImplementor1 implements Interface1 {
|
||||||
|
public void interfaceMethod() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Requires that the CHA analysis starts from the referenced type. Since {@code
|
||||||
|
// SingleImplementor1}
|
||||||
|
// is not a single implementor of {@code Interface2} devirtualization shouldn't happen.
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private static void singleImplementorInterfaceSnippet1(Interface2 i) {
|
||||||
|
i.interfaceMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Devirtualization should happen in this case.
|
||||||
|
@SuppressWarnings("all")
|
||||||
|
private static void singleImplementorInterfaceSnippet2(Interface1 i) {
|
||||||
|
i.interfaceMethod();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleImplementorInterfaceDevirtualization1() {
|
||||||
|
ResolvedJavaType singleImplementorType = getMetaAccess().lookupJavaType(SingleImplementor1.class);
|
||||||
|
ResolvedJavaType expectedReferencedType = getMetaAccess().lookupJavaType(Interface2.class);
|
||||||
|
singleImplementorType.initialize();
|
||||||
|
StructuredGraph graph = parseEager("singleImplementorInterfaceSnippet1", StructuredGraph.AllowAssumptions.YES);
|
||||||
|
createCanonicalizerPhase().apply(graph, getProviders());
|
||||||
|
// Devirtualization shouldn't work in this case. The invoke should remain intact.
|
||||||
|
InvokeNode invoke = graph.getNodes().filter(InvokeNode.class).first();
|
||||||
|
assertTrue(invoke != null, "Should have an invoke");
|
||||||
|
assertTrue(invoke.callTarget().invokeKind() == CallTargetNode.InvokeKind.Interface, "Should still be an interface call");
|
||||||
|
if (GraalServices.hasLookupReferencedType()) {
|
||||||
|
assertTrue(invoke.callTarget().referencedType() != null, "Invoke should have a reference class set");
|
||||||
|
assertTrue(invoke.callTarget().referencedType().equals(expectedReferencedType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleImplementorInterfaceDevirtualization2() {
|
||||||
|
ResolvedJavaType singleImplementorType = getMetaAccess().lookupJavaType(SingleImplementor1.class);
|
||||||
|
singleImplementorType.initialize();
|
||||||
|
StructuredGraph graph = parseEager("singleImplementorInterfaceSnippet2", StructuredGraph.AllowAssumptions.YES);
|
||||||
|
createCanonicalizerPhase().apply(graph, getProviders());
|
||||||
|
InvokeNode invoke = graph.getNodes().filter(InvokeNode.class).first();
|
||||||
|
assertTrue(invoke != null, "Should have an invoke");
|
||||||
|
if (GraalServices.hasLookupReferencedType()) {
|
||||||
|
assertTrue(invoke.callTarget().invokeKind() == CallTargetNode.InvokeKind.Special, "Should be devirtualized");
|
||||||
|
InstanceOfNode instanceOfNode = graph.getNodes().filter(InstanceOfNode.class).first();
|
||||||
|
assertTrue(instanceOfNode != null, "Missing the subtype check");
|
||||||
|
assertTrue(instanceOfNode.getCheckedStamp().type().equals(singleImplementorType), "Checking against a wrong type");
|
||||||
|
} else {
|
||||||
|
assertTrue(invoke.callTarget().invokeKind() == CallTargetNode.InvokeKind.Interface, "Should not be devirtualized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleImplementorInterfaceInlining1() {
|
||||||
|
ResolvedJavaType singleImplementorType = getMetaAccess().lookupJavaType(SingleImplementor1.class);
|
||||||
|
ResolvedJavaType expectedReferencedType = getMetaAccess().lookupJavaType(Interface2.class);
|
||||||
|
singleImplementorType.initialize();
|
||||||
|
StructuredGraph graph = parseEager("singleImplementorInterfaceSnippet1", StructuredGraph.AllowAssumptions.YES);
|
||||||
|
HighTierContext context = new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
|
||||||
|
createInliningPhase().apply(graph, context);
|
||||||
|
// Inlining shouldn't do anything
|
||||||
|
InvokeNode invoke = graph.getNodes().filter(InvokeNode.class).first();
|
||||||
|
assertTrue(invoke != null, "Should have an invoke");
|
||||||
|
if (GraalServices.hasLookupReferencedType()) {
|
||||||
|
assertTrue(invoke.callTarget().referencedType() != null, "Invoke should have a reference class set");
|
||||||
|
assertTrue(invoke.callTarget().invokeKind() == CallTargetNode.InvokeKind.Interface, "Should still be an interface call");
|
||||||
|
assertTrue(invoke.callTarget().referencedType().equals(expectedReferencedType));
|
||||||
|
} else {
|
||||||
|
assertTrue(invoke.callTarget().invokeKind() == CallTargetNode.InvokeKind.Interface, "Should not be devirtualized");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleImplementorInterfaceInlining2() {
|
||||||
|
ResolvedJavaType singleImplementorType = getMetaAccess().lookupJavaType(SingleImplementor1.class);
|
||||||
|
ResolvedJavaType expectedReferencedType = getMetaAccess().lookupJavaType(Interface1.class);
|
||||||
|
singleImplementorType.initialize();
|
||||||
|
StructuredGraph graph = parseEager("singleImplementorInterfaceSnippet2", StructuredGraph.AllowAssumptions.YES);
|
||||||
|
HighTierContext context = new HighTierContext(getProviders(), getDefaultGraphBuilderSuite(), OptimisticOptimizations.ALL);
|
||||||
|
createInliningPhase().apply(graph, context);
|
||||||
|
|
||||||
|
// Right now inlining will not do anything, but if it starts doing devirtualization of
|
||||||
|
// interface calls
|
||||||
|
// in the future there should be a subtype check.
|
||||||
|
InvokeNode invoke = graph.getNodes().filter(InvokeNode.class).first();
|
||||||
|
if (invoke != null) {
|
||||||
|
assertTrue(invoke.callTarget().invokeKind() == CallTargetNode.InvokeKind.Interface, "Should still be an interface call");
|
||||||
|
if (GraalServices.hasLookupReferencedType()) {
|
||||||
|
assertTrue(invoke.callTarget().referencedType() != null, "Invoke should have a reference class set");
|
||||||
|
assertTrue(invoke.callTarget().referencedType().equals(expectedReferencedType));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
InstanceOfNode instanceOfNode = graph.getNodes().filter(InstanceOfNode.class).first();
|
||||||
|
assertTrue(instanceOfNode != null, "Missing the subtype check");
|
||||||
|
assertTrue(instanceOfNode.getCheckedStamp().type().equals(singleImplementorType), "Checking against a wrong type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,7 @@ import org.graalvm.compiler.phases.OptimisticOptimizations;
|
|||||||
import org.graalvm.compiler.phases.PhaseSuite;
|
import org.graalvm.compiler.phases.PhaseSuite;
|
||||||
import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
|
import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
|
||||||
import org.graalvm.compiler.phases.tiers.HighTierContext;
|
import org.graalvm.compiler.phases.tiers.HighTierContext;
|
||||||
|
import org.graalvm.compiler.serviceprovider.GraalServices;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@ -182,7 +183,9 @@ public class InliningTest extends GraalCompilerTest {
|
|||||||
public void testClassHierarchyAnalysis() {
|
public void testClassHierarchyAnalysis() {
|
||||||
assertInlined(getGraph("invokeLeafClassMethodSnippet", false));
|
assertInlined(getGraph("invokeLeafClassMethodSnippet", false));
|
||||||
assertInlined(getGraph("invokeConcreteMethodSnippet", false));
|
assertInlined(getGraph("invokeConcreteMethodSnippet", false));
|
||||||
assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", false));
|
if (GraalServices.hasLookupReferencedType()) {
|
||||||
|
assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", false));
|
||||||
|
}
|
||||||
// assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", false));
|
// assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", false));
|
||||||
|
|
||||||
assertNotInlined(getGraph("invokeOverriddenPublicMethodSnippet", false));
|
assertNotInlined(getGraph("invokeOverriddenPublicMethodSnippet", false));
|
||||||
@ -194,7 +197,9 @@ public class InliningTest extends GraalCompilerTest {
|
|||||||
public void testClassHierarchyAnalysisIP() {
|
public void testClassHierarchyAnalysisIP() {
|
||||||
assertManyMethodInfopoints(assertInlined(getGraph("invokeLeafClassMethodSnippet", true)));
|
assertManyMethodInfopoints(assertInlined(getGraph("invokeLeafClassMethodSnippet", true)));
|
||||||
assertManyMethodInfopoints(assertInlined(getGraph("invokeConcreteMethodSnippet", true)));
|
assertManyMethodInfopoints(assertInlined(getGraph("invokeConcreteMethodSnippet", true)));
|
||||||
assertManyMethodInfopoints(assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", true)));
|
if (GraalServices.hasLookupReferencedType()) {
|
||||||
|
assertManyMethodInfopoints(assertInlined(getGraph("invokeSingleImplementorInterfaceSnippet", true)));
|
||||||
|
}
|
||||||
//@formatter:off
|
//@formatter:off
|
||||||
// assertInlineInfopoints(assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", true)));
|
// assertInlineInfopoints(assertInlined(getGraph("invokeConcreteInterfaceMethodSnippet", true)));
|
||||||
//@formatter:on
|
//@formatter:on
|
||||||
|
@ -432,6 +432,7 @@ import org.graalvm.compiler.nodes.util.GraphUtil;
|
|||||||
import org.graalvm.compiler.options.OptionValues;
|
import org.graalvm.compiler.options.OptionValues;
|
||||||
import org.graalvm.compiler.phases.OptimisticOptimizations;
|
import org.graalvm.compiler.phases.OptimisticOptimizations;
|
||||||
import org.graalvm.compiler.phases.util.ValueMergeUtil;
|
import org.graalvm.compiler.phases.util.ValueMergeUtil;
|
||||||
|
import org.graalvm.compiler.serviceprovider.GraalServices;
|
||||||
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
|
import org.graalvm.compiler.serviceprovider.JavaVersionUtil;
|
||||||
import jdk.internal.vm.compiler.word.LocationIdentity;
|
import jdk.internal.vm.compiler.word.LocationIdentity;
|
||||||
|
|
||||||
@ -1685,13 +1686,17 @@ public class BytecodeParser implements GraphBuilderContext {
|
|||||||
|
|
||||||
protected void genInvokeInterface(int cpi, int opcode) {
|
protected void genInvokeInterface(int cpi, int opcode) {
|
||||||
JavaMethod target = lookupMethod(cpi, opcode);
|
JavaMethod target = lookupMethod(cpi, opcode);
|
||||||
genInvokeInterface(target);
|
JavaType referencedType = lookupReferencedTypeInPool(cpi, opcode);
|
||||||
|
genInvokeInterface(referencedType, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void genInvokeInterface(JavaMethod target) {
|
protected void genInvokeInterface(JavaType referencedType, JavaMethod target) {
|
||||||
if (callTargetIsResolved(target)) {
|
if (callTargetIsResolved(target) && (referencedType == null || referencedType instanceof ResolvedJavaType)) {
|
||||||
ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(true));
|
ValueNode[] args = frameState.popArguments(target.getSignature().getParameterCount(true));
|
||||||
appendInvoke(InvokeKind.Interface, (ResolvedJavaMethod) target, args);
|
Invoke invoke = appendInvoke(InvokeKind.Interface, (ResolvedJavaMethod) target, args);
|
||||||
|
if (invoke != null) {
|
||||||
|
invoke.callTarget().setReferencedType((ResolvedJavaType) referencedType);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
handleUnresolvedInvoke(target, InvokeKind.Interface);
|
handleUnresolvedInvoke(target, InvokeKind.Interface);
|
||||||
}
|
}
|
||||||
@ -4311,6 +4316,16 @@ public class BytecodeParser implements GraphBuilderContext {
|
|||||||
return constantPool.lookupMethod(cpi, opcode);
|
return constantPool.lookupMethod(cpi, opcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected JavaType lookupReferencedTypeInPool(int cpi, int opcode) {
|
||||||
|
if (GraalServices.hasLookupReferencedType()) {
|
||||||
|
return GraalServices.lookupReferencedType(constantPool, cpi, opcode);
|
||||||
|
}
|
||||||
|
// Returning null means that we should not attempt using CHA to devirtualize or inline
|
||||||
|
// interface calls. This is a normal behavior if the JVMCI doesn't support
|
||||||
|
// {@code ConstantPool.lookupReferencedType()}.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected JavaField lookupField(int cpi, int opcode) {
|
protected JavaField lookupField(int cpi, int opcode) {
|
||||||
maybeEagerlyResolve(cpi, opcode);
|
maybeEagerlyResolve(cpi, opcode);
|
||||||
JavaField result = constantPool.lookupField(cpi, method, opcode);
|
JavaField result = constantPool.lookupField(cpi, method, opcode);
|
||||||
|
@ -79,6 +79,46 @@ public abstract class CallTargetNode extends ValueNode implements LIRLowerable {
|
|||||||
|
|
||||||
@Input protected NodeInputList<ValueNode> arguments;
|
@Input protected NodeInputList<ValueNode> arguments;
|
||||||
protected ResolvedJavaMethod targetMethod;
|
protected ResolvedJavaMethod targetMethod;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receiver type referenced at the interface call site.
|
||||||
|
*
|
||||||
|
* We need to distinguish the declaring type from the type referenced at the call site. We must
|
||||||
|
* use the referenced type as lower type bound when doing CHA since interface calls must throw
|
||||||
|
* exception if the receiver type is not a subtype of the reference type.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* interface I1 {
|
||||||
|
* void foo();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* interface I2 extends I1 {
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* void bar(I2 o) {
|
||||||
|
* o.foo();
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* Here at the call site the declaring type for {@code foo()} is {@code I1}, while the
|
||||||
|
* referenced type is {@code I2}. Only receivers of type {@code T} that is {@code T <: I2}
|
||||||
|
* should be allowed at the call site. If they are not - an exception should be thrown.
|
||||||
|
*
|
||||||
|
* Since the interface types are not verified, another way to think about this call site is to
|
||||||
|
* rewrite it as follows:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* void bar(Object o) {
|
||||||
|
* ((I2) o).foo();
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* So, in case the receiver is not a subtype of {@code I2} an exception is thrown.
|
||||||
|
*/
|
||||||
|
protected ResolvedJavaType referencedType;
|
||||||
|
|
||||||
protected InvokeKind invokeKind;
|
protected InvokeKind invokeKind;
|
||||||
protected final StampPair returnStamp;
|
protected final StampPair returnStamp;
|
||||||
|
|
||||||
@ -117,8 +157,8 @@ public abstract class CallTargetNode extends ValueNode implements LIRLowerable {
|
|||||||
// nop
|
// nop
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTargetMethod(ResolvedJavaMethod method) {
|
public void setTargetMethod(ResolvedJavaMethod targetMethod) {
|
||||||
targetMethod = method;
|
this.targetMethod = targetMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -130,6 +170,14 @@ public abstract class CallTargetNode extends ValueNode implements LIRLowerable {
|
|||||||
return targetMethod;
|
return targetMethod;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setReferencedType(ResolvedJavaType referencedType) {
|
||||||
|
this.referencedType = referencedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResolvedJavaType referencedType() {
|
||||||
|
return referencedType;
|
||||||
|
}
|
||||||
|
|
||||||
public InvokeKind invokeKind() {
|
public InvokeKind invokeKind() {
|
||||||
return invokeKind;
|
return invokeKind;
|
||||||
}
|
}
|
||||||
|
@ -180,37 +180,38 @@ public class MethodCallTargetNode extends CallTargetNode implements IterableNode
|
|||||||
Assumptions assumptions = graph().getAssumptions();
|
Assumptions assumptions = graph().getAssumptions();
|
||||||
/*
|
/*
|
||||||
* Even though we are not registering an assumption (see comment below), the optimization is
|
* Even though we are not registering an assumption (see comment below), the optimization is
|
||||||
* only valid when speculative optimizations are enabled.
|
* only valid when speculative optimizations are enabled. We need to check the invoke kind
|
||||||
|
* to avoid recursive simplification for virtual interface methods calls.
|
||||||
*/
|
*/
|
||||||
if (invokeKind().isIndirect() && invokeKind().isInterface() && assumptions != null) {
|
if (invokeKind().isInterface() && assumptions != null) {
|
||||||
|
|
||||||
// check if the type of the receiver can narrow the result
|
// check if the type of the receiver can narrow the result
|
||||||
ValueNode receiver = receiver();
|
ValueNode receiver = receiver();
|
||||||
|
|
||||||
// try to turn a interface call into a virtual call
|
// try to turn a interface call into a virtual call
|
||||||
ResolvedJavaType declaredReceiverType = targetMethod().getDeclaringClass();
|
ResolvedJavaType declaredReceiverType = targetMethod().getDeclaringClass();
|
||||||
|
ResolvedJavaType referencedReceiverType = referencedType();
|
||||||
/*
|
if (referencedReceiverType != null) {
|
||||||
* We need to check the invoke kind to avoid recursive simplification for virtual
|
if (declaredReceiverType.isInterface()) {
|
||||||
* interface methods calls.
|
ResolvedJavaType singleImplementor = referencedReceiverType.getSingleImplementor();
|
||||||
*/
|
// If singleImplementor is equal to declaredReceiverType it means that there are
|
||||||
if (declaredReceiverType.isInterface()) {
|
// multiple implementors.
|
||||||
ResolvedJavaType singleImplementor = declaredReceiverType.getSingleImplementor();
|
if (singleImplementor != null && !singleImplementor.equals(declaredReceiverType)) {
|
||||||
if (singleImplementor != null && !singleImplementor.equals(declaredReceiverType)) {
|
TypeReference speculatedType = TypeReference.createTrusted(assumptions, singleImplementor);
|
||||||
TypeReference speculatedType = TypeReference.createTrusted(assumptions, singleImplementor);
|
if (tryCheckCastSingleImplementor(receiver, speculatedType)) {
|
||||||
if (tryCheckCastSingleImplementor(receiver, speculatedType)) {
|
return;
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (receiver instanceof UncheckedInterfaceProvider) {
|
if (receiver instanceof UncheckedInterfaceProvider) {
|
||||||
UncheckedInterfaceProvider uncheckedInterfaceProvider = (UncheckedInterfaceProvider) receiver;
|
UncheckedInterfaceProvider uncheckedInterfaceProvider = (UncheckedInterfaceProvider) receiver;
|
||||||
Stamp uncheckedStamp = uncheckedInterfaceProvider.uncheckedStamp();
|
Stamp uncheckedStamp = uncheckedInterfaceProvider.uncheckedStamp();
|
||||||
if (uncheckedStamp != null) {
|
if (uncheckedStamp != null) {
|
||||||
TypeReference speculatedType = StampTool.typeReferenceOrNull(uncheckedStamp);
|
TypeReference speculatedType = StampTool.typeReferenceOrNull(uncheckedStamp);
|
||||||
if (speculatedType != null) {
|
// speculatedType must be related to the referencedReceiverType.
|
||||||
tryCheckCastSingleImplementor(receiver, speculatedType);
|
if (speculatedType != null && referencedReceiverType.isAssignableFrom(speculatedType.getType())) {
|
||||||
|
tryCheckCastSingleImplementor(receiver, speculatedType);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -227,7 +228,7 @@ public class MethodCallTargetNode extends CallTargetNode implements IterableNode
|
|||||||
* with an invoke virtual.
|
* with an invoke virtual.
|
||||||
*
|
*
|
||||||
* To do so we need to ensure two properties: 1) the receiver must implement the
|
* To do so we need to ensure two properties: 1) the receiver must implement the
|
||||||
* interface (declaredReceiverType). The verifier does not prove this so we need a
|
* interface (referencedReceiverType). The verifier does not prove this so we need a
|
||||||
* dynamic check. 2) we need to ensure that there is still only one implementor of
|
* dynamic check. 2) we need to ensure that there is still only one implementor of
|
||||||
* this interface, i.e. that we are calling the right method. We could do this with
|
* this interface, i.e. that we are calling the right method. We could do this with
|
||||||
* an assumption but as we need an instanceof check anyway we can verify both
|
* an assumption but as we need an instanceof check anyway we can verify both
|
||||||
|
@ -235,17 +235,19 @@ public class InliningData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssumptionResult<ResolvedJavaType> leafConcreteSubtype = holder.findLeafConcreteSubtype();
|
if (invokeKind != InvokeKind.Interface) {
|
||||||
if (leafConcreteSubtype != null) {
|
AssumptionResult<ResolvedJavaType> leafConcreteSubtype = holder.findLeafConcreteSubtype();
|
||||||
ResolvedJavaMethod resolvedMethod = leafConcreteSubtype.getResult().resolveConcreteMethod(targetMethod, contextType);
|
if (leafConcreteSubtype != null) {
|
||||||
if (resolvedMethod != null && leafConcreteSubtype.canRecordTo(callTarget.graph().getAssumptions())) {
|
ResolvedJavaMethod resolvedMethod = leafConcreteSubtype.getResult().resolveConcreteMethod(targetMethod, contextType);
|
||||||
return getAssumptionInlineInfo(invoke, resolvedMethod, leafConcreteSubtype);
|
if (resolvedMethod != null && leafConcreteSubtype.canRecordTo(callTarget.graph().getAssumptions())) {
|
||||||
|
return getAssumptionInlineInfo(invoke, resolvedMethod, leafConcreteSubtype);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
AssumptionResult<ResolvedJavaMethod> concrete = holder.findUniqueConcreteMethod(targetMethod);
|
AssumptionResult<ResolvedJavaMethod> concrete = holder.findUniqueConcreteMethod(targetMethod);
|
||||||
if (concrete != null && concrete.canRecordTo(callTarget.graph().getAssumptions())) {
|
if (concrete != null && concrete.canRecordTo(callTarget.graph().getAssumptions())) {
|
||||||
return getAssumptionInlineInfo(invoke, concrete.getResult(), concrete);
|
return getAssumptionInlineInfo(invoke, concrete.getResult(), concrete);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// type check based inlining
|
// type check based inlining
|
||||||
|
@ -45,12 +45,15 @@ import jdk.vm.ci.meta.JavaType;
|
|||||||
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
import jdk.vm.ci.meta.ResolvedJavaMethod;
|
||||||
import jdk.vm.ci.meta.Signature;
|
import jdk.vm.ci.meta.Signature;
|
||||||
|
|
||||||
class ClassfileConstantPool implements ConstantPool {
|
class ClassfileConstantPool implements ConstantPool, ConstantPoolPatch {
|
||||||
|
|
||||||
final ClassfileConstant[] entries;
|
final ClassfileConstant[] entries;
|
||||||
final ClassfileBytecodeProvider context;
|
final ClassfileBytecodeProvider context;
|
||||||
|
|
||||||
public static class Bytecodes {
|
public static class Bytecodes {
|
||||||
|
public static final int LDC = 18; // 0x12
|
||||||
|
public static final int LDC_W = 19; // 0x13
|
||||||
|
public static final int LDC2_W = 20; // 0x14
|
||||||
public static final int GETSTATIC = 178; // 0xB2
|
public static final int GETSTATIC = 178; // 0xB2
|
||||||
public static final int PUTSTATIC = 179; // 0xB3
|
public static final int PUTSTATIC = 179; // 0xB3
|
||||||
public static final int GETFIELD = 180; // 0xB4
|
public static final int GETFIELD = 180; // 0xB4
|
||||||
@ -60,6 +63,12 @@ class ClassfileConstantPool implements ConstantPool {
|
|||||||
public static final int INVOKESTATIC = 184; // 0xB8
|
public static final int INVOKESTATIC = 184; // 0xB8
|
||||||
public static final int INVOKEINTERFACE = 185; // 0xB9
|
public static final int INVOKEINTERFACE = 185; // 0xB9
|
||||||
public static final int INVOKEDYNAMIC = 186; // 0xBA
|
public static final int INVOKEDYNAMIC = 186; // 0xBA
|
||||||
|
public static final int NEW = 187; // 0xBB
|
||||||
|
public static final int NEWARRAY = 188; // 0xBC
|
||||||
|
public static final int ANEWARRAY = 189; // 0xBD
|
||||||
|
public static final int CHECKCAST = 192; // 0xC0
|
||||||
|
public static final int INSTANCEOF = 193; // 0xC1
|
||||||
|
public static final int MULTIANEWARRAY = 197; // 0xC5
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassfileConstantPool(DataInputStream stream, ClassfileBytecodeProvider context) throws IOException {
|
ClassfileConstantPool(DataInputStream stream, ClassfileBytecodeProvider context) throws IOException {
|
||||||
@ -160,6 +169,35 @@ class ClassfileConstantPool implements ConstantPool {
|
|||||||
return get(ClassRef.class, index).resolve(this);
|
return get(ClassRef.class, index).resolve(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JavaType lookupReferencedType(int index, int opcode) {
|
||||||
|
switch (opcode) {
|
||||||
|
case Bytecodes.CHECKCAST:
|
||||||
|
case Bytecodes.INSTANCEOF:
|
||||||
|
case Bytecodes.NEW:
|
||||||
|
case Bytecodes.ANEWARRAY:
|
||||||
|
case Bytecodes.MULTIANEWARRAY:
|
||||||
|
case Bytecodes.LDC:
|
||||||
|
case Bytecodes.LDC_W:
|
||||||
|
case Bytecodes.LDC2_W:
|
||||||
|
return get(ClassRef.class, index).resolve(this);
|
||||||
|
case Bytecodes.GETSTATIC:
|
||||||
|
case Bytecodes.PUTSTATIC:
|
||||||
|
case Bytecodes.GETFIELD:
|
||||||
|
case Bytecodes.PUTFIELD:
|
||||||
|
FieldRef f = get(FieldRef.class, index);
|
||||||
|
return get(ClassRef.class, f.classIndex).resolve(this);
|
||||||
|
case Bytecodes.INVOKEVIRTUAL:
|
||||||
|
case Bytecodes.INVOKESPECIAL:
|
||||||
|
case Bytecodes.INVOKESTATIC:
|
||||||
|
case Bytecodes.INVOKEINTERFACE:
|
||||||
|
ExecutableRef e = get(ExecutableRef.class, index);
|
||||||
|
return get(ClassRef.class, e.classIndex).resolve(this);
|
||||||
|
default:
|
||||||
|
throw GraalError.shouldNotReachHere("Unexpected opcode: " + opcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String lookupUtf8(int index) {
|
public String lookupUtf8(int index) {
|
||||||
return ((Utf8) entries[index]).value;
|
return ((Utf8) entries[index]).value;
|
||||||
|
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, 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 org.graalvm.compiler.replacements.classfile;
|
||||||
|
|
||||||
|
import jdk.vm.ci.meta.JavaType;
|
||||||
|
|
||||||
|
public interface ConstantPoolPatch {
|
||||||
|
JavaType lookupReferencedType(int index, int opcode);
|
||||||
|
}
|
@ -28,6 +28,7 @@ import static java.lang.Thread.currentThread;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -39,6 +40,8 @@ import java.util.ServiceLoader;
|
|||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import jdk.vm.ci.meta.ConstantPool;
|
||||||
|
import jdk.vm.ci.meta.JavaType;
|
||||||
import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup.SpeculationContextObject;
|
import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup.SpeculationContextObject;
|
||||||
|
|
||||||
import jdk.vm.ci.code.BytecodePosition;
|
import jdk.vm.ci.code.BytecodePosition;
|
||||||
@ -558,4 +561,33 @@ public final class GraalServices {
|
|||||||
public static int getJavaUpdateVersion() {
|
public static int getJavaUpdateVersion() {
|
||||||
return Runtime.version().update();
|
return Runtime.version().update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Method constantPoolLookupReferencedType;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Method lookupReferencedType = null;
|
||||||
|
Class<?> constantPool = ConstantPool.class;
|
||||||
|
try {
|
||||||
|
lookupReferencedType = constantPool.getDeclaredMethod("lookupReferencedType", Integer.TYPE, Integer.TYPE);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
}
|
||||||
|
constantPoolLookupReferencedType = lookupReferencedType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static JavaType lookupReferencedType(ConstantPool constantPool, int cpi, int opcode) {
|
||||||
|
if (constantPoolLookupReferencedType != null) {
|
||||||
|
try {
|
||||||
|
return (JavaType) constantPoolLookupReferencedType.invoke(constantPool, cpi, opcode);
|
||||||
|
} catch (Error e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Throwable throwable) {
|
||||||
|
throw new InternalError(throwable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new InternalError("This JVMCI version doesn't support ConstantPool.lookupReferencedType()");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean hasLookupReferencedType() {
|
||||||
|
return constantPoolLookupReferencedType != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user