mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 23:04:50 +02:00
8296477: Foreign linker implementation update following JEP 434
Co-authored-by: Jorn Vernee <jvernee@openjdk.org> Co-authored-by: Nick Gasson <ngasson@openjdk.org> Co-authored-by: Per Minborg <pminborg@openjdk.org> Reviewed-by: rehn, mcimadamore, vlivanov
This commit is contained in:
parent
73baadceb6
commit
0452c39fec
72 changed files with 2584 additions and 933 deletions
|
@ -27,12 +27,17 @@ package java.lang.foreign;
|
|||
|
||||
import jdk.internal.foreign.abi.AbstractLinker;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
import jdk.internal.foreign.abi.CapturableState;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.javac.PreviewFeature;
|
||||
import jdk.internal.reflect.CallerSensitive;
|
||||
import jdk.internal.reflect.Reflection;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.Arrays;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A linker provides access to foreign functions from Java code, and access to Java code from foreign functions.
|
||||
|
@ -282,7 +287,8 @@ public sealed interface Linker permits AbstractLinker {
|
|||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
sealed interface Option
|
||||
permits LinkerOptions.FirstVariadicArg {
|
||||
permits LinkerOptions.LinkerOptionImpl,
|
||||
Option.CaptureCallState {
|
||||
|
||||
/**
|
||||
* {@return a linker option used to denote the index of the first variadic argument layout in a
|
||||
|
@ -292,5 +298,71 @@ public sealed interface Linker permits AbstractLinker {
|
|||
static Option firstVariadicArg(int index) {
|
||||
return new LinkerOptions.FirstVariadicArg(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return A linker option used to save portions of the execution state immediately after
|
||||
* calling a foreign function associated with a downcall method handle,
|
||||
* before it can be overwritten by the Java runtime, or read through conventional means}
|
||||
* <p>
|
||||
* A downcall method handle linked with this option will feature an additional {@link MemorySegment}
|
||||
* parameter directly following the target address parameter. This memory segment must be a
|
||||
* native segment into which the captured state is written.
|
||||
*
|
||||
* @param capturedState the names of the values to save.
|
||||
* @see CaptureCallState#supported()
|
||||
*/
|
||||
static CaptureCallState captureCallState(String... capturedState) {
|
||||
Set<CapturableState> set = Stream.of(capturedState)
|
||||
.map(CapturableState::forName)
|
||||
.collect(Collectors.toSet());
|
||||
return new LinkerOptions.CaptureCallStateImpl(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* A linker option for saving portions of the execution state immediately
|
||||
* after calling a foreign function associated with a downcall method handle,
|
||||
* before it can be overwritten by the runtime, or read through conventional means.
|
||||
* <p>
|
||||
* Execution state is captured by a downcall method handle on invocation, by writing it
|
||||
* to a native segment provided by the user to the downcall method handle.
|
||||
* <p>
|
||||
* The native segment should have the layout {@linkplain CaptureCallState#layout associated}
|
||||
* with the particular {@code CaptureCallState} instance used to link the downcall handle.
|
||||
* <p>
|
||||
* Captured state can be retrieved from this native segment by constructing var handles
|
||||
* from the {@linkplain #layout layout} associated with the {@code CaptureCallState} instance.
|
||||
* <p>
|
||||
* The following example demonstrates the use of this linker option:
|
||||
* {@snippet lang = "java":
|
||||
* MemorySegment targetAddress = ...
|
||||
* CaptureCallState ccs = Linker.Option.captureCallState("errno");
|
||||
* MethodHandle handle = Linker.nativeLinker().downcallHandle(targetAddress, FunctionDescriptor.ofVoid(), ccs);
|
||||
*
|
||||
* VarHandle errnoHandle = ccs.layout().varHandle(PathElement.groupElement("errno"));
|
||||
* try (Arena arena = Arena.openConfined()) {
|
||||
* MemorySegment capturedState = arena.allocate(ccs.layout());
|
||||
* handle.invoke(capturedState);
|
||||
* int errno = errnoHandle.get(capturedState);
|
||||
* // use errno
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
sealed interface CaptureCallState extends Option
|
||||
permits LinkerOptions.CaptureCallStateImpl {
|
||||
/**
|
||||
* {@return A struct layout that represents the layout of the native segment passed
|
||||
* to a downcall handle linked with this {@code CapturedCallState} instance}
|
||||
*/
|
||||
StructLayout layout();
|
||||
|
||||
/**
|
||||
* {@return the names of the state that can be capture by this implementation}
|
||||
*/
|
||||
static Set<String> supported() {
|
||||
return Arrays.stream(CapturableState.values())
|
||||
.map(CapturableState::stateName)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,20 +40,29 @@ public class ABIDescriptor {
|
|||
final int stackAlignment;
|
||||
final int shadowSpace;
|
||||
|
||||
final VMStorage scratch1;
|
||||
final VMStorage scratch2;
|
||||
|
||||
final VMStorage targetAddrStorage;
|
||||
final VMStorage retBufAddrStorage;
|
||||
final VMStorage capturedStateStorage;
|
||||
|
||||
public ABIDescriptor(Architecture arch, VMStorage[][] inputStorage, VMStorage[][] outputStorage,
|
||||
VMStorage[][] volatileStorage, int stackAlignment, int shadowSpace,
|
||||
VMStorage targetAddrStorage, VMStorage retBufAddrStorage) {
|
||||
VMStorage scratch1, VMStorage scratch2,
|
||||
VMStorage targetAddrStorage, VMStorage retBufAddrStorage,
|
||||
VMStorage capturedStateStorage) {
|
||||
this.arch = arch;
|
||||
this.inputStorage = inputStorage;
|
||||
this.outputStorage = outputStorage;
|
||||
this.volatileStorage = volatileStorage;
|
||||
this.stackAlignment = stackAlignment;
|
||||
this.shadowSpace = shadowSpace;
|
||||
this.scratch1 = scratch1;
|
||||
this.scratch2 = scratch2;
|
||||
this.targetAddrStorage = targetAddrStorage;
|
||||
this.retBufAddrStorage = retBufAddrStorage;
|
||||
this.capturedStateStorage = capturedStateStorage;
|
||||
}
|
||||
|
||||
public VMStorage targetAddrStorage() {
|
||||
|
@ -63,4 +72,8 @@ public class ABIDescriptor {
|
|||
public VMStorage retBufAddrStorage() {
|
||||
return retBufAddrStorage;
|
||||
}
|
||||
|
||||
public VMStorage capturedStateStorage() {
|
||||
return capturedStateStorage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,7 +53,7 @@ public abstract sealed class AbstractLinker implements Linker permits LinuxAArch
|
|||
Objects.requireNonNull(function);
|
||||
Objects.requireNonNull(options);
|
||||
checkHasNaturalAlignment(function);
|
||||
LinkerOptions optionSet = LinkerOptions.of(options);
|
||||
LinkerOptions optionSet = LinkerOptions.forDowncall(function, options);
|
||||
|
||||
return DOWNCALL_CACHE.get(new LinkRequest(function, optionSet), linkRequest -> {
|
||||
FunctionDescriptor fd = linkRequest.descriptor();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2020, 2022, 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
|
||||
|
@ -27,5 +27,4 @@ package jdk.internal.foreign.abi;
|
|||
public interface Architecture {
|
||||
boolean isStackType(int cls);
|
||||
int typeSize(int cls);
|
||||
int stackType();
|
||||
}
|
||||
|
|
|
@ -42,9 +42,12 @@ public class CallingSequence {
|
|||
private final List<Binding> returnBindings;
|
||||
private final List<List<Binding>> argumentBindings;
|
||||
|
||||
private final LinkerOptions linkerOptions;
|
||||
|
||||
public CallingSequence(boolean forUpcall, MethodType callerMethodType, MethodType calleeMethodType, FunctionDescriptor desc,
|
||||
boolean needsReturnBuffer, long returnBufferSize, long allocationSize,
|
||||
List<List<Binding>> argumentBindings, List<Binding> returnBindings) {
|
||||
List<List<Binding>> argumentBindings, List<Binding> returnBindings,
|
||||
LinkerOptions linkerOptions) {
|
||||
this.forUpcall = forUpcall;
|
||||
this.callerMethodType = callerMethodType;
|
||||
this.calleeMethodType = calleeMethodType;
|
||||
|
@ -54,6 +57,7 @@ public class CallingSequence {
|
|||
this.allocationSize = allocationSize;
|
||||
this.returnBindings = returnBindings;
|
||||
this.argumentBindings = argumentBindings;
|
||||
this.linkerOptions = linkerOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,6 +185,16 @@ public class CallingSequence {
|
|||
return !returnBindings.isEmpty();
|
||||
}
|
||||
|
||||
public int capturedStateMask() {
|
||||
return linkerOptions.capturedCallState()
|
||||
.mapToInt(CapturableState::mask)
|
||||
.reduce(0, (a, b) -> a | b);
|
||||
}
|
||||
|
||||
public int numLeadingParams() {
|
||||
return 2 + (linkerOptions.hasCapturedCallState() ? 1 : 0); // 2 for addr, allocator
|
||||
}
|
||||
|
||||
public String asString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
|
|
|
@ -47,6 +47,7 @@ public class CallingSequenceBuilder {
|
|||
GetPropertyAction.privilegedGetProperty("java.lang.foreign.VERIFY_BINDINGS", "true"));
|
||||
|
||||
private final ABIDescriptor abi;
|
||||
private final LinkerOptions linkerOptions;
|
||||
|
||||
private final boolean forUpcall;
|
||||
private final List<List<Binding>> inputBindings = new ArrayList<>();
|
||||
|
@ -55,9 +56,10 @@ public class CallingSequenceBuilder {
|
|||
private MethodType mt = MethodType.methodType(void.class);
|
||||
private FunctionDescriptor desc = FunctionDescriptor.ofVoid();
|
||||
|
||||
public CallingSequenceBuilder(ABIDescriptor abi, boolean forUpcall) {
|
||||
public CallingSequenceBuilder(ABIDescriptor abi, boolean forUpcall, LinkerOptions linkerOptions) {
|
||||
this.abi = abi;
|
||||
this.forUpcall = forUpcall;
|
||||
this.linkerOptions = linkerOptions;
|
||||
}
|
||||
|
||||
public final CallingSequenceBuilder addArgumentBindings(Class<?> carrier, MemoryLayout layout,
|
||||
|
@ -95,6 +97,11 @@ public class CallingSequenceBuilder {
|
|||
MethodType callerMethodType;
|
||||
MethodType calleeMethodType;
|
||||
if (!forUpcall) {
|
||||
if (linkerOptions.hasCapturedCallState()) {
|
||||
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(),
|
||||
Binding.vmStore(abi.targetAddrStorage(), long.class)));
|
||||
|
@ -117,7 +124,7 @@ public class CallingSequenceBuilder {
|
|||
calleeMethodType = mt;
|
||||
}
|
||||
return new CallingSequence(forUpcall, callerMethodType, calleeMethodType, desc, needsReturnBuffer,
|
||||
returnBufferSize, allocationSize, inputBindings, outputBindings);
|
||||
returnBufferSize, allocationSize, inputBindings, outputBindings, linkerOptions);
|
||||
}
|
||||
|
||||
private MethodType computeCallerTypeForUpcall() {
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 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 jdk.internal.foreign.abi;
|
||||
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.JAVA_INT;
|
||||
|
||||
public enum CapturableState {
|
||||
GET_LAST_ERROR ("GetLastError", JAVA_INT, 1 << 0),
|
||||
WSA_GET_LAST_ERROR("WSAGetLastError", JAVA_INT, 1 << 1),
|
||||
ERRNO ("errno", JAVA_INT, 1 << 2);
|
||||
|
||||
private final String stateName;
|
||||
private final ValueLayout layout;
|
||||
private final int mask;
|
||||
|
||||
CapturableState(String stateName, ValueLayout layout, int mask) {
|
||||
this.stateName = stateName;
|
||||
this.layout = layout.withName(stateName);
|
||||
this.mask = mask;
|
||||
}
|
||||
|
||||
public static CapturableState forName(String name) {
|
||||
return Stream.of(values())
|
||||
.filter(stl -> stl.stateName().equals(name))
|
||||
.findAny()
|
||||
.orElseThrow(() -> new IllegalArgumentException(
|
||||
"Unknown name: " + name +", must be one of: "
|
||||
+ Stream.of(CapturableState.values())
|
||||
.map(CapturableState::stateName)
|
||||
.collect(Collectors.joining(", "))));
|
||||
}
|
||||
|
||||
public String stateName() {
|
||||
return stateName;
|
||||
}
|
||||
|
||||
public ValueLayout layout() {
|
||||
return layout;
|
||||
}
|
||||
|
||||
public int mask() {
|
||||
return mask;
|
||||
}
|
||||
}
|
|
@ -85,7 +85,8 @@ public class DowncallLinker {
|
|||
toStorageArray(argMoves),
|
||||
toStorageArray(retMoves),
|
||||
leafType,
|
||||
callingSequence.needsReturnBuffer()
|
||||
callingSequence.needsReturnBuffer(),
|
||||
callingSequence.capturedStateMask()
|
||||
);
|
||||
MethodHandle handle = JLIA.nativeMethodHandle(nep);
|
||||
|
||||
|
|
|
@ -24,28 +24,36 @@
|
|||
*/
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.StructLayout;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class LinkerOptions {
|
||||
|
||||
private static final LinkerOptions EMPTY = LinkerOptions.of();
|
||||
private final Map<Class<?>, Linker.Option> optionsMap;
|
||||
private static final LinkerOptions EMPTY = new LinkerOptions(Map.of());
|
||||
private final Map<Class<?>, LinkerOptionImpl> optionsMap;
|
||||
|
||||
private LinkerOptions(Map<Class<?>, Linker.Option> optionsMap) {
|
||||
private LinkerOptions(Map<Class<?>, LinkerOptionImpl> optionsMap) {
|
||||
this.optionsMap = optionsMap;
|
||||
}
|
||||
|
||||
public static LinkerOptions of(Linker.Option... options) {
|
||||
Map<Class<?>, Linker.Option> optionMap = new HashMap<>();
|
||||
public static LinkerOptions forDowncall(FunctionDescriptor desc, Linker.Option... options) {
|
||||
Map<Class<?>, LinkerOptionImpl> optionMap = new HashMap<>();
|
||||
|
||||
for (Linker.Option option : options) {
|
||||
if (optionMap.containsKey(option.getClass())) {
|
||||
throw new IllegalArgumentException("Duplicate option: " + option);
|
||||
}
|
||||
optionMap.put(option.getClass(), option);
|
||||
LinkerOptionImpl opImpl = (LinkerOptionImpl) option;
|
||||
opImpl.validateForDowncall(desc);
|
||||
optionMap.put(option.getClass(), opImpl);
|
||||
}
|
||||
|
||||
return new LinkerOptions(optionMap);
|
||||
|
@ -64,6 +72,15 @@ public class LinkerOptions {
|
|||
return fva != null && argIndex >= fva.index();
|
||||
}
|
||||
|
||||
public boolean hasCapturedCallState() {
|
||||
return getOption(CaptureCallStateImpl.class) != null;
|
||||
}
|
||||
|
||||
public Stream<CapturableState> capturedCallState() {
|
||||
CaptureCallStateImpl stl = getOption(CaptureCallStateImpl.class);
|
||||
return stl == null ? Stream.empty() : stl.saved().stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -76,5 +93,39 @@ public class LinkerOptions {
|
|||
return Objects.hash(optionsMap);
|
||||
}
|
||||
|
||||
public record FirstVariadicArg(int index) implements Linker.Option { }
|
||||
public sealed interface LinkerOptionImpl extends Linker.Option
|
||||
permits FirstVariadicArg,
|
||||
CaptureCallStateImpl {
|
||||
default void validateForDowncall(FunctionDescriptor descriptor) {
|
||||
throw new IllegalArgumentException("Not supported for downcall: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
public record FirstVariadicArg(int index) implements LinkerOptionImpl {
|
||||
@Override
|
||||
public void validateForDowncall(FunctionDescriptor descriptor) {
|
||||
if (index < 0 || index > descriptor.argumentLayouts().size()) {
|
||||
throw new IllegalArgumentException("Index '" + index + "' not in bounds for descriptor: " + descriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public record CaptureCallStateImpl(Set<CapturableState> saved) implements LinkerOptionImpl, Linker.Option.CaptureCallState {
|
||||
|
||||
@Override
|
||||
public void validateForDowncall(FunctionDescriptor descriptor) {
|
||||
// done during construction
|
||||
}
|
||||
|
||||
@Override
|
||||
public StructLayout layout() {
|
||||
return MemoryLayout.structLayout(
|
||||
saved.stream()
|
||||
.sorted(Comparator.comparingInt(CapturableState::ordinal))
|
||||
.map(CapturableState::layout)
|
||||
.toArray(MemoryLayout[]::new)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ public class NativeEntryPoint {
|
|||
private static final SoftReferenceCache<CacheKey, NativeEntryPoint> NEP_CACHE = new SoftReferenceCache<>();
|
||||
private record CacheKey(MethodType methodType, ABIDescriptor abi,
|
||||
List<VMStorage> argMoves, List<VMStorage> retMoves,
|
||||
boolean needsReturnBuffer) {}
|
||||
boolean needsReturnBuffer, int capturedStateMask) {}
|
||||
|
||||
private NativeEntryPoint(MethodType methodType, long downcallStubAddress) {
|
||||
this.methodType = methodType;
|
||||
|
@ -56,26 +56,38 @@ public class NativeEntryPoint {
|
|||
|
||||
public static NativeEntryPoint make(ABIDescriptor abi,
|
||||
VMStorage[] argMoves, VMStorage[] returnMoves,
|
||||
MethodType methodType, boolean needsReturnBuffer) {
|
||||
MethodType methodType,
|
||||
boolean needsReturnBuffer,
|
||||
int capturedStateMask) {
|
||||
if (returnMoves.length > 1 != needsReturnBuffer) {
|
||||
throw new IllegalArgumentException("Multiple register return, but needsReturnBuffer was false");
|
||||
throw new AssertionError("Multiple register return, but needsReturnBuffer was false");
|
||||
}
|
||||
checkType(methodType, needsReturnBuffer, capturedStateMask);
|
||||
|
||||
assert (methodType.parameterType(0) == long.class) : "Address expected";
|
||||
assert (!needsReturnBuffer || methodType.parameterType(1) == long.class) : "return buffer address expected";
|
||||
|
||||
CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves), needsReturnBuffer);
|
||||
CacheKey key = new CacheKey(methodType, abi, Arrays.asList(argMoves), Arrays.asList(returnMoves), needsReturnBuffer, capturedStateMask);
|
||||
return NEP_CACHE.get(key, k -> {
|
||||
long downcallStub = makeDowncallStub(methodType, abi, argMoves, returnMoves, needsReturnBuffer);
|
||||
long downcallStub = makeDowncallStub(methodType, abi, argMoves, returnMoves, needsReturnBuffer, capturedStateMask);
|
||||
NativeEntryPoint nep = new NativeEntryPoint(methodType, downcallStub);
|
||||
CLEANER.register(nep, () -> freeDowncallStub(downcallStub));
|
||||
return nep;
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static native long makeDowncallStub(MethodType methodType, ABIDescriptor abi,
|
||||
VMStorage[] encArgMoves, VMStorage[] encRetMoves,
|
||||
boolean needsReturnBuffer);
|
||||
boolean needsReturnBuffer,
|
||||
int capturedStateMask);
|
||||
|
||||
private static native boolean freeDowncallStub0(long downcallStub);
|
||||
private static void freeDowncallStub(long downcallStub) {
|
||||
|
|
|
@ -108,17 +108,18 @@ public final class SharedUtils {
|
|||
* @param cDesc the function descriptor of the native function (with actual return layout)
|
||||
* @return the adapted handle
|
||||
*/
|
||||
public static MethodHandle adaptDowncallForIMR(MethodHandle handle, FunctionDescriptor cDesc) {
|
||||
public static MethodHandle adaptDowncallForIMR(MethodHandle handle, FunctionDescriptor cDesc, CallingSequence sequence) {
|
||||
if (handle.type().returnType() != void.class)
|
||||
throw new IllegalArgumentException("return expected to be void for in memory returns: " + handle.type());
|
||||
if (handle.type().parameterType(2) != MemorySegment.class)
|
||||
int imrAddrIdx = sequence.numLeadingParams();
|
||||
if (handle.type().parameterType(imrAddrIdx) != MemorySegment.class)
|
||||
throw new IllegalArgumentException("MemorySegment expected as third param: " + handle.type());
|
||||
if (cDesc.returnLayout().isEmpty())
|
||||
throw new IllegalArgumentException("Return layout needed: " + cDesc);
|
||||
|
||||
MethodHandle ret = identity(MemorySegment.class); // (MemorySegment) MemorySegment
|
||||
handle = collectArguments(ret, 1, handle); // (MemorySegment, MemorySegment, SegmentAllocator, MemorySegment, ...) MemorySegment
|
||||
handle = mergeArguments(handle, 0, 3); // (MemorySegment, MemorySegment, SegmentAllocator, ...) MemorySegment
|
||||
handle = mergeArguments(handle, 0, 1 + imrAddrIdx); // (MemorySegment, MemorySegment, SegmentAllocator, ...) MemorySegment
|
||||
handle = collectArguments(handle, 0, insertArguments(MH_ALLOC_BUFFER, 1, cDesc.returnLayout().get())); // (SegmentAllocator, MemorySegment, SegmentAllocator, ...) MemorySegment
|
||||
handle = mergeArguments(handle, 0, 2); // (SegmentAllocator, MemorySegment, ...) MemorySegment
|
||||
handle = swapArguments(handle, 0, 1); // (MemorySegment, SegmentAllocator, ...) MemorySegment
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2022, 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 jdk.internal.foreign.abi;
|
||||
|
||||
// must keep in sync with StubLocations in VM code
|
||||
public enum StubLocations {
|
||||
TARGET_ADDRESS,
|
||||
RETURN_BUFFER,
|
||||
CAPTURED_STATE_BUFFER;
|
||||
|
||||
public VMStorage storage(byte type) {
|
||||
return new VMStorage(type, (short) 8, ordinal());
|
||||
}
|
||||
}
|
|
@ -24,52 +24,23 @@
|
|||
*/
|
||||
package jdk.internal.foreign.abi;
|
||||
|
||||
import java.util.Objects;
|
||||
/**
|
||||
*
|
||||
* @param type the type of storage. e.g. stack, or which register type (GP, FP, vector)
|
||||
* @param segmentMaskOrSize the (on stack) size in bytes when type = stack, a register mask otherwise,
|
||||
* the register mask indicates which segments of a register are used.
|
||||
* @param indexOrOffset the index is either a register number within a type, or
|
||||
* a stack offset in bytes if type = stack.
|
||||
* (a particular platform might add a bias to this in generate code)
|
||||
* @param debugName the debug name
|
||||
*/
|
||||
public record VMStorage(byte type,
|
||||
short segmentMaskOrSize,
|
||||
int indexOrOffset,
|
||||
String debugName) {
|
||||
|
||||
public class VMStorage {
|
||||
private final int type;
|
||||
private final int index;
|
||||
|
||||
private final String debugName;
|
||||
|
||||
public VMStorage(int type, int index, String debugName) {
|
||||
this.type = type;
|
||||
this.index = index;
|
||||
this.debugName = debugName;
|
||||
public VMStorage(byte type, short segmentMaskOrSize, int indexOrOffset) {
|
||||
this(type, segmentMaskOrSize, indexOrOffset, "Stack@" + indexOrOffset);
|
||||
}
|
||||
|
||||
public int type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public int index() {
|
||||
return index;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return debugName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
VMStorage vmStorage = (VMStorage) o;
|
||||
return type == vmStorage.type &&
|
||||
index == vmStorage.index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(type, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "VMStorage{" +
|
||||
"type=" + type +
|
||||
", index=" + index +
|
||||
", debugName='" + debugName + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,118 +27,118 @@ package jdk.internal.foreign.abi.aarch64;
|
|||
|
||||
import jdk.internal.foreign.abi.ABIDescriptor;
|
||||
import jdk.internal.foreign.abi.Architecture;
|
||||
import jdk.internal.foreign.abi.StubLocations;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
|
||||
public class AArch64Architecture implements Architecture {
|
||||
public static final Architecture INSTANCE = new AArch64Architecture();
|
||||
|
||||
private static final short REG64_MASK = 0b0000_0000_0000_0001;
|
||||
private static final short V128_MASK = 0b0000_0000_0000_0001;
|
||||
|
||||
private static final int INTEGER_REG_SIZE = 8;
|
||||
private static final int VECTOR_REG_SIZE = 16;
|
||||
private static final int STACK_SLOT_SIZE = 8;
|
||||
|
||||
@Override
|
||||
public boolean isStackType(int cls) {
|
||||
return cls == StorageClasses.STACK;
|
||||
return cls == StorageType.STACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typeSize(int cls) {
|
||||
switch (cls) {
|
||||
case StorageClasses.INTEGER: return INTEGER_REG_SIZE;
|
||||
case StorageClasses.VECTOR: return VECTOR_REG_SIZE;
|
||||
case StorageClasses.STACK: return STACK_SLOT_SIZE;
|
||||
case StorageType.INTEGER: return INTEGER_REG_SIZE;
|
||||
case StorageType.VECTOR: return VECTOR_REG_SIZE;
|
||||
// STACK is deliberately omitted
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid Storage Class: " + cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackType() {
|
||||
return StorageClasses.STACK;
|
||||
public interface StorageType {
|
||||
byte INTEGER = 0;
|
||||
byte VECTOR = 1;
|
||||
byte STACK = 2;
|
||||
byte PLACEHOLDER = 3;
|
||||
}
|
||||
|
||||
public interface StorageClasses {
|
||||
int INTEGER = 0;
|
||||
int VECTOR = 1;
|
||||
int STACK = 3;
|
||||
public static class Regs { // break circular dependency
|
||||
public static final VMStorage r0 = integerRegister(0);
|
||||
public static final VMStorage r1 = integerRegister(1);
|
||||
public static final VMStorage r2 = integerRegister(2);
|
||||
public static final VMStorage r3 = integerRegister(3);
|
||||
public static final VMStorage r4 = integerRegister(4);
|
||||
public static final VMStorage r5 = integerRegister(5);
|
||||
public static final VMStorage r6 = integerRegister(6);
|
||||
public static final VMStorage r7 = integerRegister(7);
|
||||
public static final VMStorage r8 = integerRegister(8);
|
||||
public static final VMStorage r9 = integerRegister(9);
|
||||
public static final VMStorage r10 = integerRegister(10);
|
||||
public static final VMStorage r11 = integerRegister(11);
|
||||
public static final VMStorage r12 = integerRegister(12);
|
||||
public static final VMStorage r13 = integerRegister(13);
|
||||
public static final VMStorage r14 = integerRegister(14);
|
||||
public static final VMStorage r15 = integerRegister(15);
|
||||
public static final VMStorage r16 = integerRegister(16);
|
||||
public static final VMStorage r17 = integerRegister(17);
|
||||
public static final VMStorage r18 = integerRegister(18);
|
||||
public static final VMStorage r19 = integerRegister(19);
|
||||
public static final VMStorage r20 = integerRegister(20);
|
||||
public static final VMStorage r21 = integerRegister(21);
|
||||
public static final VMStorage r22 = integerRegister(22);
|
||||
public static final VMStorage r23 = integerRegister(23);
|
||||
public static final VMStorage r24 = integerRegister(24);
|
||||
public static final VMStorage r25 = integerRegister(25);
|
||||
public static final VMStorage r26 = integerRegister(26);
|
||||
public static final VMStorage r27 = integerRegister(27);
|
||||
public static final VMStorage r28 = integerRegister(28);
|
||||
public static final VMStorage r29 = integerRegister(29);
|
||||
public static final VMStorage r30 = integerRegister(30);
|
||||
public static final VMStorage r31 = integerRegister(31);
|
||||
public static final VMStorage v0 = vectorRegister(0);
|
||||
public static final VMStorage v1 = vectorRegister(1);
|
||||
public static final VMStorage v2 = vectorRegister(2);
|
||||
public static final VMStorage v3 = vectorRegister(3);
|
||||
public static final VMStorage v4 = vectorRegister(4);
|
||||
public static final VMStorage v5 = vectorRegister(5);
|
||||
public static final VMStorage v6 = vectorRegister(6);
|
||||
public static final VMStorage v7 = vectorRegister(7);
|
||||
public static final VMStorage v8 = vectorRegister(8);
|
||||
public static final VMStorage v9 = vectorRegister(9);
|
||||
public static final VMStorage v10 = vectorRegister(10);
|
||||
public static final VMStorage v11 = vectorRegister(11);
|
||||
public static final VMStorage v12 = vectorRegister(12);
|
||||
public static final VMStorage v13 = vectorRegister(13);
|
||||
public static final VMStorage v14 = vectorRegister(14);
|
||||
public static final VMStorage v15 = vectorRegister(15);
|
||||
public static final VMStorage v16 = vectorRegister(16);
|
||||
public static final VMStorage v17 = vectorRegister(17);
|
||||
public static final VMStorage v18 = vectorRegister(18);
|
||||
public static final VMStorage v19 = vectorRegister(19);
|
||||
public static final VMStorage v20 = vectorRegister(20);
|
||||
public static final VMStorage v21 = vectorRegister(21);
|
||||
public static final VMStorage v22 = vectorRegister(22);
|
||||
public static final VMStorage v23 = vectorRegister(23);
|
||||
public static final VMStorage v24 = vectorRegister(24);
|
||||
public static final VMStorage v25 = vectorRegister(25);
|
||||
public static final VMStorage v26 = vectorRegister(26);
|
||||
public static final VMStorage v27 = vectorRegister(27);
|
||||
public static final VMStorage v28 = vectorRegister(28);
|
||||
public static final VMStorage v29 = vectorRegister(29);
|
||||
public static final VMStorage v30 = vectorRegister(30);
|
||||
public static final VMStorage v31 = vectorRegister(31);
|
||||
}
|
||||
|
||||
public static final VMStorage r0 = integerRegister(0);
|
||||
public static final VMStorage r1 = integerRegister(1);
|
||||
public static final VMStorage r2 = integerRegister(2);
|
||||
public static final VMStorage r3 = integerRegister(3);
|
||||
public static final VMStorage r4 = integerRegister(4);
|
||||
public static final VMStorage r5 = integerRegister(5);
|
||||
public static final VMStorage r6 = integerRegister(6);
|
||||
public static final VMStorage r7 = integerRegister(7);
|
||||
public static final VMStorage r8 = integerRegister(8);
|
||||
public static final VMStorage r9 = integerRegister(9);
|
||||
public static final VMStorage r10 = integerRegister(10);
|
||||
public static final VMStorage r11 = integerRegister(11);
|
||||
public static final VMStorage r12 = integerRegister(12);
|
||||
public static final VMStorage r13 = integerRegister(13);
|
||||
public static final VMStorage r14 = integerRegister(14);
|
||||
public static final VMStorage r15 = integerRegister(15);
|
||||
public static final VMStorage r16 = integerRegister(16);
|
||||
public static final VMStorage r17 = integerRegister(17);
|
||||
public static final VMStorage r18 = integerRegister(18);
|
||||
public static final VMStorage r19 = integerRegister(19);
|
||||
public static final VMStorage r20 = integerRegister(20);
|
||||
public static final VMStorage r21 = integerRegister(21);
|
||||
public static final VMStorage r22 = integerRegister(22);
|
||||
public static final VMStorage r23 = integerRegister(23);
|
||||
public static final VMStorage r24 = integerRegister(24);
|
||||
public static final VMStorage r25 = integerRegister(25);
|
||||
public static final VMStorage r26 = integerRegister(26);
|
||||
public static final VMStorage r27 = integerRegister(27);
|
||||
public static final VMStorage r28 = integerRegister(28);
|
||||
public static final VMStorage r29 = integerRegister(29);
|
||||
public static final VMStorage r30 = integerRegister(30);
|
||||
public static final VMStorage r31 = integerRegister(31);
|
||||
|
||||
public static final VMStorage v0 = vectorRegister(0);
|
||||
public static final VMStorage v1 = vectorRegister(1);
|
||||
public static final VMStorage v2 = vectorRegister(2);
|
||||
public static final VMStorage v3 = vectorRegister(3);
|
||||
public static final VMStorage v4 = vectorRegister(4);
|
||||
public static final VMStorage v5 = vectorRegister(5);
|
||||
public static final VMStorage v6 = vectorRegister(6);
|
||||
public static final VMStorage v7 = vectorRegister(7);
|
||||
public static final VMStorage v8 = vectorRegister(8);
|
||||
public static final VMStorage v9 = vectorRegister(9);
|
||||
public static final VMStorage v10 = vectorRegister(10);
|
||||
public static final VMStorage v11 = vectorRegister(11);
|
||||
public static final VMStorage v12 = vectorRegister(12);
|
||||
public static final VMStorage v13 = vectorRegister(13);
|
||||
public static final VMStorage v14 = vectorRegister(14);
|
||||
public static final VMStorage v15 = vectorRegister(15);
|
||||
public static final VMStorage v16 = vectorRegister(16);
|
||||
public static final VMStorage v17 = vectorRegister(17);
|
||||
public static final VMStorage v18 = vectorRegister(18);
|
||||
public static final VMStorage v19 = vectorRegister(19);
|
||||
public static final VMStorage v20 = vectorRegister(20);
|
||||
public static final VMStorage v21 = vectorRegister(21);
|
||||
public static final VMStorage v22 = vectorRegister(22);
|
||||
public static final VMStorage v23 = vectorRegister(23);
|
||||
public static final VMStorage v24 = vectorRegister(24);
|
||||
public static final VMStorage v25 = vectorRegister(25);
|
||||
public static final VMStorage v26 = vectorRegister(26);
|
||||
public static final VMStorage v27 = vectorRegister(27);
|
||||
public static final VMStorage v28 = vectorRegister(28);
|
||||
public static final VMStorage v29 = vectorRegister(29);
|
||||
public static final VMStorage v30 = vectorRegister(30);
|
||||
public static final VMStorage v31 = vectorRegister(31);
|
||||
|
||||
private static VMStorage integerRegister(int index) {
|
||||
return new VMStorage(StorageClasses.INTEGER, index, "r" + index);
|
||||
return new VMStorage(StorageType.INTEGER, REG64_MASK, index, "r" + index);
|
||||
}
|
||||
|
||||
private static VMStorage vectorRegister(int index) {
|
||||
return new VMStorage(StorageClasses.VECTOR, index, "v" + index);
|
||||
return new VMStorage(StorageType.VECTOR, V128_MASK, index, "v" + index);
|
||||
}
|
||||
|
||||
public static VMStorage stackStorage(int index) {
|
||||
return new VMStorage(StorageClasses.STACK, index, "Stack@" + index);
|
||||
public static VMStorage stackStorage(short size, int byteOffset) {
|
||||
return new VMStorage(StorageType.STACK, size, byteOffset);
|
||||
}
|
||||
|
||||
public static ABIDescriptor abiFor(VMStorage[] inputIntRegs,
|
||||
|
@ -149,7 +149,7 @@ public class AArch64Architecture implements Architecture {
|
|||
VMStorage[] volatileVectorRegs,
|
||||
int stackAlignment,
|
||||
int shadowSpace,
|
||||
VMStorage targetAddrStorage, VMStorage retBufAddrStorage) {
|
||||
VMStorage scratch1, VMStorage scratch2) {
|
||||
return new ABIDescriptor(
|
||||
INSTANCE,
|
||||
new VMStorage[][] {
|
||||
|
@ -166,7 +166,10 @@ public class AArch64Architecture implements Architecture {
|
|||
},
|
||||
stackAlignment,
|
||||
shadowSpace,
|
||||
targetAddrStorage, retBufAddrStorage);
|
||||
scratch1, scratch2,
|
||||
StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER),
|
||||
StubLocations.RETURN_BUFFER.storage(StorageType.PLACEHOLDER),
|
||||
StubLocations.CAPTURED_STATE_BUFFER.storage(StorageType.PLACEHOLDER));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2019, 2021, Arm Limited. All rights reserved.
|
||||
* Copyright (c) 2019, 2022, Arm Limited. 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
|
||||
|
@ -50,6 +50,7 @@ import java.util.Optional;
|
|||
|
||||
import static jdk.internal.foreign.PlatformLayouts.*;
|
||||
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
|
||||
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
|
||||
|
||||
/**
|
||||
* For the AArch64 C ABI specifically, this class uses CallingSequenceBuilder
|
||||
|
@ -84,12 +85,11 @@ public abstract class CallArranger {
|
|||
new VMStorage[] { r0, r1 },
|
||||
new VMStorage[] { v0, v1, v2, v3 },
|
||||
new VMStorage[] { r9, r10, r11, r12, r13, r14, r15 },
|
||||
new VMStorage[] { v16, v17, v18, v19, v20, v21, v22, v23, v25,
|
||||
new VMStorage[] { v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
|
||||
v26, v27, v28, v29, v30, v31 },
|
||||
16, // Stack is always 16 byte aligned on AArch64
|
||||
0, // No shadow space
|
||||
r9, // target addr reg
|
||||
r10 // return buffer addr reg
|
||||
r9, r10 // scratch 1 & 2
|
||||
);
|
||||
|
||||
public record Bindings(CallingSequence callingSequence,
|
||||
|
@ -119,7 +119,7 @@ public abstract class CallArranger {
|
|||
}
|
||||
|
||||
public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall, LinkerOptions options) {
|
||||
CallingSequenceBuilder csb = new CallingSequenceBuilder(C, forUpcall);
|
||||
CallingSequenceBuilder csb = new CallingSequenceBuilder(C, forUpcall, options);
|
||||
|
||||
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true);
|
||||
BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false);
|
||||
|
@ -152,7 +152,7 @@ public abstract class CallArranger {
|
|||
MethodHandle handle = new DowncallLinker(C, bindings.callingSequence).getBoundMethodHandle();
|
||||
|
||||
if (bindings.isInMemoryReturn) {
|
||||
handle = SharedUtils.adaptDowncallForIMR(handle, cDesc);
|
||||
handle = SharedUtils.adaptDowncallForIMR(handle, cDesc, bindings.callingSequence);
|
||||
}
|
||||
|
||||
return handle;
|
||||
|
@ -186,32 +186,28 @@ public abstract class CallArranger {
|
|||
this.forArguments = forArguments;
|
||||
}
|
||||
|
||||
void alignStack(long alignment) {
|
||||
stackOffset = Utils.alignUp(stackOffset, alignment);
|
||||
}
|
||||
|
||||
VMStorage stackAlloc(long size, long alignment) {
|
||||
assert forArguments : "no stack returns";
|
||||
// Implementation limit: each arg must take up at least an 8 byte stack slot (on the Java side)
|
||||
// There is currently no way to address stack offsets that are not multiples of 8 bytes
|
||||
// The VM can only address multiple-of-4-bytes offsets, which is also not good enough for some ABIs
|
||||
// see JDK-8283462 and related issues
|
||||
long stackSlotAlignment = Math.max(alignment, STACK_SLOT_SIZE);
|
||||
long alignedStackOffset = Utils.alignUp(stackOffset, stackSlotAlignment);
|
||||
// macos-aarch64 ABI potentially requires addressing stack offsets that are not multiples of 8 bytes
|
||||
// Reject such call types here, to prevent undefined behavior down the line
|
||||
// Reject if the above stack-slot-aligned offset does not match the offset the ABI really wants
|
||||
// Except for variadic arguments, which _are_ passed at 8-byte-aligned offsets
|
||||
if (requiresSubSlotStackPacking() && alignedStackOffset != Utils.alignUp(stackOffset, alignment)
|
||||
&& !forVarArgs) // varargs are given a pass on all aarch64 ABIs
|
||||
throw new UnsupportedOperationException("Call type not supported on this platform");
|
||||
long alignedStackOffset = Utils.alignUp(stackOffset, alignment);
|
||||
|
||||
stackOffset = alignedStackOffset;
|
||||
short encodedSize = (short) size;
|
||||
assert (encodedSize & 0xFFFF) == size;
|
||||
|
||||
VMStorage storage =
|
||||
stackStorage((int)(stackOffset / STACK_SLOT_SIZE));
|
||||
stackOffset += size;
|
||||
AArch64Architecture.stackStorage(encodedSize, (int)alignedStackOffset);
|
||||
stackOffset = alignedStackOffset + size;
|
||||
return storage;
|
||||
}
|
||||
|
||||
VMStorage stackAlloc(MemoryLayout layout) {
|
||||
return stackAlloc(layout.byteSize(), layout.byteAlignment());
|
||||
long stackSlotAlignment = requiresSubSlotStackPacking() && !forVarArgs
|
||||
? layout.byteAlignment()
|
||||
: Math.max(layout.byteAlignment(), STACK_SLOT_SIZE);
|
||||
return stackAlloc(layout.byteSize(), stackSlotAlignment);
|
||||
}
|
||||
|
||||
VMStorage[] regAlloc(int type, int count) {
|
||||
|
@ -244,11 +240,31 @@ public abstract class CallArranger {
|
|||
return storage[0];
|
||||
}
|
||||
|
||||
VMStorage[] nextStorageForHFA(GroupLayout group) {
|
||||
final int nFields = group.memberLayouts().size();
|
||||
VMStorage[] regs = regAlloc(StorageType.VECTOR, nFields);
|
||||
if (regs == null && requiresSubSlotStackPacking() && !forVarArgs) {
|
||||
// For the ABI variants that pack arguments spilled to the
|
||||
// stack, HFA arguments are spilled as if their individual
|
||||
// fields had been allocated separately rather than as if the
|
||||
// struct had been spilled as a whole.
|
||||
|
||||
VMStorage[] slots = new VMStorage[nFields];
|
||||
for (int i = 0; i < nFields; i++) {
|
||||
slots[i] = stackAlloc(group.memberLayouts().get(i));
|
||||
}
|
||||
|
||||
return slots;
|
||||
} else {
|
||||
return regs;
|
||||
}
|
||||
}
|
||||
|
||||
void adjustForVarArgs() {
|
||||
// This system passes all variadic parameters on the stack. Ensure
|
||||
// no further arguments are allocated to registers.
|
||||
nRegs[StorageClasses.INTEGER] = MAX_REGISTER_ARGUMENTS;
|
||||
nRegs[StorageClasses.VECTOR] = MAX_REGISTER_ARGUMENTS;
|
||||
nRegs[StorageType.INTEGER] = MAX_REGISTER_ARGUMENTS;
|
||||
nRegs[StorageType.VECTOR] = MAX_REGISTER_ARGUMENTS;
|
||||
forVarArgs = true;
|
||||
}
|
||||
}
|
||||
|
@ -279,6 +295,12 @@ public abstract class CallArranger {
|
|||
.vmStore(storage, type);
|
||||
offset += STACK_SLOT_SIZE;
|
||||
}
|
||||
|
||||
if (requiresSubSlotStackPacking()) {
|
||||
// Pad to the next stack slot boundary instead of packing
|
||||
// additional arguments into the unused space.
|
||||
storageCalculator.alignStack(STACK_SLOT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
protected void spillStructBox(Binding.Builder bindings, MemoryLayout layout) {
|
||||
|
@ -298,6 +320,12 @@ public abstract class CallArranger {
|
|||
.bufferStore(offset, type);
|
||||
offset += STACK_SLOT_SIZE;
|
||||
}
|
||||
|
||||
if (requiresSubSlotStackPacking()) {
|
||||
// Pad to the next stack slot boundary instead of packing
|
||||
// additional arguments into the unused space.
|
||||
storageCalculator.alignStack(STACK_SLOT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
abstract List<Binding> getBindings(Class<?> carrier, MemoryLayout layout);
|
||||
|
@ -326,14 +354,14 @@ public abstract class CallArranger {
|
|||
case STRUCT_REGISTER: {
|
||||
assert carrier == MemorySegment.class;
|
||||
VMStorage[] regs = storageCalculator.regAlloc(
|
||||
StorageClasses.INTEGER, layout);
|
||||
StorageType.INTEGER, layout);
|
||||
if (regs != null) {
|
||||
int regIndex = 0;
|
||||
long offset = 0;
|
||||
while (offset < layout.byteSize()) {
|
||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
VMStorage storage = regs[regIndex++];
|
||||
boolean useFloat = storage.type() == StorageClasses.VECTOR;
|
||||
boolean useFloat = storage.type() == StorageType.VECTOR;
|
||||
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, useFloat);
|
||||
if (offset + copy < layout.byteSize()) {
|
||||
bindings.dup();
|
||||
|
@ -352,21 +380,20 @@ public abstract class CallArranger {
|
|||
bindings.copy(layout)
|
||||
.unboxAddress();
|
||||
VMStorage storage = storageCalculator.nextStorage(
|
||||
StorageClasses.INTEGER, AArch64.C_POINTER);
|
||||
StorageType.INTEGER, AArch64.C_POINTER);
|
||||
bindings.vmStore(storage, long.class);
|
||||
break;
|
||||
}
|
||||
case STRUCT_HFA: {
|
||||
assert carrier == MemorySegment.class;
|
||||
GroupLayout group = (GroupLayout)layout;
|
||||
VMStorage[] regs = storageCalculator.regAlloc(
|
||||
StorageClasses.VECTOR, group.memberLayouts().size());
|
||||
VMStorage[] regs = storageCalculator.nextStorageForHFA(group);
|
||||
if (regs != null) {
|
||||
long offset = 0;
|
||||
for (int i = 0; i < group.memberLayouts().size(); i++) {
|
||||
VMStorage storage = regs[i];
|
||||
final long size = group.memberLayouts().get(i).byteSize();
|
||||
boolean useFloat = storage.type() == StorageClasses.VECTOR;
|
||||
boolean useFloat = storage.type() == StorageType.VECTOR;
|
||||
Class<?> type = SharedUtils.primitiveCarrierForSize(size, useFloat);
|
||||
if (i + 1 < group.memberLayouts().size()) {
|
||||
bindings.dup();
|
||||
|
@ -383,19 +410,19 @@ public abstract class CallArranger {
|
|||
case POINTER: {
|
||||
bindings.unboxAddress();
|
||||
VMStorage storage =
|
||||
storageCalculator.nextStorage(StorageClasses.INTEGER, layout);
|
||||
storageCalculator.nextStorage(StorageType.INTEGER, layout);
|
||||
bindings.vmStore(storage, long.class);
|
||||
break;
|
||||
}
|
||||
case INTEGER: {
|
||||
VMStorage storage =
|
||||
storageCalculator.nextStorage(StorageClasses.INTEGER, layout);
|
||||
storageCalculator.nextStorage(StorageType.INTEGER, layout);
|
||||
bindings.vmStore(storage, carrier);
|
||||
break;
|
||||
}
|
||||
case FLOAT: {
|
||||
VMStorage storage =
|
||||
storageCalculator.nextStorage(StorageClasses.VECTOR, layout);
|
||||
storageCalculator.nextStorage(StorageType.VECTOR, layout);
|
||||
bindings.vmStore(storage, carrier);
|
||||
break;
|
||||
}
|
||||
|
@ -428,7 +455,7 @@ public abstract class CallArranger {
|
|||
assert carrier == MemorySegment.class;
|
||||
bindings.allocate(layout);
|
||||
VMStorage[] regs = storageCalculator.regAlloc(
|
||||
StorageClasses.INTEGER, layout);
|
||||
StorageType.INTEGER, layout);
|
||||
if (regs != null) {
|
||||
int regIndex = 0;
|
||||
long offset = 0;
|
||||
|
@ -436,7 +463,7 @@ public abstract class CallArranger {
|
|||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
VMStorage storage = regs[regIndex++];
|
||||
bindings.dup();
|
||||
boolean useFloat = storage.type() == StorageClasses.VECTOR;
|
||||
boolean useFloat = storage.type() == StorageType.VECTOR;
|
||||
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, useFloat);
|
||||
bindings.vmLoad(storage, type)
|
||||
.bufferStore(offset, type);
|
||||
|
@ -449,7 +476,7 @@ public abstract class CallArranger {
|
|||
case STRUCT_REFERENCE -> {
|
||||
assert carrier == MemorySegment.class;
|
||||
VMStorage storage = storageCalculator.nextStorage(
|
||||
StorageClasses.INTEGER, AArch64.C_POINTER);
|
||||
StorageType.INTEGER, AArch64.C_POINTER);
|
||||
bindings.vmLoad(storage, long.class)
|
||||
.boxAddress(layout);
|
||||
}
|
||||
|
@ -457,14 +484,13 @@ public abstract class CallArranger {
|
|||
assert carrier == MemorySegment.class;
|
||||
bindings.allocate(layout);
|
||||
GroupLayout group = (GroupLayout) layout;
|
||||
VMStorage[] regs = storageCalculator.regAlloc(
|
||||
StorageClasses.VECTOR, group.memberLayouts().size());
|
||||
VMStorage[] regs = storageCalculator.nextStorageForHFA(group);
|
||||
if (regs != null) {
|
||||
long offset = 0;
|
||||
for (int i = 0; i < group.memberLayouts().size(); i++) {
|
||||
VMStorage storage = regs[i];
|
||||
final long size = group.memberLayouts().get(i).byteSize();
|
||||
boolean useFloat = storage.type() == StorageClasses.VECTOR;
|
||||
boolean useFloat = storage.type() == StorageType.VECTOR;
|
||||
Class<?> type = SharedUtils.primitiveCarrierForSize(size, useFloat);
|
||||
bindings.dup()
|
||||
.vmLoad(storage, type)
|
||||
|
@ -477,18 +503,18 @@ public abstract class CallArranger {
|
|||
}
|
||||
case POINTER -> {
|
||||
VMStorage storage =
|
||||
storageCalculator.nextStorage(StorageClasses.INTEGER, layout);
|
||||
storageCalculator.nextStorage(StorageType.INTEGER, layout);
|
||||
bindings.vmLoad(storage, long.class)
|
||||
.boxAddressRaw(Utils.pointeeSize(layout));
|
||||
}
|
||||
case INTEGER -> {
|
||||
VMStorage storage =
|
||||
storageCalculator.nextStorage(StorageClasses.INTEGER, layout);
|
||||
storageCalculator.nextStorage(StorageType.INTEGER, layout);
|
||||
bindings.vmLoad(storage, carrier);
|
||||
}
|
||||
case FLOAT -> {
|
||||
VMStorage storage =
|
||||
storageCalculator.nextStorage(StorageClasses.VECTOR, layout);
|
||||
storageCalculator.nextStorage(StorageType.VECTOR, layout);
|
||||
bindings.vmLoad(storage, carrier);
|
||||
}
|
||||
default -> throw new UnsupportedOperationException("Unhandled class " + argumentClass);
|
||||
|
|
|
@ -26,116 +26,126 @@ package jdk.internal.foreign.abi.x64;
|
|||
|
||||
import jdk.internal.foreign.abi.ABIDescriptor;
|
||||
import jdk.internal.foreign.abi.Architecture;
|
||||
import jdk.internal.foreign.abi.StubLocations;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public class X86_64Architecture implements Architecture {
|
||||
public static final Architecture INSTANCE = new X86_64Architecture();
|
||||
|
||||
private static final short REG8_H_MASK = 0b0000_0000_0000_0010;
|
||||
private static final short REG8_L_MASK = 0b0000_0000_0000_0001;
|
||||
private static final short REG16_MASK = 0b0000_0000_0000_0011;
|
||||
private static final short REG32_MASK = 0b0000_0000_0000_0111;
|
||||
private static final short REG64_MASK = 0b0000_0000_0000_1111;
|
||||
private static final short XMM_MASK = 0b0000_0000_0000_0001;
|
||||
private static final short YMM_MASK = 0b0000_0000_0000_0011;
|
||||
private static final short ZMM_MASK = 0b0000_0000_0000_0111;
|
||||
private static final short STP_MASK = 0b0000_0000_0000_0001;
|
||||
|
||||
private static final int INTEGER_REG_SIZE = 8; // bytes
|
||||
private static final int VECTOR_REG_SIZE = 16; // size of XMM register
|
||||
private static final int X87_REG_SIZE = 16;
|
||||
private static final int STACK_SLOT_SIZE = 8;
|
||||
|
||||
@Override
|
||||
public boolean isStackType(int cls) {
|
||||
return cls == StorageClasses.STACK;
|
||||
return cls == StorageType.STACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int typeSize(int cls) {
|
||||
switch (cls) {
|
||||
case StorageClasses.INTEGER: return INTEGER_REG_SIZE;
|
||||
case StorageClasses.VECTOR: return VECTOR_REG_SIZE;
|
||||
case StorageClasses.X87: return X87_REG_SIZE;
|
||||
case StorageClasses.STACK: return STACK_SLOT_SIZE;
|
||||
case StorageType.INTEGER: return INTEGER_REG_SIZE;
|
||||
case StorageType.VECTOR: return VECTOR_REG_SIZE;
|
||||
case StorageType.X87: return X87_REG_SIZE;
|
||||
// STACK is deliberately omitted
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Invalid Storage Class: " +cls);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stackType() {
|
||||
return StorageClasses.STACK;
|
||||
// must keep in sync with StorageType in VM code
|
||||
public interface StorageType {
|
||||
byte INTEGER = 0;
|
||||
byte VECTOR = 1;
|
||||
byte X87 = 2;
|
||||
byte STACK = 3;
|
||||
byte PLACEHOLDER = 4;
|
||||
}
|
||||
|
||||
public interface StorageClasses {
|
||||
int INTEGER = 0;
|
||||
int VECTOR = 1;
|
||||
int X87 = 2;
|
||||
int STACK = 3;
|
||||
public static class Regs { // break circular dependency
|
||||
public static final VMStorage rax = integerRegister(0, "rax");
|
||||
public static final VMStorage rcx = integerRegister(1, "rcx");
|
||||
public static final VMStorage rdx = integerRegister(2, "rdx");
|
||||
public static final VMStorage rbx = integerRegister(3, "rbx");
|
||||
public static final VMStorage rsp = integerRegister(4, "rsp");
|
||||
public static final VMStorage rbp = integerRegister(5, "rbp");
|
||||
public static final VMStorage rsi = integerRegister(6, "rsi");
|
||||
public static final VMStorage rdi = integerRegister(7, "rdi");
|
||||
public static final VMStorage r8 = integerRegister(8, "r8");
|
||||
public static final VMStorage r9 = integerRegister(9, "r9");
|
||||
public static final VMStorage r10 = integerRegister(10, "r10");
|
||||
public static final VMStorage r11 = integerRegister(11, "r11");
|
||||
public static final VMStorage r12 = integerRegister(12, "r12");
|
||||
public static final VMStorage r13 = integerRegister(13, "r13");
|
||||
public static final VMStorage r14 = integerRegister(14, "r14");
|
||||
public static final VMStorage r15 = integerRegister(15, "r15");
|
||||
|
||||
public static final VMStorage xmm0 = vectorRegister(0, "xmm0");
|
||||
public static final VMStorage xmm1 = vectorRegister(1, "xmm1");
|
||||
public static final VMStorage xmm2 = vectorRegister(2, "xmm2");
|
||||
public static final VMStorage xmm3 = vectorRegister(3, "xmm3");
|
||||
public static final VMStorage xmm4 = vectorRegister(4, "xmm4");
|
||||
public static final VMStorage xmm5 = vectorRegister(5, "xmm5");
|
||||
public static final VMStorage xmm6 = vectorRegister(6, "xmm6");
|
||||
public static final VMStorage xmm7 = vectorRegister(7, "xmm7");
|
||||
public static final VMStorage xmm8 = vectorRegister(8, "xmm8");
|
||||
public static final VMStorage xmm9 = vectorRegister(9, "xmm9");
|
||||
public static final VMStorage xmm10 = vectorRegister(10, "xmm10");
|
||||
public static final VMStorage xmm11 = vectorRegister(11, "xmm11");
|
||||
public static final VMStorage xmm12 = vectorRegister(12, "xmm12");
|
||||
public static final VMStorage xmm13 = vectorRegister(13, "xmm13");
|
||||
public static final VMStorage xmm14 = vectorRegister(14, "xmm14");
|
||||
public static final VMStorage xmm15 = vectorRegister(15, "xmm15");
|
||||
public static final VMStorage xmm16 = vectorRegister(16, "xmm16");
|
||||
public static final VMStorage xmm17 = vectorRegister(17, "xmm17");
|
||||
public static final VMStorage xmm18 = vectorRegister(18, "xmm18");
|
||||
public static final VMStorage xmm19 = vectorRegister(19, "xmm19");
|
||||
public static final VMStorage xmm20 = vectorRegister(20, "xmm20");
|
||||
public static final VMStorage xmm21 = vectorRegister(21, "xmm21");
|
||||
public static final VMStorage xmm22 = vectorRegister(22, "xmm22");
|
||||
public static final VMStorage xmm23 = vectorRegister(23, "xmm23");
|
||||
public static final VMStorage xmm24 = vectorRegister(24, "xmm24");
|
||||
public static final VMStorage xmm25 = vectorRegister(25, "xmm25");
|
||||
public static final VMStorage xmm26 = vectorRegister(26, "xmm26");
|
||||
public static final VMStorage xmm27 = vectorRegister(27, "xmm27");
|
||||
public static final VMStorage xmm28 = vectorRegister(28, "xmm28");
|
||||
public static final VMStorage xmm29 = vectorRegister(29, "xmm29");
|
||||
public static final VMStorage xmm30 = vectorRegister(30, "xmm30");
|
||||
public static final VMStorage xmm31 = vectorRegister(31, "xmm31");
|
||||
}
|
||||
|
||||
public static final VMStorage rax = integerRegister(0, "rax");
|
||||
public static final VMStorage rcx = integerRegister(1, "rcx");
|
||||
public static final VMStorage rdx = integerRegister(2, "rdx");
|
||||
public static final VMStorage rbx = integerRegister(3, "rbx");
|
||||
public static final VMStorage rsp = integerRegister(4, "rsp");
|
||||
public static final VMStorage rbp = integerRegister(5, "rbp");
|
||||
public static final VMStorage rsi = integerRegister(6, "rsi");
|
||||
public static final VMStorage rdi = integerRegister(7, "rdi");
|
||||
public static final VMStorage r8 = integerRegister(8, "r8");
|
||||
public static final VMStorage r9 = integerRegister(9, "r9");
|
||||
public static final VMStorage r10 = integerRegister(10, "r10");
|
||||
public static final VMStorage r11 = integerRegister(11, "r11");
|
||||
public static final VMStorage r12 = integerRegister(12, "r12");
|
||||
public static final VMStorage r13 = integerRegister(13, "r13");
|
||||
public static final VMStorage r14 = integerRegister(14, "r14");
|
||||
public static final VMStorage r15 = integerRegister(15, "r15");
|
||||
|
||||
public static final VMStorage xmm0 = vectorRegister(0, "xmm0");
|
||||
public static final VMStorage xmm1 = vectorRegister(1, "xmm1");
|
||||
public static final VMStorage xmm2 = vectorRegister(2, "xmm2");
|
||||
public static final VMStorage xmm3 = vectorRegister(3, "xmm3");
|
||||
public static final VMStorage xmm4 = vectorRegister(4, "xmm4");
|
||||
public static final VMStorage xmm5 = vectorRegister(5, "xmm5");
|
||||
public static final VMStorage xmm6 = vectorRegister(6, "xmm6");
|
||||
public static final VMStorage xmm7 = vectorRegister(7, "xmm7");
|
||||
public static final VMStorage xmm8 = vectorRegister(8, "xmm8");
|
||||
public static final VMStorage xmm9 = vectorRegister(9, "xmm9");
|
||||
public static final VMStorage xmm10 = vectorRegister(10, "xmm10");
|
||||
public static final VMStorage xmm11 = vectorRegister(11, "xmm11");
|
||||
public static final VMStorage xmm12 = vectorRegister(12, "xmm12");
|
||||
public static final VMStorage xmm13 = vectorRegister(13, "xmm13");
|
||||
public static final VMStorage xmm14 = vectorRegister(14, "xmm14");
|
||||
public static final VMStorage xmm15 = vectorRegister(15, "xmm15");
|
||||
public static final VMStorage xmm16 = vectorRegister(16, "xmm16");
|
||||
public static final VMStorage xmm17 = vectorRegister(17, "xmm17");
|
||||
public static final VMStorage xmm18 = vectorRegister(18, "xmm18");
|
||||
public static final VMStorage xmm19 = vectorRegister(19, "xmm19");
|
||||
public static final VMStorage xmm20 = vectorRegister(20, "xmm20");
|
||||
public static final VMStorage xmm21 = vectorRegister(21, "xmm21");
|
||||
public static final VMStorage xmm22 = vectorRegister(22, "xmm22");
|
||||
public static final VMStorage xmm23 = vectorRegister(23, "xmm23");
|
||||
public static final VMStorage xmm24 = vectorRegister(24, "xmm24");
|
||||
public static final VMStorage xmm25 = vectorRegister(25, "xmm25");
|
||||
public static final VMStorage xmm26 = vectorRegister(26, "xmm26");
|
||||
public static final VMStorage xmm27 = vectorRegister(27, "xmm27");
|
||||
public static final VMStorage xmm28 = vectorRegister(28, "xmm28");
|
||||
public static final VMStorage xmm29 = vectorRegister(29, "xmm29");
|
||||
public static final VMStorage xmm30 = vectorRegister(30, "xmm30");
|
||||
public static final VMStorage xmm31 = vectorRegister(31, "xmm31");
|
||||
|
||||
private static VMStorage integerRegister(int index, String debugName) {
|
||||
return new VMStorage(StorageClasses.INTEGER, index, debugName);
|
||||
return new VMStorage(StorageType.INTEGER, REG64_MASK, index, debugName);
|
||||
}
|
||||
|
||||
private static VMStorage vectorRegister(int index, String debugName) {
|
||||
return new VMStorage(StorageClasses.VECTOR, index, debugName);
|
||||
return new VMStorage(StorageType.VECTOR, XMM_MASK, index, debugName);
|
||||
}
|
||||
|
||||
public static VMStorage stackStorage(int index) {
|
||||
return new VMStorage(StorageClasses.STACK, index, "Stack@" + index);
|
||||
public static VMStorage stackStorage(short size, int byteOffset) {
|
||||
return new VMStorage(StorageType.STACK, size, byteOffset);
|
||||
}
|
||||
|
||||
public static VMStorage x87Storage(int index) {
|
||||
return new VMStorage(StorageClasses.X87, index, "X87(" + index + ")");
|
||||
return new VMStorage(StorageType.X87, STP_MASK, index, "X87(" + index + ")");
|
||||
}
|
||||
|
||||
public static ABIDescriptor abiFor(VMStorage[] inputIntRegs, VMStorage[] inputVectorRegs, VMStorage[] outputIntRegs,
|
||||
VMStorage[] outputVectorRegs, int numX87Outputs, VMStorage[] volatileIntRegs,
|
||||
VMStorage[] volatileVectorRegs, int stackAlignment, int shadowSpace,
|
||||
VMStorage targetAddrStorage, VMStorage retBufAddrStorage) {
|
||||
VMStorage scratch1, VMStorage scratch2) {
|
||||
return new ABIDescriptor(
|
||||
INSTANCE,
|
||||
new VMStorage[][] {
|
||||
|
@ -153,7 +163,10 @@ public class X86_64Architecture implements Architecture {
|
|||
},
|
||||
stackAlignment,
|
||||
shadowSpace,
|
||||
targetAddrStorage, retBufAddrStorage);
|
||||
scratch1, scratch2,
|
||||
StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER),
|
||||
StubLocations.RETURN_BUFFER.storage(StorageType.PLACEHOLDER),
|
||||
StubLocations.CAPTURED_STATE_BUFFER.storage(StorageType.PLACEHOLDER));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -31,9 +31,11 @@ import jdk.internal.foreign.abi.Binding;
|
|||
import jdk.internal.foreign.abi.CallingSequence;
|
||||
import jdk.internal.foreign.abi.CallingSequenceBuilder;
|
||||
import jdk.internal.foreign.abi.DowncallLinker;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.UpcallLinker;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
import jdk.internal.foreign.abi.x64.X86_64Architecture;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
|
@ -49,6 +51,7 @@ import java.util.Optional;
|
|||
import static jdk.internal.foreign.PlatformLayouts.SysV;
|
||||
import static jdk.internal.foreign.abi.Binding.vmStore;
|
||||
import static jdk.internal.foreign.abi.x64.X86_64Architecture.*;
|
||||
import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*;
|
||||
|
||||
/**
|
||||
* For the SysV x64 C ABI specifically, this class uses namely CallingSequenceBuilder
|
||||
|
@ -57,9 +60,11 @@ import static jdk.internal.foreign.abi.x64.X86_64Architecture.*;
|
|||
* This includes taking care of synthetic arguments like pointers to return buffers for 'in-memory' returns.
|
||||
*/
|
||||
public class CallArranger {
|
||||
public static final int MAX_INTEGER_ARGUMENT_REGISTERS = 6;
|
||||
public static final int MAX_VECTOR_ARGUMENT_REGISTERS = 8;
|
||||
private static final ABIDescriptor CSysV = abiFor(
|
||||
private static final int STACK_SLOT_SIZE = 8;
|
||||
private static final int MAX_INTEGER_ARGUMENT_REGISTERS = 6;
|
||||
private static final int MAX_VECTOR_ARGUMENT_REGISTERS = 8;
|
||||
|
||||
private static final ABIDescriptor CSysV = X86_64Architecture.abiFor(
|
||||
new VMStorage[] { rdi, rsi, rdx, rcx, r8, r9, rax },
|
||||
new VMStorage[] { xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7 },
|
||||
new VMStorage[] { rax, rdx },
|
||||
|
@ -69,8 +74,7 @@ public class CallArranger {
|
|||
new VMStorage[] { xmm8, xmm9, xmm10, xmm11, xmm12, xmm13, xmm14, xmm15 },
|
||||
16,
|
||||
0, //no shadow space
|
||||
r10, // target addr reg
|
||||
r11 // ret buf addr reg
|
||||
r10, r11 // scratch 1 & 2
|
||||
);
|
||||
|
||||
public record Bindings(
|
||||
|
@ -80,7 +84,11 @@ public class CallArranger {
|
|||
}
|
||||
|
||||
public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
|
||||
CallingSequenceBuilder csb = new CallingSequenceBuilder(CSysV, forUpcall);
|
||||
return getBindings(mt, cDesc, forUpcall, LinkerOptions.empty());
|
||||
}
|
||||
|
||||
public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall, LinkerOptions options) {
|
||||
CallingSequenceBuilder csb = new CallingSequenceBuilder(CSysV, forUpcall, options);
|
||||
|
||||
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true);
|
||||
BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false);
|
||||
|
@ -111,14 +119,14 @@ public class CallArranger {
|
|||
return new Bindings(csb.build(), returnInMemory, argCalc.storageCalculator.nVectorReg);
|
||||
}
|
||||
|
||||
public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc) {
|
||||
Bindings bindings = getBindings(mt, cDesc, false);
|
||||
public static MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
|
||||
Bindings bindings = getBindings(mt, cDesc, false, options);
|
||||
|
||||
MethodHandle handle = new DowncallLinker(CSysV, bindings.callingSequence).getBoundMethodHandle();
|
||||
handle = MethodHandles.insertArguments(handle, handle.type().parameterCount() - 1, bindings.nVectorArgs);
|
||||
|
||||
if (bindings.isInMemoryReturn) {
|
||||
handle = SharedUtils.adaptDowncallForIMR(handle, cDesc);
|
||||
handle = SharedUtils.adaptDowncallForIMR(handle, cDesc, bindings.callingSequence);
|
||||
}
|
||||
|
||||
return handle;
|
||||
|
@ -153,15 +161,15 @@ public class CallArranger {
|
|||
}
|
||||
|
||||
private int maxRegisterArguments(int type) {
|
||||
return type == StorageClasses.INTEGER ?
|
||||
return type == StorageType.INTEGER ?
|
||||
MAX_INTEGER_ARGUMENT_REGISTERS :
|
||||
MAX_VECTOR_ARGUMENT_REGISTERS;
|
||||
}
|
||||
|
||||
VMStorage stackAlloc() {
|
||||
assert forArguments : "no stack returns";
|
||||
VMStorage storage = stackStorage((int)stackOffset);
|
||||
stackOffset++;
|
||||
VMStorage storage = X86_64Architecture.stackStorage((short) STACK_SLOT_SIZE, (int)stackOffset);
|
||||
stackOffset += STACK_SLOT_SIZE;
|
||||
return storage;
|
||||
}
|
||||
|
||||
|
@ -199,23 +207,23 @@ public class CallArranger {
|
|||
VMStorage[] storage = new VMStorage[(int)(nIntegerReg + nVectorReg)];
|
||||
for (int i = 0 ; i < typeClass.classes.size() ; i++) {
|
||||
boolean sse = typeClass.classes.get(i) == ArgumentClassImpl.SSE;
|
||||
storage[i] = nextStorage(sse ? StorageClasses.VECTOR : StorageClasses.INTEGER);
|
||||
storage[i] = nextStorage(sse ? StorageType.VECTOR : StorageType.INTEGER);
|
||||
}
|
||||
return storage;
|
||||
}
|
||||
|
||||
int registerCount(int type) {
|
||||
return switch (type) {
|
||||
case StorageClasses.INTEGER -> nIntegerReg;
|
||||
case StorageClasses.VECTOR -> nVectorReg;
|
||||
case StorageType.INTEGER -> nIntegerReg;
|
||||
case StorageType.VECTOR -> nVectorReg;
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
||||
void incrementRegisterCount(int type) {
|
||||
switch (type) {
|
||||
case StorageClasses.INTEGER -> nIntegerReg++;
|
||||
case StorageClasses.VECTOR -> nVectorReg++;
|
||||
case StorageType.INTEGER -> nIntegerReg++;
|
||||
case StorageType.VECTOR -> nVectorReg++;
|
||||
default -> throw new IllegalStateException();
|
||||
}
|
||||
}
|
||||
|
@ -253,7 +261,7 @@ public class CallArranger {
|
|||
if (offset + copy < layout.byteSize()) {
|
||||
bindings.dup();
|
||||
}
|
||||
boolean useFloat = storage.type() == StorageClasses.VECTOR;
|
||||
boolean useFloat = storage.type() == StorageType.VECTOR;
|
||||
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, useFloat);
|
||||
bindings.bufferLoad(offset, type)
|
||||
.vmStore(storage, type);
|
||||
|
@ -262,15 +270,15 @@ public class CallArranger {
|
|||
}
|
||||
case POINTER -> {
|
||||
bindings.unboxAddress();
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmStore(storage, long.class);
|
||||
}
|
||||
case INTEGER -> {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmStore(storage, carrier);
|
||||
}
|
||||
case FLOAT -> {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.VECTOR);
|
||||
bindings.vmStore(storage, carrier);
|
||||
}
|
||||
default -> throw new UnsupportedOperationException("Unhandled class " + argumentClass);
|
||||
|
@ -300,7 +308,7 @@ public class CallArranger {
|
|||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
VMStorage storage = regs[regIndex++];
|
||||
bindings.dup();
|
||||
boolean useFloat = storage.type() == StorageClasses.VECTOR;
|
||||
boolean useFloat = storage.type() == StorageType.VECTOR;
|
||||
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, useFloat);
|
||||
bindings.vmLoad(storage, type)
|
||||
.bufferStore(offset, type);
|
||||
|
@ -308,16 +316,16 @@ public class CallArranger {
|
|||
}
|
||||
}
|
||||
case POINTER -> {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmLoad(storage, long.class)
|
||||
.boxAddressRaw(Utils.pointeeSize(layout));
|
||||
}
|
||||
case INTEGER -> {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmLoad(storage, carrier);
|
||||
}
|
||||
case FLOAT -> {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.VECTOR);
|
||||
bindings.vmLoad(storage, carrier);
|
||||
}
|
||||
default -> throw new UnsupportedOperationException("Unhandled class " + argumentClass);
|
||||
|
|
|
@ -54,7 +54,7 @@ public final class SysVx64Linker extends AbstractLinker {
|
|||
}
|
||||
@Override
|
||||
protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) {
|
||||
return CallArranger.arrangeDowncall(inferredMethodType, function);
|
||||
return CallArranger.arrangeDowncall(inferredMethodType, function, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -48,6 +48,7 @@ import java.util.Optional;
|
|||
|
||||
import static jdk.internal.foreign.PlatformLayouts.Win64;
|
||||
import static jdk.internal.foreign.abi.x64.X86_64Architecture.*;
|
||||
import static jdk.internal.foreign.abi.x64.X86_64Architecture.Regs.*;
|
||||
|
||||
/**
|
||||
* For the Windowx x64 C ABI specifically, this class uses CallingSequenceBuilder
|
||||
|
@ -69,8 +70,7 @@ public class CallArranger {
|
|||
new VMStorage[] { xmm4, xmm5 },
|
||||
16,
|
||||
32,
|
||||
r10, // target addr reg
|
||||
r11 // ret buf addr reg
|
||||
r10, r11 // scratch 1 & 2
|
||||
);
|
||||
|
||||
public record Bindings(
|
||||
|
@ -84,7 +84,7 @@ public class CallArranger {
|
|||
|
||||
public static Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall, LinkerOptions options) {
|
||||
class CallingSequenceBuilderHelper {
|
||||
final CallingSequenceBuilder csb = new CallingSequenceBuilder(CWindows, forUpcall);
|
||||
final CallingSequenceBuilder csb = new CallingSequenceBuilder(CWindows, forUpcall, options);
|
||||
final BindingCalculator argCalc =
|
||||
forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true);
|
||||
final BindingCalculator retCalc =
|
||||
|
@ -125,7 +125,7 @@ public class CallArranger {
|
|||
MethodHandle handle = new DowncallLinker(CWindows, bindings.callingSequence).getBoundMethodHandle();
|
||||
|
||||
if (bindings.isInMemoryReturn) {
|
||||
handle = SharedUtils.adaptDowncallForIMR(handle, cDesc);
|
||||
handle = SharedUtils.adaptDowncallForIMR(handle, cDesc, bindings.callingSequence);
|
||||
}
|
||||
|
||||
return handle;
|
||||
|
@ -164,7 +164,7 @@ public class CallArranger {
|
|||
// stack
|
||||
assert stackOffset == Utils.alignUp(stackOffset, STACK_SLOT_SIZE); // should always be aligned
|
||||
|
||||
VMStorage storage = X86_64Architecture.stackStorage((int) (stackOffset / STACK_SLOT_SIZE));
|
||||
VMStorage storage = X86_64Architecture.stackStorage((short) STACK_SLOT_SIZE, (int) stackOffset);
|
||||
stackOffset += STACK_SLOT_SIZE;
|
||||
return storage;
|
||||
}
|
||||
|
@ -176,7 +176,7 @@ public class CallArranger {
|
|||
|
||||
public VMStorage extraVarargsStorage() {
|
||||
assert forArguments;
|
||||
return CWindows.inputStorage[StorageClasses.INTEGER][nRegs - 1];
|
||||
return CWindows.inputStorage[StorageType.INTEGER][nRegs - 1];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -198,7 +198,7 @@ public class CallArranger {
|
|||
switch (argumentClass) {
|
||||
case STRUCT_REGISTER: {
|
||||
assert carrier == MemorySegment.class;
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
Class<?> type = SharedUtils.primitiveCarrierForSize(layout.byteSize(), false);
|
||||
bindings.bufferLoad(0, type)
|
||||
.vmStore(storage, type);
|
||||
|
@ -208,28 +208,28 @@ public class CallArranger {
|
|||
assert carrier == MemorySegment.class;
|
||||
bindings.copy(layout)
|
||||
.unboxAddress();
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmStore(storage, long.class);
|
||||
break;
|
||||
}
|
||||
case POINTER: {
|
||||
bindings.unboxAddress();
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmStore(storage, long.class);
|
||||
break;
|
||||
}
|
||||
case INTEGER: {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmStore(storage, carrier);
|
||||
break;
|
||||
}
|
||||
case FLOAT: {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.VECTOR);
|
||||
bindings.vmStore(storage, carrier);
|
||||
break;
|
||||
}
|
||||
case VARARG_FLOAT: {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.VECTOR);
|
||||
if (!INSTANCE.isStackType(storage.type())) { // need extra for register arg
|
||||
VMStorage extraStorage = storageCalculator.extraVarargsStorage();
|
||||
bindings.dup()
|
||||
|
@ -262,7 +262,7 @@ public class CallArranger {
|
|||
assert carrier == MemorySegment.class;
|
||||
bindings.allocate(layout)
|
||||
.dup();
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
Class<?> type = SharedUtils.primitiveCarrierForSize(layout.byteSize(), false);
|
||||
bindings.vmLoad(storage, type)
|
||||
.bufferStore(0, type);
|
||||
|
@ -270,24 +270,24 @@ public class CallArranger {
|
|||
}
|
||||
case STRUCT_REFERENCE: {
|
||||
assert carrier == MemorySegment.class;
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmLoad(storage, long.class)
|
||||
.boxAddress(layout);
|
||||
break;
|
||||
}
|
||||
case POINTER: {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmLoad(storage, long.class)
|
||||
.boxAddressRaw(Utils.pointeeSize(layout));
|
||||
break;
|
||||
}
|
||||
case INTEGER: {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.INTEGER);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.INTEGER);
|
||||
bindings.vmLoad(storage, carrier);
|
||||
break;
|
||||
}
|
||||
case FLOAT: {
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageClasses.VECTOR);
|
||||
VMStorage storage = storageCalculator.nextStorage(StorageType.VECTOR);
|
||||
bindings.vmLoad(storage, carrier);
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue