Skip to content

Commit

Permalink
[GR-57119] Espresso: Unify resolution paths into a LinkResolver class.
Browse files Browse the repository at this point in the history
PullRequest: graal/18865
  • Loading branch information
rakachan committed Sep 30, 2024
2 parents 2a99d81 + 99eb05e commit a8efbf7
Show file tree
Hide file tree
Showing 14 changed files with 735 additions and 433 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import com.oracle.truffle.espresso.meta.EspressoError;
import com.oracle.truffle.espresso.meta.Meta;
import com.oracle.truffle.espresso.nodes.methodhandle.MHInvokeGenericNode;
import com.oracle.truffle.espresso.resolver.LinkResolver;
import com.oracle.truffle.espresso.runtime.EspressoContext;

public interface ClassMethodRefConstant extends MethodRefConstant {
Expand All @@ -50,10 +51,6 @@ default Tag tag() {
return Tag.METHOD_REF;
}

default MHInvokeGenericNode.MethodHandleInvoker invoker() {
return null;
}

final class Indexes extends MethodRefConstant.Indexes implements ClassMethodRefConstant, Resolvable {
Indexes(int classIndex, int nameAndTypeIndex) {
super(classIndex, nameAndTypeIndex);
Expand Down Expand Up @@ -172,28 +169,16 @@ final class Indexes extends MethodRefConstant.Indexes implements ClassMethodRefC
@Override
public ResolvedConstant resolve(RuntimeConstantPool pool, int thisIndex, ObjectKlass accessingKlass) {
METHODREF_RESOLVE_COUNT.inc();

EspressoContext context = pool.getContext();
Klass holderKlass = getResolvedHolderKlass(accessingKlass, pool);

Meta meta = context.getMeta();
if (holderKlass.isInterface()) {
throw meta.throwExceptionWithMessage(meta.java_lang_IncompatibleClassChangeError, meta.toGuestString(getName(pool)));
}

Klass holderKlass = getResolvedHolderKlass(accessingKlass, pool);
Symbol<Name> name = getName(pool);
Symbol<Signature> signature = getSignature(pool);

Method method = holderKlass.lookupMethod(name, signature);
if (method == null) {
throw meta.throwExceptionWithMessage(meta.java_lang_NoSuchMethodError, meta.toGuestString(holderKlass.getNameAsString() + "." + getName(pool) + signature));
}

MemberRefConstant.doAccessCheck(accessingKlass, holderKlass, method, meta);
Method method = LinkResolver.resolveSymbol(meta, accessingKlass, name, signature, holderKlass, false, true, true);

if (!method.isPolySignatureIntrinsic()) {
method.checkLoadingConstraints(accessingKlass.getDefiningClassLoader(), method.getDeclaringKlass().getDefiningClassLoader());
} else if (method.isInvokeIntrinsic()) {
if (method.isInvokeIntrinsic()) {
MHInvokeGenericNode.MethodHandleInvoker invoker = MHInvokeGenericNode.linkMethod(meta.getLanguage(), meta, accessingKlass, method, name, signature);
return new ResolvedWithInvoker(method, invoker);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@
import com.oracle.truffle.espresso.impl.Method;
import com.oracle.truffle.espresso.impl.ObjectKlass;
import com.oracle.truffle.espresso.meta.EspressoError;
import com.oracle.truffle.espresso.meta.Meta;
import com.oracle.truffle.espresso.runtime.EspressoContext;
import com.oracle.truffle.espresso.resolver.LinkResolver;

public interface InterfaceMethodRefConstant extends MethodRefConstant {

Expand Down Expand Up @@ -110,30 +109,12 @@ final class Indexes extends MethodRefConstant.Indexes implements InterfaceMethod
@Override
public ResolvedConstant resolve(RuntimeConstantPool pool, int thisIndex, ObjectKlass accessingKlass) {
METHODREF_RESOLVE_COUNT.inc();
EspressoContext context = pool.getContext();
Meta meta = context.getMeta();

Klass holderInterface = getResolvedHolderKlass(accessingKlass, pool);

Symbol<Name> name = getName(pool);

// 1. If C is not an interface, interface method resolution throws an
// IncompatibleClassChangeError.
if (!holderInterface.isInterface()) {
throw meta.throwExceptionWithMessage(meta.java_lang_IncompatibleClassChangeError, "Found class " + holderInterface.getExternalName() + ", but interface was expected");
}

Symbol<Signature> signature = getSignature(pool);

Method method = ((ObjectKlass) holderInterface).resolveInterfaceMethod(name, signature);

if (method == null) {
throw meta.throwExceptionWithMessage(meta.java_lang_NoSuchMethodError, meta.toGuestString(name));
}

MemberRefConstant.doAccessCheck(accessingKlass, holderInterface, method, meta);

method.checkLoadingConstraints(accessingKlass.getDefiningClassLoader(), method.getDeclaringKlass().getDefiningClassLoader());
Method method = LinkResolver.resolveSymbol(pool.getContext().getMeta(), accessingKlass, name, signature, holderInterface, true, true, true);

return new Resolved(method);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import com.oracle.truffle.espresso.classfile.ConstantPool;
import com.oracle.truffle.espresso.descriptors.Symbol;
import com.oracle.truffle.espresso.descriptors.Symbol.Signature;
import com.oracle.truffle.espresso.nodes.methodhandle.MHInvokeGenericNode;
import com.oracle.truffle.espresso.perf.DebugCounter;

public interface MethodRefConstant extends MemberRefConstant {
Expand All @@ -41,6 +42,10 @@ default Symbol<Signature> getSignature(ConstantPool pool) {
return (Symbol<Signature>) getDescriptor(pool);
}

default MHInvokeGenericNode.MethodHandleInvoker invoker() {
return null;
}

abstract class Indexes extends MemberRefConstant.Indexes implements MethodRefConstant {
Indexes(int classIndex, int nameAndTypeIndex) {
super(classIndex, nameAndTypeIndex);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,6 @@
import com.oracle.truffle.espresso.classfile.attributes.BootstrapMethodsAttribute;
import com.oracle.truffle.espresso.classfile.attributes.LineNumberTableAttribute;
import com.oracle.truffle.espresso.classfile.constantpool.ClassConstant;
import com.oracle.truffle.espresso.classfile.constantpool.ClassMethodRefConstant;
import com.oracle.truffle.espresso.classfile.constantpool.DoubleConstant;
import com.oracle.truffle.espresso.classfile.constantpool.DynamicConstant;
import com.oracle.truffle.espresso.classfile.constantpool.FloatConstant;
Expand Down Expand Up @@ -376,6 +375,10 @@
import com.oracle.truffle.espresso.nodes.quick.invoke.InvokeVirtualQuickNode;
import com.oracle.truffle.espresso.nodes.quick.invoke.inline.InlinedMethodNode;
import com.oracle.truffle.espresso.perf.DebugCounter;
import com.oracle.truffle.espresso.resolver.CallKind;
import com.oracle.truffle.espresso.resolver.CallSiteType;
import com.oracle.truffle.espresso.resolver.LinkResolver;
import com.oracle.truffle.espresso.resolver.ResolvedCall;
import com.oracle.truffle.espresso.runtime.EspressoContext;
import com.oracle.truffle.espresso.runtime.EspressoException;
import com.oracle.truffle.espresso.runtime.EspressoExitException;
Expand Down Expand Up @@ -2394,141 +2397,38 @@ private int quickenArrayStore(final VirtualFrame frame, int top, int curBCI, int
// endregion quickenForeign

private InvokeQuickNode dispatchQuickened(int top, int curBCI, char cpi, int opcode, int statementIndex, Method resolutionSeed, boolean allowBytecodeInlining) {
Method resolved = resolutionSeed;
int resolvedOpCode = opcode;
switch (opcode) {
case INVOKESTATIC:
// Otherwise, if the resolved method is an instance method, the invokestatic
// instruction throws an IncompatibleClassChangeError.
if (!resolved.isStatic()) {
enterLinkageExceptionProfile();
throw throwBoundary(getMethod().getMeta().java_lang_IncompatibleClassChangeError, "Expected static method '%s.%s%s'",
resolved.getDeclaringKlass().getName(),
resolved.getName(),
resolved.getRawSignature());
}
break;
case INVOKEINTERFACE:
// Otherwise, if the resolved method is static or (jdk8 or earlier) private, the
// invokeinterface instruction throws an IncompatibleClassChangeError.
if (resolved.isStatic() ||
(getMethod().getContext().getJavaVersion().java8OrEarlier() && resolved.isPrivate())) {
enterLinkageExceptionProfile();
throw throwBoundary(getMethod().getMeta().java_lang_IncompatibleClassChangeError, "Expected instance method '%s.%s%s'",
resolved.getDeclaringKlass().getName(),
resolved.getName(),
resolved.getRawSignature());
}
if (resolved.getITableIndex() < 0) {
if (resolved.isPrivate()) {
assert getJavaVersion().java9OrLater();
// Interface private methods do not appear in itables.
resolvedOpCode = INVOKESPECIAL;
} else {
// Can happen in old classfiles that calls j.l.Object on interfaces.
resolvedOpCode = INVOKEVIRTUAL;
}
}
break;
case INVOKEVIRTUAL:
// Otherwise, if the resolved method is a class (static) method, the invokevirtual
// instruction throws an IncompatibleClassChangeError.
if (resolved.isStatic()) {
enterLinkageExceptionProfile();
throw throwBoundary(getMethod().getMeta().java_lang_IncompatibleClassChangeError, "Expected instance not static method '%s.%s%s'",
resolved.getDeclaringKlass().getName(),
resolved.getName(),
resolved.getRawSignature());
}
if (resolved.isFinalFlagSet() || resolved.getDeclaringKlass().isFinalFlagSet() || resolved.isPrivate()) {
resolvedOpCode = INVOKESPECIAL;
}
break;
case INVOKESPECIAL:
// Otherwise, if the resolved method is an instance initialization method, and the
// class in which it is declared is not the class symbolically referenced by the
// instruction, a NoSuchMethodError is thrown.
if (resolved.isConstructor()) {
if (resolved.getDeclaringKlass().getName() != getConstantPool().methodAt(cpi).getHolderKlassName(getConstantPool())) {
enterLinkageExceptionProfile();
throw throwBoundary(getMethod().getMeta().java_lang_NoSuchMethodError,
"%s.%s%s",
resolved.getDeclaringKlass().getName(),
resolved.getName(),
resolved.getRawSignature());
}
}
// Otherwise, if the resolved method is a class (static) method, the invokespecial
// instruction throws an IncompatibleClassChangeError.
if (resolved.isStatic()) {
enterLinkageExceptionProfile();
throw throwBoundary(getMethod().getMeta().java_lang_IncompatibleClassChangeError, "Expected instance not static method '%s.%s%s'",
resolved.getDeclaringKlass().getName(),
resolved.getName(),
resolved.getRawSignature());
}
// If all of the following are true, let C be the direct superclass of the current
// class:
//
// * The resolved method is not an instance initialization method (&sect;2.9).
//
// * If the symbolic reference names a class (not an interface), then that class is
// a superclass of the current class.
//
// * The ACC_SUPER flag is set for the class file (&sect;4.1). In Java SE 8 and
// above, the Java Virtual Machine considers the ACC_SUPER flag to be set in every
// class file, regardless of the actual value of the flag in the class file and the
// version of the class file.
if (!resolved.isConstructor()) {
ObjectKlass declaringKlass = getMethod().getDeclaringKlass();
Klass symbolicRef = ((MethodRefConstant.Indexes) getConstantPool().methodAt(cpi)).getResolvedHolderKlass(declaringKlass, getConstantPool());
if (!symbolicRef.isInterface() &&
symbolicRef != declaringKlass &&
declaringKlass.getSuperKlass() != null &&
symbolicRef != declaringKlass.getSuperKlass() &&
symbolicRef.isAssignableFrom(declaringKlass)) {
resolved = declaringKlass.getSuperKlass().lookupMethod(resolved.getName(), resolved.getRawSignature(), Klass.LookupMode.INSTANCE_ONLY);
}
}
break;
default:
CompilerDirectives.transferToInterpreterAndInvalidate();
throw EspressoError.unimplemented("Quickening for " + Bytecodes.nameOf(opcode));
}

Klass symbolicRef = ((MethodRefConstant.Indexes) getConstantPool().methodAt(cpi)).getResolvedHolderKlass(getDeclaringKlass(), getConstantPool());
ResolvedCall resolvedCall = LinkResolver.resolveCallSite(getMeta(), getDeclaringKlass(), resolutionSeed, CallSiteType.fromOpCode(opcode), symbolicRef);

Method resolved = resolvedCall.getResolvedMethod();
CallKind callKind = resolvedCall.getCallKind();

// Skip inlined nodes if instrumentation is live.
// Lock must be owned for correctness.
assert lockIsHeld();
boolean tryBytecodeLevelInlining = this.instrumentation == null && allowBytecodeInlining;
if (tryBytecodeLevelInlining) {
var node = InlinedMethodNode.createFor(resolved, top, resolvedOpCode, curBCI, statementIndex);
InlinedMethodNode node = InlinedMethodNode.createFor(resolvedCall, top, opcode, curBCI, statementIndex);
if (node != null) {
return node;
}
}

InvokeQuickNode invoke;
if (resolved.isPolySignatureIntrinsic()) {
MethodHandleInvoker invoker = null;
if ((resolvedOpCode == INVOKEVIRTUAL || resolvedOpCode == INVOKESPECIAL) && (getConstantPool().resolvedMethodRefAt(getDeclaringKlass(), cpi) instanceof ClassMethodRefConstant methodRef)) {
// There might be an invoker if it's an InvokeGeneric
invoker = methodRef.invoker();
}
invoke = new InvokeHandleNode(resolved, invoker, top, curBCI);
MethodHandleInvoker invoker = getConstantPool().resolvedMethodRefAt(getDeclaringKlass(), cpi).invoker();
assert invoker == null || ((opcode == INVOKEVIRTUAL || opcode == INVOKESPECIAL) && resolved.isInvokeIntrinsic());
return new InvokeHandleNode(resolved, invoker, top, curBCI);
} else {
// @formatter:off
switch (resolvedOpCode) {
case INVOKESTATIC : invoke = new InvokeStaticQuickNode(resolved, top, curBCI); break;
case INVOKEINTERFACE : invoke = new InvokeInterfaceQuickNode(resolved, top, curBCI); break;
case INVOKEVIRTUAL : invoke = new InvokeVirtualQuickNode(resolved, top, curBCI); break;
case INVOKESPECIAL : invoke = new InvokeSpecialQuickNode(resolved, top, curBCI); break;
default :
CompilerDirectives.transferToInterpreterAndInvalidate();
throw EspressoError.unimplemented("Quickening for " + Bytecodes.nameOf(resolvedOpCode));
}
return switch (callKind) {
case STATIC -> new InvokeStaticQuickNode(resolved, top, curBCI);
case ITABLE_LOOKUP -> new InvokeInterfaceQuickNode(resolved, top, curBCI);
case VTABLE_LOOKUP -> new InvokeVirtualQuickNode(resolved, top, curBCI);
case DIRECT -> new InvokeSpecialQuickNode(resolved, top, curBCI);
};
// @formatter:on
}
return invoke;
}

@TruffleBoundary
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@

package com.oracle.truffle.espresso.nodes.quick.invoke.inline;

import static com.oracle.truffle.espresso.bytecode.Bytecodes.INVOKEINTERFACE;
import static com.oracle.truffle.espresso.bytecode.Bytecodes.INVOKESPECIAL;
import static com.oracle.truffle.espresso.bytecode.Bytecodes.INVOKESTATIC;
import static com.oracle.truffle.espresso.bytecode.Bytecodes.INVOKEVIRTUAL;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.espresso.impl.Method;
Expand All @@ -36,6 +31,7 @@
import com.oracle.truffle.espresso.nodes.quick.invoke.InvokeSpecialQuickNode;
import com.oracle.truffle.espresso.nodes.quick.invoke.InvokeStaticQuickNode;
import com.oracle.truffle.espresso.nodes.quick.invoke.InvokeVirtualQuickNode;
import com.oracle.truffle.espresso.resolver.ResolvedCall;

public class ConditionalInlinedMethodNode extends InlinedMethodNode {

Expand All @@ -50,9 +46,9 @@ public interface Recipes {
@Child protected InvokeQuickNode fallbackNode;
private final InlinedMethodPredicate condition;

public ConditionalInlinedMethodNode(Method.MethodVersion inlinedMethod, int top, int opcode, int callerBCI, int statementIndex, Recipes recipes, InlinedMethodPredicate condition) {
super(inlinedMethod, top, opcode, callerBCI, statementIndex, null);
this.fallbackNode = getFallback(inlinedMethod.getMethod(), top, callerBCI, opcode);
public ConditionalInlinedMethodNode(ResolvedCall resolvedCall, int top, int opcode, int callerBCI, int statementIndex, Recipes recipes, InlinedMethodPredicate condition) {
super(resolvedCall.getResolvedMethod().getMethodVersion(), top, opcode, callerBCI, statementIndex, null);
this.fallbackNode = getFallback(resolvedCall, top, callerBCI);
this.condition = condition;
this.recipes = recipes;
}
Expand Down Expand Up @@ -83,13 +79,13 @@ public static InlinedMethodNode getDefinitiveNode(Recipes recipes,
return replacement;
}

static InvokeQuickNode getFallback(Method inlinedMethod, int top, int callerBCI, int opcode) {
static InvokeQuickNode getFallback(ResolvedCall resolvedCall, int top, int callerBCI) {
// @formatter:off
switch (opcode) {
case INVOKESTATIC : return new InvokeStaticQuickNode(inlinedMethod, top, callerBCI);
case INVOKESPECIAL : return new InvokeSpecialQuickNode(inlinedMethod, top, callerBCI);
case INVOKEVIRTUAL : return new InvokeVirtualQuickNode(inlinedMethod, top, callerBCI);
case INVOKEINTERFACE : // fallback.
switch (resolvedCall.getCallKind()) {
case STATIC : return new InvokeStaticQuickNode(resolvedCall.getResolvedMethod(), top, callerBCI);
case DIRECT : return new InvokeSpecialQuickNode(resolvedCall.getResolvedMethod(), top, callerBCI);
case VTABLE_LOOKUP : return new InvokeVirtualQuickNode(resolvedCall.getResolvedMethod(), top, callerBCI);
case ITABLE_LOOKUP : // fallback.
default :
CompilerDirectives.transferToInterpreterAndInvalidate();
throw EspressoError.unimplemented("Conditional bytecode-level inlining only available for invokestatic, invokespecial and invokevirtual");
Expand Down
Loading

0 comments on commit a8efbf7

Please sign in to comment.