Skip to content

Commit

Permalink
[FFI/JDKnext] Perf improvement via NativeMethodHandle/linkToNative
Browse files Browse the repository at this point in the history
The changes aim to generate the NativeMethodHandle instance
to ensure the invocation path goes via linkToNative.

Signed-off-by: ChengJin01 <[email protected]>
  • Loading branch information
ChengJin01 committed Jan 19, 2024
1 parent 17235e2 commit d2ccdb2
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,14 @@

/*
* ===========================================================================
* (c) Copyright IBM Corp. 2022, 2022 All Rights Reserved
* (c) Copyright IBM Corp. 2022, 2023 All Rights Reserved
* ===========================================================================
*/

package java.lang.invoke;

import jdk.internal.access.JavaLangInvokeAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.abi.NativeEntryPoint;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
Expand Down Expand Up @@ -1580,7 +1579,7 @@ public VarHandle memorySegmentViewHandle(Class<?> carrier, long alignmentMask, B
}

@Override
public MethodHandle nativeMethodHandle(NativeEntryPoint nep) {
public MethodHandle nativeMethodHandle(MethodHandle nep) {
return NativeMethodHandle.make(nep);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,48 @@
* questions.
*/

/*
* ===========================================================================
* (c) Copyright IBM Corp. 2023, 2023 All Rights Reserved
* ===========================================================================
*/

package java.lang.invoke;

import jdk.internal.vm.annotation.ForceInline;
import jdk.internal.foreign.abi.NativeEntryPoint;

import java.lang.foreign.MemorySegment;
import java.lang.foreign.SegmentAllocator;

import static java.lang.invoke.LambdaForm.*;
import static java.lang.invoke.MethodHandleNatives.Constants.LM_TRUSTED;
import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
import static java.lang.invoke.MethodHandleStatics.newInternalError;

/**
* This class models a method handle to a native function. A native method handle is made up of a {@link NativeEntryPoint},
* This class models a method handle to a native function. A native method handle is made up of a bounded {@link MethodHandle},
* which is used to capture the characteristics of the native call (such as calling convention to be used,
* or whether a native transition is required) and a <em>fallback</em> method handle, which can be used
* when intrinsification of this method handle is not possible.
*/
/*non-public*/ final class NativeMethodHandle extends MethodHandle {
final NativeEntryPoint nep;
/* The native entry point is literally the bounded MethodHandle implemented in OpenJ9. */
final MethodHandle nep;

/* The cache array for the bounded MethodHandle stores MemberName and appendix which are populated
* by MethodHandleResolver.ffiCallLinkCallerMethod().
*/
private Object[] invokeCache;

private NativeMethodHandle(MethodType type, LambdaForm form, NativeEntryPoint nep) {
private NativeMethodHandle(MethodType type, LambdaForm form, MethodHandle nep) {
super(type, form);
this.nep = nep;
}

/**
* Creates a new native method handle with given {@link NativeEntryPoint} and <em>fallback</em> method handle.
* Creates a new native method handle with given {@link MethodHandle} and <em>fallback</em> method handle.
*/
public static MethodHandle make(NativeEntryPoint nep) {
public static MethodHandle make(MethodHandle nep) {
MethodType type = nep.type();
if (hasIllegalType(type))
throw new IllegalArgumentException("Illegal type(s) found: " + type);
Expand All @@ -74,11 +88,12 @@ private static boolean hasIllegalType(MethodType type) {

private static boolean isIllegalType(Class<?> pType) {
return !(pType == long.class
|| pType == int.class
|| pType == float.class
|| pType == double.class
|| pType == void.class
|| pType == Object.class);
|| pType == int.class
|| pType == float.class
|| pType == double.class
|| pType == void.class
|| pType == MemorySegment.class // Skip checking the argument preset in OpenJ9 for the moment.
|| pType == SegmentAllocator.class); // Skip checking the argument preset OpenJ9 for the moment.
}

private static final MemberName.Factory IMPL_NAMES = MemberName.getFactory();
Expand All @@ -93,8 +108,12 @@ private static LambdaForm preparedLambdaForm(MethodType mtype) {
}

private static LambdaForm makePreparedLambdaForm(MethodType mtype) {
/* The NativeMethodHandle object must be appended to the end of the argument
* list to ensure the invoke cache of the bounded MH is correctly fetched
* from linkToNative in the interpreter.
*/
MethodType linkerType = mtype
.appendParameterTypes(Object.class); // NEP
.appendParameterTypes(Object.class); // the NativeMethodHandle object
MemberName linker = new MemberName(MethodHandle.class, "linkToNative", linkerType, REF_invokeStatic);
try {
linker = IMPL_NAMES.resolveOrFail(REF_invokeStatic, linker, null, LM_TRUSTED, NoSuchMethodException.class);
Expand Down Expand Up @@ -137,8 +156,11 @@ BoundMethodHandle rebind() {
}

@ForceInline
static Object internalNativeEntryPoint(Object mh) {
return ((NativeMethodHandle)mh).nep;
static Object internalNativeEntryPoint(Object nativeMH) {
/* Return the nativeMH object as the argument passed over
* to linkToNative in the LambdaForm-generated bytecode.
*/
return nativeMH;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
* questions.
*/

/*
* ===========================================================================
* (c) Copyright IBM Corp. 2023, 2023 All Rights Reserved
* ===========================================================================
*/

package jdk.internal.access;

import jdk.internal.foreign.abi.NativeEntryPoint;
Expand Down Expand Up @@ -121,7 +127,7 @@ public interface JavaLangInvokeAccess {
* @param nep the native entry point
* @return the native method handle
*/
MethodHandle nativeMethodHandle(NativeEntryPoint nep);
MethodHandle nativeMethodHandle(MethodHandle nep);

/**
* Produces a method handle unreflecting from a {@code Constructor} with
Expand Down

0 comments on commit d2ccdb2

Please sign in to comment.