Skip to content

Commit 6f40847

Browse files
committed
[GR-73925] Support build-time initialization of downcall handles.
PullRequest: graal/23528
2 parents c31544f + 0ce355e commit 6f40847

File tree

25 files changed

+1093
-131
lines changed

25 files changed

+1093
-131
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/MethodHandlePlugin.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@
4141
import jdk.graal.compiler.replacements.nodes.MacroInvokable;
4242
import jdk.graal.compiler.replacements.nodes.MacroNode;
4343
import jdk.graal.compiler.replacements.nodes.MethodHandleNode;
44+
import jdk.graal.compiler.replacements.nodes.MethodHandleNode.GraphAdder;
45+
import jdk.graal.compiler.replacements.nodes.MethodHandleNode.InvokeFactory;
4446
import jdk.vm.ci.meta.JavaKind;
4547
import jdk.vm.ci.meta.MethodHandleAccessProvider;
4648
import jdk.vm.ci.meta.MethodHandleAccessProvider.IntrinsicMethod;
4749
import jdk.vm.ci.meta.ResolvedJavaMethod;
4850

4951
public class MethodHandlePlugin implements NodePlugin {
50-
private final MethodHandleAccessProvider methodHandleAccess;
52+
protected final MethodHandleAccessProvider methodHandleAccess;
5153
private final boolean safeForDeoptimization;
5254

5355
public MethodHandlePlugin(MethodHandleAccessProvider methodHandleAccess, boolean safeForDeoptimization) {
@@ -75,11 +77,7 @@ protected void onCreateHook(MacroInvokable methodHandleNode, GraphBuilderContext
7577
@Override
7678
public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
7779
IntrinsicMethod intrinsicMethod = methodHandleAccess.lookupMethodHandleIntrinsic(method);
78-
// We skip intrinsification for LINK_TO_NATIVE, because:
79-
// 1. HotSpot generates compiler entry jumping to the native wrapper of the target c method.
80-
// 2. SVM intrinsification is not yet implemented.
81-
// Use String comparison for JDK21 compatibility.
82-
if (intrinsicMethod != null && !"LINK_TO_NATIVE".equals(intrinsicMethod.name())) {
80+
if (intrinsicMethod != null && canHandleIntrinsicMethod(intrinsicMethod)) {
8381
InvokeKind invokeKind = b.getInvokeKind();
8482
if (invokeKind != InvokeKind.Static) {
8583
args[0] = b.nullCheckedValue(args[0]);
@@ -91,7 +89,7 @@ public <T extends ValueNode> T add(T node) {
9189
return b.add(node);
9290
}
9391
};
94-
Invoke invoke = MethodHandleNode.tryResolveTargetInvoke(adder, this::createInvoke, methodHandleAccess, intrinsicMethod, method, b.bci(), invokeReturnStamp, args);
92+
Invoke invoke = tryResolveTargetInvoke(adder, this::createInvoke, intrinsicMethod, method, b.bci(), invokeReturnStamp, args);
9593
if (invoke == null) {
9694
MacroInvokable methodHandleNode = createMethodHandleNode(b, method, args, intrinsicMethod, invokeKind, invokeReturnStamp);
9795
onCreateHook(methodHandleNode, b);
@@ -159,4 +157,18 @@ public <T extends ValueNode> T add(T node) {
159157
}
160158
return false;
161159
}
160+
161+
protected boolean canHandleIntrinsicMethod(IntrinsicMethod intrinsicMethod) {
162+
assert intrinsicMethod != null;
163+
/*
164+
* We skip intrinsification for LINK_TO_NATIVE because HotSpot generates compiler entry code
165+
* jumping to the native wrapper of the target C method.
166+
*/
167+
return intrinsicMethod != IntrinsicMethod.LINK_TO_NATIVE;
168+
}
169+
170+
protected <T extends Invoke> T tryResolveTargetInvoke(GraphAdder adder, InvokeFactory<T> factory, IntrinsicMethod intrinsicMethod,
171+
ResolvedJavaMethod original, int bci, StampPair returnStamp, ValueNode... arguments) {
172+
return MethodHandleNode.tryResolveTargetInvoke(adder, factory, methodHandleAccess, intrinsicMethod, original, bci, returnStamp, arguments);
173+
}
162174
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/MethodHandleWithExceptionPlugin.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
import jdk.graal.compiler.replacements.nodes.MacroInvokable;
3535
import jdk.graal.compiler.replacements.nodes.MacroNode;
3636
import jdk.graal.compiler.replacements.nodes.MethodHandleWithExceptionNode;
37-
3837
import jdk.vm.ci.meta.MethodHandleAccessProvider;
3938
import jdk.vm.ci.meta.ResolvedJavaMethod;
4039

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/nodes/MethodHandleNode.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ private static <T extends Invoke> T getTargetInvokeNode(GraphAdder adder, Invoke
302302
* @param index of the argument to be cast
303303
* @param type the type the argument should be cast to
304304
*/
305-
private static void maybeCastArgument(GraphAdder adder, ValueNode[] arguments, int index, JavaType type) {
305+
public static void maybeCastArgument(GraphAdder adder, ValueNode[] arguments, int index, JavaType type) {
306306
ValueNode argument = arguments[index];
307307
if (type instanceof ResolvedJavaType && !((ResolvedJavaType) type).isJavaLangObject()) {
308308
Assumptions assumptions = adder.getAssumptions();

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/nodes/MethodHandleWithExceptionNode.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -31,29 +31,35 @@
3131

3232
import jdk.graal.compiler.graph.NodeClass;
3333
import jdk.graal.compiler.nodeinfo.NodeInfo;
34+
import jdk.graal.compiler.nodes.Invoke;
3435
import jdk.graal.compiler.nodes.InvokeWithExceptionNode;
3536
import jdk.graal.compiler.nodes.ValueNode;
3637
import jdk.graal.compiler.nodes.WithExceptionNode;
3738
import jdk.graal.compiler.nodes.spi.Simplifiable;
3839
import jdk.graal.compiler.nodes.spi.SimplifierTool;
39-
40+
import jdk.graal.compiler.replacements.nodes.MethodHandleNode.GraphAdder;
41+
import jdk.graal.compiler.replacements.nodes.MethodHandleNode.InvokeFactory;
4042
import jdk.vm.ci.meta.MethodHandleAccessProvider;
4143
import jdk.vm.ci.meta.MethodHandleAccessProvider.IntrinsicMethod;
4244

4345
/**
4446
* Node for invocation methods defined on the class {@link MethodHandle}.
4547
*/
4648
@NodeInfo(cycles = CYCLES_UNKNOWN, cyclesRationale = "see MacroNode", size = SIZE_UNKNOWN, sizeRationale = "see MacroNode")
47-
public final class MethodHandleWithExceptionNode extends MacroWithExceptionNode implements Simplifiable {
49+
public class MethodHandleWithExceptionNode extends MacroWithExceptionNode implements Simplifiable {
4850
public static final NodeClass<MethodHandleWithExceptionNode> TYPE = NodeClass.create(MethodHandleWithExceptionNode.class);
4951

5052
protected final IntrinsicMethod intrinsicMethod;
5153

52-
public MethodHandleWithExceptionNode(IntrinsicMethod intrinsicMethod, MacroNode.MacroParams p) {
53-
super(TYPE, p);
54+
protected MethodHandleWithExceptionNode(NodeClass<? extends MacroWithExceptionNode> c, IntrinsicMethod intrinsicMethod, MacroNode.MacroParams p) {
55+
super(c, p);
5456
this.intrinsicMethod = intrinsicMethod;
5557
}
5658

59+
public MethodHandleWithExceptionNode(IntrinsicMethod intrinsicMethod, MacroNode.MacroParams p) {
60+
this(TYPE, intrinsicMethod, p);
61+
}
62+
5763
@Override
5864
public void simplify(SimplifierTool tool) {
5965
MethodHandleAccessProvider methodHandleAccess = tool.getConstantReflection().getMethodHandleAccess();
@@ -69,7 +75,7 @@ public WithExceptionNode trySimplify(MethodHandleAccessProvider methodHandleAcce
6975
invoke.setStamp(stmp);
7076
return invoke;
7177
};
72-
InvokeWithExceptionNode invoke = MethodHandleNode.tryResolveTargetInvoke(adder, invokeFactory, methodHandleAccess, intrinsicMethod, targetMethod, bci, returnStamp, argumentsArray);
78+
InvokeWithExceptionNode invoke = tryResolveTargetInvoke(adder, invokeFactory, methodHandleAccess, argumentsArray);
7379
if (invoke == null) {
7480
return this;
7581
}
@@ -80,4 +86,9 @@ public WithExceptionNode trySimplify(MethodHandleAccessProvider methodHandleAcce
8086
graph().replaceWithExceptionSplit(this, invoke);
8187
return invoke;
8288
}
89+
90+
/** {@link MethodHandleNode#tryResolveTargetInvoke}. */
91+
protected <T extends Invoke> T tryResolveTargetInvoke(GraphAdder adder, InvokeFactory<T> factory, MethodHandleAccessProvider methodHandleAccess, ValueNode... argumentsArray) {
92+
return MethodHandleNode.tryResolveTargetInvoke(adder, factory, methodHandleAccess, intrinsicMethod, targetMethod, bci, returnStamp, argumentsArray);
93+
}
8394
}

substratevm/mx.substratevm/suite.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -839,6 +839,7 @@
839839
"jdk.internal.foreign.layout",
840840
"jdk.internal.loader",
841841
"jdk.internal.reflect",
842+
"jdk.internal.vm.annotation",
842843
],
843844
"jdk.internal.vm.ci" : [
844845
"jdk.vm.ci.aarch64",
@@ -2843,7 +2844,8 @@
28432844
"org.graalvm.collections",
28442845
],
28452846
"exports" : [
2846-
"* to org.graalvm.nativeimage.builder"
2847+
"* to org.graalvm.nativeimage.builder",
2848+
"com.oracle.svm.hosted.foreign to jdk.graal.compiler"
28472849
],
28482850
"requiresConcealed": {
28492851
"jdk.internal.vm.ci" : [

substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2023, 2026, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -72,6 +72,7 @@
7272
import com.oracle.svm.core.snippets.SnippetRuntime;
7373
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
7474
import com.oracle.svm.core.util.ImageHeapMap;
75+
import com.oracle.svm.shared.AlwaysInline;
7576
import com.oracle.svm.shared.Uninterruptible;
7677
import com.oracle.svm.shared.singletons.traits.BuiltinTraits.AllAccess;
7778
import com.oracle.svm.shared.singletons.traits.BuiltinTraits.NoLayeredCallbacks;
@@ -102,6 +103,7 @@ public static ForeignFunctionsRuntime singleton() {
102103
private final AbiUtils.TrampolineTemplate trampolineTemplate;
103104

104105
private final EconomicMap<NativeEntryPointInfo, FunctionPointerHolder> downcallStubs = ImageHeapMap.create("downcallStubs");
106+
private final EconomicMap<MethodType, FunctionPointerHolder> downcallStubInvokers = ImageHeapMap.create("downcallStubInvokers");
105107
private final EconomicMap<Pair<DirectMethodHandleDesc, JavaEntryPointInfo>, FunctionPointerHolder> directUpcallStubs = ImageHeapMap.create("directUpcallStubs");
106108
private final EconomicMap<JavaEntryPointInfo, FunctionPointerHolder> upcallStubs = ImageHeapMap.create("upcallStubs");
107109
private final EconomicSet<ResolvedJavaType> neverAccessesSharedArenaTypes = EconomicSet.create();
@@ -161,6 +163,11 @@ public boolean downcallStubExists(NativeEntryPointInfo nep) {
161163
return downcallStubs.containsKey(nep);
162164
}
163165

166+
@Platforms(Platform.HOSTED_ONLY.class)
167+
public boolean downcallStubInvokerExists(MethodType methodType) {
168+
return downcallStubInvokers.containsKey(methodType);
169+
}
170+
164171
@Platforms(Platform.HOSTED_ONLY.class)
165172
public int getDowncallStubsCount() {
166173
return downcallStubs.size();
@@ -171,6 +178,11 @@ public boolean upcallStubExists(JavaEntryPointInfo jep) {
171178
return upcallStubs.containsKey(jep);
172179
}
173180

181+
@Platforms(Platform.HOSTED_ONLY.class)
182+
public boolean addDowncallStubInvokerPointer(MethodType methodType, CFunctionPointer ptr) {
183+
return downcallStubInvokers.putIfAbsent(methodType, new FunctionPointerHolder(ptr)) == null;
184+
}
185+
174186
@Platforms(Platform.HOSTED_ONLY.class)
175187
public int getUpcallStubsCount() {
176188
return upcallStubs.size();
@@ -212,14 +224,22 @@ public void registerSafeArenaAccessorMethod(ResolvedJavaMethod method) {
212224
neverAccessesSharedArenaMethods.add(method);
213225
}
214226

215-
CFunctionPointer getDowncallStubPointer(NativeEntryPointInfo nep) {
227+
public CFunctionPointer getDowncallStubPointer(NativeEntryPointInfo nep) {
216228
FunctionPointerHolder holder = downcallStubs.get(nep);
217229
if (holder == null) {
218230
throw reportMissingDowncall(nep);
219231
}
220232
return holder.functionPointer;
221233
}
222234

235+
CFunctionPointer getDowncallStubInvokerPointer(MethodType methodType) {
236+
FunctionPointerHolder holder = downcallStubInvokers.get(methodType);
237+
if (holder == null) {
238+
throw reportMissingDowncall(methodType);
239+
}
240+
return holder.functionPointer;
241+
}
242+
223243
CFunctionPointer getUpcallStubPointer(JavaEntryPointInfo jep) {
224244
FunctionPointerHolder holder = upcallStubs.get(jep);
225245
if (holder == null) {
@@ -307,6 +327,9 @@ void freeTrampoline(long addr) {
307327
private MissingForeignRegistrationError reportMissingDowncall(NativeEntryPointInfo nep) {
308328
LinkRequest currentLinkRequest = null;
309329
for (LinkRequest linkRequest : currentLinkRequests) {
330+
if (!Thread.currentThread().equals(linkRequest.requester)) {
331+
continue;
332+
}
310333
NativeEntryPointInfo nativeEntryPointInfo = abiUtils.makeNativeEntrypoint(linkRequest.functionDescriptor, linkRequest.linkerOptions);
311334
if (nep.equals(nativeEntryPointInfo)) {
312335
currentLinkRequest = linkRequest;
@@ -316,6 +339,21 @@ private MissingForeignRegistrationError reportMissingDowncall(NativeEntryPointIn
316339
throw MissingForeignRegistrationUtils.report(false, currentLinkRequest, nep.methodType());
317340
}
318341

342+
/**
343+
* Similar to {@link #reportMissingDowncall(NativeEntryPointInfo)} but only matches the
344+
* requested {@link MethodType}.
345+
*/
346+
private MissingForeignRegistrationError reportMissingDowncall(MethodType methodType) {
347+
LinkRequest currentLinkRequest = null;
348+
for (LinkRequest linkRequest : currentLinkRequests) {
349+
if (methodType.equals(linkRequest.functionDescriptor.toMethodType())) {
350+
currentLinkRequest = linkRequest;
351+
break;
352+
}
353+
}
354+
throw MissingForeignRegistrationUtils.report(false, currentLinkRequest, methodType);
355+
}
356+
319357
/**
320358
* Similar to {@link #reportMissingDowncall} but for upcalls.
321359
*/
@@ -352,10 +390,10 @@ private static MissingForeignRegistrationError report(boolean upcall, LinkReques
352390
"upcallStub"));
353391
}
354392

355-
record LinkRequest(boolean upcall, FunctionDescriptor functionDescriptor, LinkerOptions linkerOptions) implements AutoCloseable, JsonPrintable {
393+
record LinkRequest(boolean upcall, FunctionDescriptor functionDescriptor, LinkerOptions linkerOptions, Thread requester) implements AutoCloseable, JsonPrintable {
356394

357395
static LinkRequest create(boolean upcall, FunctionDescriptor functionDescriptor, LinkerOptions linkerOptions) {
358-
LinkRequest linkRequest = new LinkRequest(upcall, functionDescriptor, linkerOptions);
396+
LinkRequest linkRequest = new LinkRequest(upcall, functionDescriptor, linkerOptions, Thread.currentThread());
359397
ForeignFunctionsRuntime.singleton().currentLinkRequests.push(linkRequest);
360398
return linkRequest;
361399
}
@@ -399,9 +437,10 @@ public void printJson(JsonWriter writer) throws IOException {
399437
@Override
400438
public Object linkToNative(Object... args) throws Throwable {
401439
Target_jdk_internal_foreign_abi_NativeEntryPoint nep = (Target_jdk_internal_foreign_abi_NativeEntryPoint) args[args.length - 1];
402-
StubPointer pointer = Word.pointer(nep.downcallStubAddress);
403-
/* The nep argument will be dropped in the invoked function */
404-
return pointer.invoke(args);
440+
StubInvokerPointer invoker = (StubInvokerPointer) nep.downcallInvokerPointer;
441+
CFunctionPointer stub = nep.downcallStubPointer;
442+
/* The nep argument will be dropped in the invoked downcall stub */
443+
return invoker.invoke(stub, args);
405444
}
406445

407446
@Override
@@ -437,6 +476,12 @@ public void onScopeReachable(Object scopeObj, DisallowedObjectReporter reporter)
437476
}
438477
}
439478

479+
@AlwaysInline("method handle interpreter performance")
480+
@Override
481+
public MethodType getMethodTypeFromNativeEntryPoint(Object nativeEntryPoint) {
482+
return ((Target_jdk_internal_foreign_abi_NativeEntryPoint) nativeEntryPoint).type();
483+
}
484+
440485
/**
441486
* Workaround for CapturableState.maskFromName(String) being interruptible.
442487
*/
@@ -502,7 +547,8 @@ public boolean isSafeCallee(ResolvedJavaMethod method) {
502547
}
503548
}
504549

505-
interface StubPointer extends CFunctionPointer {
550+
/** Invoke interface for {@code com.oracle.svm.hosted.foreign.DowncallStubInvoker}. */
551+
interface StubInvokerPointer extends CFunctionPointer {
506552
@InvokeJavaFunctionPointer
507-
Object invoke(Object... args);
553+
Object invoke(CFunctionPointer downcallStub, Object... args);
508554
}

0 commit comments

Comments
 (0)