8336768: Allow captureCallState and critical linker options to be combined

Reviewed-by: mcimadamore
This commit is contained in:
Jorn Vernee 2024-12-03 12:28:17 +00:00
parent 63af2f42b7
commit 8cad0431ff
14 changed files with 178 additions and 86 deletions

View file

@ -852,8 +852,6 @@ public sealed interface Linker permits AbstractLinker {
* // use errno
* }
* }
* <p>
* This linker option can not be combined with {@link #critical}.
*
* @param capturedState the names of the values to save
* @throws IllegalArgumentException if at least one of the provided

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2024, 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
@ -195,6 +195,10 @@ public class CallingSequence {
return !linkerOptions.isCritical();
}
public boolean usingAddressPairs() {
return linkerOptions.allowsHeapAccess();
}
public int numLeadingParams() {
return 2 + (linkerOptions.hasCapturedCallState() ? 1 : 0); // 2 for addr, allocator
}

View file

@ -108,9 +108,18 @@ public class CallingSequenceBuilder {
MethodType calleeMethodType;
if (!forUpcall) {
if (linkerOptions.hasCapturedCallState()) {
addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of(
Binding.unboxAddress(),
Binding.vmStore(abi.capturedStateStorage(), long.class)));
if (linkerOptions.allowsHeapAccess()) {
addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of(
Binding.dup(),
Binding.segmentBase(),
Binding.vmStore(abi.capturedStateStorage(), Object.class),
Binding.segmentOffsetAllowHeap(),
Binding.vmStore(null, long.class)));
} else {
addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of(
Binding.unboxAddress(),
Binding.vmStore(abi.capturedStateStorage(), long.class)));
}
}
addArgumentBinding(0, MemorySegment.class, ValueLayout.ADDRESS, List.of(
Binding.unboxAddress(),

View file

@ -84,7 +84,8 @@ public class DowncallLinker {
leafType,
callingSequence.needsReturnBuffer(),
callingSequence.capturedStateMask(),
callingSequence.needsTransition()
callingSequence.needsTransition(),
callingSequence.usingAddressPairs()
);
MethodHandle handle = JLIA.nativeMethodHandle(nep);

View file

@ -63,11 +63,7 @@ public class LinkerOptions {
optionMap.put(option.getClass(), opImpl);
}
LinkerOptions linkerOptions = new LinkerOptions(optionMap);
if (linkerOptions.hasCapturedCallState() && linkerOptions.isCritical()) {
throw new IllegalArgumentException("Incompatible linker options: captureCallState, critical");
}
return linkerOptions;
return new LinkerOptions(optionMap);
}
public static LinkerOptions empty() {

View file

@ -60,11 +60,12 @@ public class NativeEntryPoint {
MethodType methodType,
boolean needsReturnBuffer,
int capturedStateMask,
boolean needsTransition) {
boolean needsTransition,
boolean usingAddressPairs) {
if (returnMoves.length > 1 != needsReturnBuffer) {
throw new AssertionError("Multiple register return, but needsReturnBuffer was false");
}
checkType(methodType, needsReturnBuffer, capturedStateMask);
checkMethodType(methodType, needsReturnBuffer, capturedStateMask, usingAddressPairs);
CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves),
needsReturnBuffer, capturedStateMask, needsTransition);
@ -80,14 +81,26 @@ public class NativeEntryPoint {
});
}
private static void checkType(MethodType methodType, boolean needsReturnBuffer, int savedValueMask) {
if (methodType.parameterType(0) != long.class) {
throw new AssertionError("Address expected as first param: " + methodType);
private static void checkMethodType(MethodType methodType, boolean needsReturnBuffer, int savedValueMask,
boolean usingAddressPairs) {
int checkIdx = 0;
checkParamType(methodType, checkIdx++, long.class, "Function address");
if (needsReturnBuffer) {
checkParamType(methodType, checkIdx++, long.class, "Return buffer address");
}
int checkIdx = 1;
if ((needsReturnBuffer && methodType.parameterType(checkIdx++) != long.class)
|| (savedValueMask != 0 && methodType.parameterType(checkIdx) != long.class)) {
throw new AssertionError("return buffer and/or preserved value address expected: " + methodType);
if (savedValueMask != 0) { // capturing call state
if (usingAddressPairs) {
checkParamType(methodType, checkIdx++, Object.class, "Capture state heap base");
checkParamType(methodType, checkIdx, long.class, "Capture state offset");
} else {
checkParamType(methodType, checkIdx, long.class, "Capture state address");
}
}
}
private static void checkParamType(MethodType methodType, int checkIdx, Class<?> expectedType, String name) {
if (methodType.parameterType(checkIdx) != expectedType) {
throw new AssertionError(name + " expected at index " + checkIdx + ": " + methodType);
}
}

View file

@ -163,8 +163,14 @@ public final class FallbackLinker extends AbstractLinker {
acquiredSessions.add(targetImpl);
MemorySegment capturedState = null;
Object captureStateHeapBase = null;
if (invData.capturedStateMask() != 0) {
capturedState = SharedUtils.checkCaptureSegment((MemorySegment) args[argStart++]);
if (!invData.allowsHeapAccess) {
SharedUtils.checkNative(capturedState);
} else {
captureStateHeapBase = capturedState.heapBase().orElse(null);
}
MemorySessionImpl capturedStateImpl = ((AbstractMemorySegmentImpl) capturedState).sessionImpl();
capturedStateImpl.acquire0();
acquiredSessions.add(capturedStateImpl);
@ -199,7 +205,8 @@ public final class FallbackLinker extends AbstractLinker {
retSeg = (invData.returnLayout() instanceof GroupLayout ? returnAllocator : arena).allocate(invData.returnLayout);
}
LibFallback.doDowncall(invData.cif, target, retSeg, argPtrs, capturedState, invData.capturedStateMask(),
LibFallback.doDowncall(invData.cif, target, retSeg, argPtrs,
captureStateHeapBase, capturedState, invData.capturedStateMask(),
heapBases, args.length);
Reference.reachabilityFence(invData.cif());

View file

@ -90,10 +90,11 @@ final class LibFallback {
* @see jdk.internal.foreign.abi.CapturableState
*/
static void doDowncall(MemorySegment cif, MemorySegment target, MemorySegment retPtr, MemorySegment argPtrs,
MemorySegment capturedState, int capturedStateMask,
Object captureStateHeapBase, MemorySegment capturedState, int capturedStateMask,
Object[] heapBases, int numArgs) {
doDowncall(cif.address(), target.address(),
retPtr == null ? 0 : retPtr.address(), argPtrs.address(),
captureStateHeapBase,
capturedState == null ? 0 : capturedState.address(), capturedStateMask,
heapBases, numArgs);
}
@ -212,7 +213,7 @@ final class LibFallback {
private static native int createClosure(long cif, Object userData, long[] ptrs);
private static native void freeClosure(long closureAddress, long globalTarget);
private static native void doDowncall(long cif, long fn, long rvalue, long avalues,
long capturedState, int capturedStateMask,
Object captureStateHeapBase, long capturedState, int capturedStateMask,
Object[] heapBases, int numArgs);
private static native int ffi_prep_cif(long cif, int abi, int nargs, long rtype, long atypes);