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:
Jorn Vernee 2022-12-05 14:47:12 +00:00
parent 73baadceb6
commit 0452c39fec
72 changed files with 2584 additions and 933 deletions

View file

@ -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());
}
}
}
}

View file

@ -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;
}
}

View file

@ -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();

View file

@ -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();
}

View file

@ -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();

View file

@ -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() {

View file

@ -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;
}
}

View file

@ -85,7 +85,8 @@ public class DowncallLinker {
toStorageArray(argMoves),
toStorageArray(retMoves),
leafType,
callingSequence.needsReturnBuffer()
callingSequence.needsReturnBuffer(),
callingSequence.capturedStateMask()
);
MethodHandle handle = JLIA.nativeMethodHandle(nep);

View file

@ -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)
);
}
}
}

View file

@ -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) {

View file

@ -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

View file

@ -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());
}
}

View file

@ -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 + '\'' +
'}';
}
}

View file

@ -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));
}
}

View file

@ -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);

View file

@ -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));
}
}

View file

@ -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);

View file

@ -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

View file

@ -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;
}