8303409: Add Windows AArch64 ABI support to the Foreign Function & Memory API
Reviewed-by: jvernee
This commit is contained in:
parent
c9afd55ed6
commit
fb13063943
@ -176,6 +176,10 @@ static void move_stack(MacroAssembler* masm, Register tmp_reg, int in_stk_bias,
|
||||
static void move_v128(MacroAssembler* masm, int out_stk_bias,
|
||||
FloatRegister from_reg, VMStorage to_reg) {
|
||||
switch (to_reg.type()) {
|
||||
case StorageType::INTEGER:
|
||||
assert(to_reg.segment_mask() == REG64_MASK, "only moves to 64-bit registers supported");
|
||||
masm->fmovd(as_Register(to_reg), from_reg);
|
||||
break;
|
||||
case StorageType::VECTOR:
|
||||
assert(to_reg.segment_mask() == V128_MASK, "only moves to v128 registers supported");
|
||||
masm->fmovd(as_FloatRegister(to_reg), from_reg);
|
||||
|
@ -32,6 +32,7 @@ import java.util.function.Consumer;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList;
|
||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList;
|
||||
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64VaList;
|
||||
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64VaList;
|
||||
import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
|
||||
import jdk.internal.foreign.abi.x64.windows.WinVaList;
|
||||
@ -105,7 +106,7 @@ import jdk.internal.reflect.Reflection;
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, LinuxRISCV64VaList, SharedUtils.EmptyVaList {
|
||||
public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, WindowsAArch64VaList, LinuxRISCV64VaList, SharedUtils.EmptyVaList {
|
||||
|
||||
/**
|
||||
* Reads the next value as an {@code int} and advances this variable argument list's position. The behavior of this
|
||||
@ -300,7 +301,7 @@ public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList
|
||||
* @since 19
|
||||
*/
|
||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
||||
sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder, LinuxRISCV64VaList.Builder {
|
||||
sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder, WindowsAArch64VaList.Builder, LinuxRISCV64VaList.Builder {
|
||||
|
||||
/**
|
||||
* Writes an {@code int} value to the variable argument list being constructed.
|
||||
|
@ -33,6 +33,7 @@ public enum CABI {
|
||||
WIN_64,
|
||||
LINUX_AARCH_64,
|
||||
MAC_OS_AARCH_64,
|
||||
WIN_AARCH_64,
|
||||
LINUX_RISCV_64;
|
||||
|
||||
private static final CABI ABI;
|
||||
@ -55,6 +56,8 @@ public enum CABI {
|
||||
} else if (ARCH.equals("aarch64")) {
|
||||
if (OS.startsWith("Mac")) {
|
||||
ABI = MAC_OS_AARCH_64;
|
||||
} else if (OS.startsWith("Windows")) {
|
||||
ABI = WIN_AARCH_64;
|
||||
} else {
|
||||
// The Linux ABI follows the standard AAPCS ABI
|
||||
ABI = LINUX_AARCH_64;
|
||||
|
@ -59,7 +59,7 @@ public final class SystemLookup implements SymbolLookup {
|
||||
try {
|
||||
return switch (CABI.current()) {
|
||||
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> libLookup(libs -> libs.load(jdkLibraryPath("syslookup")));
|
||||
case WIN_64 -> makeWindowsLookup(); // out of line to workaround javac crash
|
||||
case WIN_64, WIN_AARCH_64 -> makeWindowsLookup(); // out of line to workaround javac crash
|
||||
};
|
||||
} catch (Throwable ex) {
|
||||
// This can happen in the event of a library loading failure - e.g. if one of the libraries the
|
||||
@ -120,7 +120,7 @@ public final class SystemLookup implements SymbolLookup {
|
||||
Path javahome = Path.of(GetPropertyAction.privilegedGetProperty("java.home"));
|
||||
String lib = switch (CABI.current()) {
|
||||
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> "lib";
|
||||
case WIN_64 -> "bin";
|
||||
case WIN_64, WIN_AARCH_64 -> "bin";
|
||||
};
|
||||
String libname = System.mapLibraryName(name);
|
||||
return javahome.resolve(lib).resolve(libname);
|
||||
|
@ -27,6 +27,7 @@ package jdk.internal.foreign.abi;
|
||||
import jdk.internal.foreign.SystemLookup;
|
||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
|
||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
|
||||
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker;
|
||||
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
|
||||
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
||||
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
||||
@ -44,7 +45,7 @@ import java.lang.invoke.MethodType;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker,
|
||||
SysVx64Linker, Windowsx64Linker, LinuxRISCV64Linker {
|
||||
SysVx64Linker, WindowsAArch64Linker, Windowsx64Linker, LinuxRISCV64Linker {
|
||||
|
||||
private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {}
|
||||
private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();
|
||||
|
@ -81,6 +81,11 @@ public class LinkerOptions {
|
||||
return stl == null ? Stream.empty() : stl.saved().stream();
|
||||
}
|
||||
|
||||
public boolean isVariadicFunction() {
|
||||
FirstVariadicArg fva = getOption(FirstVariadicArg.class);
|
||||
return fva != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -30,6 +30,7 @@ import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.foreign.CABI;
|
||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
|
||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
|
||||
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker;
|
||||
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
|
||||
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
||||
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
||||
@ -184,6 +185,7 @@ public final class SharedUtils {
|
||||
case SYS_V -> SysVx64Linker.getInstance();
|
||||
case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance();
|
||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance();
|
||||
case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance();
|
||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance();
|
||||
};
|
||||
}
|
||||
@ -297,6 +299,7 @@ public final class SharedUtils {
|
||||
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaList(actions, scope);
|
||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaList(actions, scope);
|
||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaList(actions, scope);
|
||||
case WIN_AARCH_64 -> WindowsAArch64Linker.newVaList(actions, scope);
|
||||
};
|
||||
}
|
||||
|
||||
@ -307,6 +310,7 @@ public final class SharedUtils {
|
||||
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaListOfAddress(address, scope);
|
||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaListOfAddress(address, scope);
|
||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaListOfAddress(address, scope);
|
||||
case WIN_AARCH_64 -> WindowsAArch64Linker.newVaListOfAddress(address, scope);
|
||||
};
|
||||
}
|
||||
|
||||
@ -317,6 +321,7 @@ public final class SharedUtils {
|
||||
case LINUX_AARCH_64 -> LinuxAArch64Linker.emptyVaList();
|
||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.emptyVaList();
|
||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.emptyVaList();
|
||||
case WIN_AARCH_64 -> WindowsAArch64Linker.emptyVaList();
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@ import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64CallArranger;
|
||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger;
|
||||
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64CallArranger;
|
||||
import jdk.internal.foreign.Utils;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
@ -60,7 +61,7 @@ import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
|
||||
*
|
||||
* There are minor differences between the ABIs implemented on Linux, macOS, and Windows
|
||||
* which are handled in sub-classes. Clients should access these through the provided
|
||||
* public constants CallArranger.LINUX and CallArranger.MACOS.
|
||||
* public constants CallArranger.LINUX, CallArranger.MACOS, and CallArranger.WINDOWS.
|
||||
*/
|
||||
public abstract class CallArranger {
|
||||
private static final int STACK_SLOT_SIZE = 8;
|
||||
@ -79,7 +80,7 @@ public abstract class CallArranger {
|
||||
// Although the AAPCS64 says r0-7 and v0-7 are all valid return
|
||||
// registers, it's not possible to generate a C function that uses
|
||||
// r2-7 and v4-7 so they are omitted here.
|
||||
private static final ABIDescriptor C = abiFor(
|
||||
protected static final ABIDescriptor C = abiFor(
|
||||
new VMStorage[] { r0, r1, r2, r3, r4, r5, r6, r7, INDIRECT_RESULT},
|
||||
new VMStorage[] { v0, v1, v2, v3, v4, v5, v6, v7 },
|
||||
new VMStorage[] { r0, r1 },
|
||||
@ -98,6 +99,7 @@ public abstract class CallArranger {
|
||||
|
||||
public static final CallArranger LINUX = new LinuxAArch64CallArranger();
|
||||
public static final CallArranger MACOS = new MacOsAArch64CallArranger();
|
||||
public static final CallArranger WINDOWS = new WindowsAArch64CallArranger();
|
||||
|
||||
/**
|
||||
* Are variadic arguments assigned to registers as in the standard calling
|
||||
@ -112,6 +114,31 @@ public abstract class CallArranger {
|
||||
*/
|
||||
protected abstract boolean requiresSubSlotStackPacking();
|
||||
|
||||
/**
|
||||
* Are floating point arguments to variadic functions passed in general purpose registers
|
||||
* instead of floating point registers?
|
||||
*
|
||||
* {@return true if this ABI uses general purpose registers for variadic floating point arguments.}
|
||||
*/
|
||||
protected abstract boolean useIntRegsForVariadicFloatingPointArgs();
|
||||
|
||||
/**
|
||||
* Should some fields of structs that assigned to registers be passed in registers when there
|
||||
* are not enough registers for all the fields of the struct?
|
||||
*
|
||||
* {@return true if this ABI passes some fields of a struct in registers.}
|
||||
*/
|
||||
protected abstract boolean spillsVariadicStructsPartially();
|
||||
|
||||
/**
|
||||
* @return The ABIDescriptor used by the CallArranger for the current platform.
|
||||
*/
|
||||
protected abstract ABIDescriptor abiDescriptor();
|
||||
|
||||
protected TypeClass getArgumentClassForBindings(MemoryLayout layout, boolean forVariadicFunction) {
|
||||
return TypeClass.classifyLayout(layout);
|
||||
}
|
||||
|
||||
protected CallArranger() {}
|
||||
|
||||
public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
|
||||
@ -119,10 +146,12 @@ public abstract class CallArranger {
|
||||
}
|
||||
|
||||
public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall, LinkerOptions options) {
|
||||
CallingSequenceBuilder csb = new CallingSequenceBuilder(C, forUpcall, options);
|
||||
CallingSequenceBuilder csb = new CallingSequenceBuilder(abiDescriptor(), forUpcall, options);
|
||||
|
||||
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true);
|
||||
BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false);
|
||||
boolean forVariadicFunction = options.isVariadicFunction();
|
||||
|
||||
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true, forVariadicFunction);
|
||||
BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false, forVariadicFunction) : new BoxBindingCalculator(false);
|
||||
|
||||
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
|
||||
if (returnInMemory) {
|
||||
@ -149,7 +178,7 @@ public abstract class CallArranger {
|
||||
public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
|
||||
Bindings bindings = getBindings(mt, cDesc, false, options);
|
||||
|
||||
MethodHandle handle = new DowncallLinker(C, bindings.callingSequence).getBoundMethodHandle();
|
||||
MethodHandle handle = new DowncallLinker(abiDescriptor(), bindings.callingSequence).getBoundMethodHandle();
|
||||
|
||||
if (bindings.isInMemoryReturn) {
|
||||
handle = SharedUtils.adaptDowncallForIMR(handle, cDesc, bindings.callingSequence);
|
||||
@ -165,7 +194,7 @@ public abstract class CallArranger {
|
||||
target = SharedUtils.adaptUpcallForIMR(target, true /* drop return, since we don't have bindings for it */);
|
||||
}
|
||||
|
||||
return UpcallLinker.make(C, target, bindings.callingSequence, session);
|
||||
return UpcallLinker.make(abiDescriptor(), target, bindings.callingSequence, session);
|
||||
}
|
||||
|
||||
private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) {
|
||||
@ -177,13 +206,15 @@ public abstract class CallArranger {
|
||||
|
||||
class StorageCalculator {
|
||||
private final boolean forArguments;
|
||||
private final boolean forVariadicFunction;
|
||||
private boolean forVarArgs = false;
|
||||
|
||||
private final int[] nRegs = new int[] { 0, 0 };
|
||||
private long stackOffset = 0;
|
||||
|
||||
public StorageCalculator(boolean forArguments) {
|
||||
public StorageCalculator(boolean forArguments, boolean forVariadicFunction) {
|
||||
this.forArguments = forArguments;
|
||||
this.forVariadicFunction = forVariadicFunction;
|
||||
}
|
||||
|
||||
void alignStack(long alignment) {
|
||||
@ -212,8 +243,9 @@ public abstract class CallArranger {
|
||||
|
||||
VMStorage[] regAlloc(int type, int count) {
|
||||
if (nRegs[type] + count <= MAX_REGISTER_ARGUMENTS) {
|
||||
ABIDescriptor abiDescriptor = abiDescriptor();
|
||||
VMStorage[] source =
|
||||
(forArguments ? C.inputStorage : C.outputStorage)[type];
|
||||
(forArguments ? abiDescriptor.inputStorage : abiDescriptor.outputStorage)[type];
|
||||
VMStorage[] result = new VMStorage[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
result[i] = source[nRegs[type]++];
|
||||
@ -228,10 +260,37 @@ public abstract class CallArranger {
|
||||
}
|
||||
|
||||
VMStorage[] regAlloc(int type, MemoryLayout layout) {
|
||||
return regAlloc(type, (int)Utils.alignUp(layout.byteSize(), 8) / 8);
|
||||
boolean spillRegistersPartially = forVariadicFunction && spillsVariadicStructsPartially();
|
||||
|
||||
return spillRegistersPartially ?
|
||||
regAllocPartial(type, layout) :
|
||||
regAlloc(type, requiredRegisters(layout));
|
||||
}
|
||||
|
||||
int requiredRegisters(MemoryLayout layout) {
|
||||
return (int)Utils.alignUp(layout.byteSize(), 8) / 8;
|
||||
}
|
||||
|
||||
VMStorage[] regAllocPartial(int type, MemoryLayout layout) {
|
||||
int availableRegisters = MAX_REGISTER_ARGUMENTS - nRegs[type];
|
||||
if (availableRegisters <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int requestRegisters = Math.min(requiredRegisters(layout), availableRegisters);
|
||||
return regAlloc(type, requestRegisters);
|
||||
}
|
||||
|
||||
VMStorage nextStorage(int type, MemoryLayout layout) {
|
||||
if (type == StorageType.VECTOR) {
|
||||
boolean forVariadicFunctionArgs = forArguments && forVariadicFunction;
|
||||
boolean useIntRegsForFloatingPointArgs = forVariadicFunctionArgs && useIntRegsForVariadicFloatingPointArgs();
|
||||
|
||||
if (useIntRegsForFloatingPointArgs) {
|
||||
type = StorageType.INTEGER;
|
||||
}
|
||||
}
|
||||
|
||||
VMStorage[] storage = regAlloc(type, 1);
|
||||
if (storage == null) {
|
||||
return stackAlloc(layout);
|
||||
@ -272,8 +331,8 @@ public abstract class CallArranger {
|
||||
abstract class BindingCalculator {
|
||||
protected final StorageCalculator storageCalculator;
|
||||
|
||||
protected BindingCalculator(boolean forArguments) {
|
||||
this.storageCalculator = new StorageCalculator(forArguments);
|
||||
protected BindingCalculator(boolean forArguments, boolean forVariadicFunction) {
|
||||
this.storageCalculator = new StorageCalculator(forArguments, forVariadicFunction);
|
||||
}
|
||||
|
||||
protected void spillStructUnbox(Binding.Builder bindings, MemoryLayout layout) {
|
||||
@ -282,7 +341,10 @@ public abstract class CallArranger {
|
||||
// struct, it must be passed on the stack. I.e. not split
|
||||
// between registers and stack.
|
||||
|
||||
long offset = 0;
|
||||
spillPartialStructUnbox(bindings, layout, 0);
|
||||
}
|
||||
|
||||
protected void spillPartialStructUnbox(Binding.Builder bindings, MemoryLayout layout, long offset) {
|
||||
while (offset < layout.byteSize()) {
|
||||
long copy = Math.min(layout.byteSize() - offset, STACK_SLOT_SIZE);
|
||||
VMStorage storage =
|
||||
@ -334,8 +396,13 @@ public abstract class CallArranger {
|
||||
}
|
||||
|
||||
class UnboxBindingCalculator extends BindingCalculator {
|
||||
UnboxBindingCalculator(boolean forArguments) {
|
||||
super(forArguments);
|
||||
protected final boolean forArguments;
|
||||
protected final boolean forVariadicFunction;
|
||||
|
||||
UnboxBindingCalculator(boolean forArguments, boolean forVariadicFunction) {
|
||||
super(forArguments, forVariadicFunction);
|
||||
this.forArguments = forArguments;
|
||||
this.forVariadicFunction = forVariadicFunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -348,21 +415,21 @@ public abstract class CallArranger {
|
||||
|
||||
@Override
|
||||
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
|
||||
TypeClass argumentClass = TypeClass.classifyLayout(layout);
|
||||
TypeClass argumentClass = getArgumentClassForBindings(layout, forVariadicFunction);
|
||||
Binding.Builder bindings = Binding.builder();
|
||||
|
||||
switch (argumentClass) {
|
||||
case STRUCT_REGISTER: {
|
||||
assert carrier == MemorySegment.class;
|
||||
VMStorage[] regs = storageCalculator.regAlloc(
|
||||
StorageType.INTEGER, layout);
|
||||
VMStorage[] regs = storageCalculator.regAlloc(StorageType.INTEGER, layout);
|
||||
|
||||
if (regs != null) {
|
||||
int regIndex = 0;
|
||||
long offset = 0;
|
||||
while (offset < layout.byteSize()) {
|
||||
while (offset < layout.byteSize() && regIndex < regs.length) {
|
||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||
VMStorage storage = regs[regIndex++];
|
||||
boolean useFloat = storage.type() == StorageType.VECTOR;
|
||||
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, useFloat);
|
||||
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, false);
|
||||
if (offset + copy < layout.byteSize()) {
|
||||
bindings.dup();
|
||||
}
|
||||
@ -370,6 +437,11 @@ public abstract class CallArranger {
|
||||
.vmStore(storage, type);
|
||||
offset += copy;
|
||||
}
|
||||
|
||||
final long bytesLeft = Math.min(layout.byteSize() - offset, 8);
|
||||
if (bytesLeft > 0) {
|
||||
spillPartialStructUnbox(bindings, layout, offset);
|
||||
}
|
||||
} else {
|
||||
spillStructUnbox(bindings, layout);
|
||||
}
|
||||
@ -435,7 +507,7 @@ public abstract class CallArranger {
|
||||
|
||||
class BoxBindingCalculator extends BindingCalculator {
|
||||
BoxBindingCalculator(boolean forArguments) {
|
||||
super(forArguments);
|
||||
super(forArguments, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -26,6 +26,7 @@
|
||||
package jdk.internal.foreign.abi.aarch64.linux;
|
||||
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
import jdk.internal.foreign.abi.ABIDescriptor;
|
||||
|
||||
/**
|
||||
* AArch64 CallArranger specialized for Linux ABI.
|
||||
@ -43,4 +44,19 @@ public class LinuxAArch64CallArranger extends CallArranger {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ABIDescriptor abiDescriptor() {
|
||||
return C;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean useIntRegsForVariadicFloatingPointArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean spillsVariadicStructsPartially() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
package jdk.internal.foreign.abi.aarch64.macos;
|
||||
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
import jdk.internal.foreign.abi.ABIDescriptor;
|
||||
|
||||
/**
|
||||
* AArch64 CallArranger specialized for macOS ABI.
|
||||
@ -43,4 +44,19 @@ public class MacOsAArch64CallArranger extends CallArranger {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ABIDescriptor abiDescriptor() {
|
||||
return C;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean useIntRegsForVariadicFloatingPointArgs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean spillsVariadicStructsPartially() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,115 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 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
|
||||
* 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.aarch64.windows;
|
||||
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
import jdk.internal.foreign.abi.aarch64.TypeClass;
|
||||
import jdk.internal.foreign.abi.ABIDescriptor;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
|
||||
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
|
||||
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
|
||||
|
||||
/**
|
||||
* AArch64 CallArranger specialized for Windows ABI.
|
||||
*/
|
||||
public class WindowsAArch64CallArranger extends CallArranger {
|
||||
|
||||
private static final VMStorage INDIRECT_RESULT = r8;
|
||||
|
||||
// This is derived from the AAPCS64 spec, restricted to what's
|
||||
// possible when calling to/from C code.
|
||||
//
|
||||
// The indirect result register, r8, is used to return a large
|
||||
// struct by value. It's treated as an input here as the caller is
|
||||
// responsible for allocating storage and passing this into the
|
||||
// function.
|
||||
//
|
||||
// Although the AAPCS64 says r0-7 and v0-7 are all valid return
|
||||
// registers, it's not possible to generate a C function that uses
|
||||
// r2-7 and v4-7 so they are omitted here.
|
||||
private static final ABIDescriptor WindowsAArch64AbiDescriptor = abiFor(
|
||||
new VMStorage[] { r0, r1, r2, r3, r4, r5, r6, r7, INDIRECT_RESULT},
|
||||
new VMStorage[] { v0, v1, v2, v3, v4, v5, v6, v7 },
|
||||
new VMStorage[] { r0, r1 },
|
||||
new VMStorage[] { v0, v1, v2, v3 },
|
||||
new VMStorage[] { r9, r10, r11, r12, r13, r14, r15, r16, r17 },
|
||||
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
|
||||
);
|
||||
|
||||
@Override
|
||||
protected ABIDescriptor abiDescriptor() {
|
||||
return WindowsAArch64AbiDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean varArgsOnStack() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean requiresSubSlotStackPacking() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean useIntRegsForVariadicFloatingPointArgs() {
|
||||
// The Windows ABI requires floating point arguments to be passed in
|
||||
// general purpose registers when calling variadic functions.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean spillsVariadicStructsPartially() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TypeClass getArgumentClassForBindings(MemoryLayout layout, boolean forVariadicFunction) {
|
||||
TypeClass argumentClass = TypeClass.classifyLayout(layout);
|
||||
|
||||
// HFA struct arguments are classified as STRUCT_REGISTER when
|
||||
// general purpose registers are being used to pass floating point
|
||||
// arguments. If the HFA is too big to pass entirely in general
|
||||
// purpose registers, it is classified as an ordinary struct
|
||||
// (i.e. as a STRUCT_REFERENCE).
|
||||
if (argumentClass == TypeClass.STRUCT_HFA && forVariadicFunction) {
|
||||
// The Windows ABI requires the members of the variadic HFA to be
|
||||
// passed in general purpose registers but only a STRUCT_HFA that
|
||||
// is at most 16 bytes can be passed in general purpose registers.
|
||||
argumentClass = layout.byteSize() <= 16 ? TypeClass.STRUCT_REGISTER : TypeClass.STRUCT_REFERENCE;
|
||||
}
|
||||
|
||||
return argumentClass;
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, Arm Limited. All rights reserved.
|
||||
* Copyright (c) 2021, 2022, Microsoft. 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.aarch64.windows;
|
||||
|
||||
import jdk.internal.foreign.abi.AbstractLinker;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* ABI implementation for Windows/AArch64. Based on AAPCS with
|
||||
* changes to va_list.
|
||||
*/
|
||||
public final class WindowsAArch64Linker extends AbstractLinker {
|
||||
private static WindowsAArch64Linker instance;
|
||||
|
||||
public static WindowsAArch64Linker getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new WindowsAArch64Linker();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) {
|
||||
return CallArranger.WINDOWS.arrangeDowncall(inferredMethodType, function, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, SegmentScope scope) {
|
||||
return CallArranger.WINDOWS.arrangeUpcall(target, targetType, function, scope);
|
||||
}
|
||||
|
||||
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
|
||||
WindowsAArch64VaList.Builder builder = WindowsAArch64VaList.builder(scope);
|
||||
actions.accept(builder);
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
|
||||
return WindowsAArch64VaList.ofAddress(address, scope);
|
||||
}
|
||||
|
||||
public static VaList emptyVaList() {
|
||||
return WindowsAArch64VaList.empty();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,261 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2021, 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
|
||||
* 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.aarch64.windows;
|
||||
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.VaList;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import jdk.internal.foreign.abi.aarch64.TypeClass;
|
||||
import jdk.internal.foreign.MemorySessionImpl;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
|
||||
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static jdk.internal.foreign.PlatformLayouts.AArch64.C_POINTER;
|
||||
import static jdk.internal.foreign.abi.SharedUtils.alignUp;
|
||||
|
||||
// see vadefs.h (VC header) for the ARM64 va_arg impl
|
||||
//
|
||||
// typedef char* va_list;
|
||||
//
|
||||
// #define __crt_va_arg(ap, t) \
|
||||
// ((sizeof(t) > (2 * sizeof(__int64))) \
|
||||
// ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \
|
||||
// : *(t*)((ap += _SLOTSIZEOF(t) + _APALIGN(t,ap)) - _SLOTSIZEOF(t)))
|
||||
//
|
||||
public non-sealed class WindowsAArch64VaList implements VaList {
|
||||
private static final long VA_SLOT_SIZE_BYTES = 8;
|
||||
private static final VarHandle VH_address = C_POINTER.varHandle();
|
||||
|
||||
private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL);
|
||||
|
||||
private MemorySegment segment;
|
||||
|
||||
private WindowsAArch64VaList(MemorySegment segment) {
|
||||
this.segment = segment;
|
||||
}
|
||||
|
||||
public static VaList empty() {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int nextVarg(ValueLayout.OfInt layout) {
|
||||
return (int) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long nextVarg(ValueLayout.OfLong layout) {
|
||||
return (long) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double nextVarg(ValueLayout.OfDouble layout) {
|
||||
return (double) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
|
||||
return (MemorySegment) read(layout);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(allocator);
|
||||
return (MemorySegment) read(layout, allocator);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout) {
|
||||
return read(layout, SharedUtils.THROWING_ALLOCATOR);
|
||||
}
|
||||
|
||||
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
Objects.requireNonNull(layout);
|
||||
Object res;
|
||||
if (layout instanceof GroupLayout) {
|
||||
TypeClass typeClass = TypeClass.classifyLayout(layout);
|
||||
res = switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
checkElement(layout, VA_SLOT_SIZE_BYTES);
|
||||
MemorySegment structAddr = (MemorySegment) VH_address.get(segment);
|
||||
MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope());
|
||||
MemorySegment seg = allocator.allocate(layout);
|
||||
seg.copyFrom(struct);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
yield seg;
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> {
|
||||
long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
checkElement(layout, size);
|
||||
MemorySegment struct = allocator.allocate(layout)
|
||||
.copyFrom(segment.asSlice(0, layout.byteSize()));
|
||||
segment = segment.asSlice(size);
|
||||
yield struct;
|
||||
}
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
};
|
||||
} else {
|
||||
checkElement(layout, VA_SLOT_SIZE_BYTES);
|
||||
VarHandle reader = layout.varHandle();
|
||||
res = reader.get(segment);
|
||||
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private static long sizeOf(MemoryLayout layout) {
|
||||
return switch (TypeClass.classifyLayout(layout)) {
|
||||
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
|
||||
default -> VA_SLOT_SIZE_BYTES;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void skip(MemoryLayout... layouts) {
|
||||
Objects.requireNonNull(layouts);
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
|
||||
for (MemoryLayout layout : layouts) {
|
||||
Objects.requireNonNull(layout);
|
||||
long size = sizeOf(layout);
|
||||
checkElement(layout, size);
|
||||
segment = segment.asSlice(size);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkElement(MemoryLayout layout, long size) {
|
||||
if (segment.byteSize() < size) {
|
||||
throw SharedUtils.newVaListNSEE(layout);
|
||||
}
|
||||
}
|
||||
|
||||
static WindowsAArch64VaList ofAddress(long address, SegmentScope session) {
|
||||
MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, session);
|
||||
return new WindowsAArch64VaList(segment);
|
||||
}
|
||||
|
||||
static Builder builder(SegmentScope session) {
|
||||
return new Builder(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VaList copy() {
|
||||
((MemorySessionImpl) segment.scope()).checkValidState();
|
||||
return new WindowsAArch64VaList(segment);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MemorySegment segment() {
|
||||
// make sure that returned segment cannot be accessed
|
||||
return segment.asSlice(0, 0);
|
||||
}
|
||||
|
||||
public static non-sealed class Builder implements VaList.Builder {
|
||||
|
||||
private final SegmentScope session;
|
||||
private final List<SimpleVaArg> args = new ArrayList<>();
|
||||
|
||||
public Builder(SegmentScope session) {
|
||||
((MemorySessionImpl) session).checkValidState();
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
private Builder arg(MemoryLayout layout, Object value) {
|
||||
Objects.requireNonNull(layout);
|
||||
Objects.requireNonNull(value);
|
||||
args.add(new SimpleVaArg(layout, value));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfInt layout, int value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfLong layout, long value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Builder addVarg(GroupLayout layout, MemorySegment value) {
|
||||
return arg(layout, value);
|
||||
}
|
||||
|
||||
public VaList build() {
|
||||
if (args.isEmpty()) {
|
||||
return EMPTY;
|
||||
}
|
||||
|
||||
long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum);
|
||||
MemorySegment segment = MemorySegment.allocateNative(allocationSize, session);
|
||||
MemorySegment cursor = segment;
|
||||
|
||||
for (SimpleVaArg arg : args) {
|
||||
if (arg.layout instanceof GroupLayout) {
|
||||
MemorySegment msArg = ((MemorySegment) arg.value);
|
||||
TypeClass typeClass = TypeClass.classifyLayout(arg.layout);
|
||||
switch (typeClass) {
|
||||
case STRUCT_REFERENCE -> {
|
||||
MemorySegment copy = MemorySegment.allocateNative(arg.layout, session);
|
||||
copy.copyFrom(msArg); // by-value
|
||||
VH_address.set(cursor, copy);
|
||||
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
case STRUCT_REGISTER, STRUCT_HFA ->
|
||||
cursor = cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize()))
|
||||
.asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES));
|
||||
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
|
||||
}
|
||||
} else {
|
||||
VarHandle writer = arg.varHandle();
|
||||
writer.set(cursor, arg.value);
|
||||
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
|
||||
}
|
||||
}
|
||||
|
||||
return new WindowsAArch64VaList(segment);
|
||||
}
|
||||
}
|
||||
}
|
@ -32,9 +32,13 @@
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.PaddingLayout;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
@ -46,6 +50,7 @@ import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static java.lang.foreign.MemoryLayout.PathElement.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestVarArgs extends CallGeneratorHelper {
|
||||
|
||||
@ -65,7 +70,7 @@ public class TestVarArgs extends CallGeneratorHelper {
|
||||
|
||||
static final MemorySegment VARARGS_ADDR = findNativeOrThrow("varargs");
|
||||
|
||||
@Test(dataProvider = "functions")
|
||||
@Test(dataProvider = "variadicFunctions")
|
||||
public void testVarArgs(int count, String fName, Ret ret, // ignore this stuff
|
||||
List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
|
||||
List<Arg> args = makeArgs(paramTypes, fields);
|
||||
@ -106,6 +111,58 @@ public class TestVarArgs extends CallGeneratorHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private static List<ParamType> createParameterTypesForStruct(int extraIntArgs) {
|
||||
List<ParamType> paramTypes = new ArrayList<ParamType>();
|
||||
for (int i = 0; i < extraIntArgs; i++) {
|
||||
paramTypes.add(ParamType.INT);
|
||||
}
|
||||
paramTypes.add(ParamType.STRUCT);
|
||||
return paramTypes;
|
||||
}
|
||||
|
||||
private static List<StructFieldType> createFieldsForStruct(int fieldCount, StructFieldType fieldType) {
|
||||
List<StructFieldType> fields = new ArrayList<StructFieldType>();
|
||||
for (int i = 0; i < fieldCount; i++) {
|
||||
fields.add(fieldType);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
@DataProvider(name = "variadicFunctions")
|
||||
public static Object[][] variadicFunctions() {
|
||||
List<Object[]> downcalls = new ArrayList<>();
|
||||
|
||||
var functionsDowncalls = functions();
|
||||
for (var array : functionsDowncalls) {
|
||||
downcalls.add(array);
|
||||
}
|
||||
|
||||
// Test struct with 4 floats
|
||||
int extraIntArgs = 0;
|
||||
List<StructFieldType> fields = createFieldsForStruct(4, StructFieldType.FLOAT);
|
||||
List<ParamType> paramTypes = createParameterTypesForStruct(extraIntArgs);
|
||||
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
|
||||
|
||||
// Test struct with 4 floats without enough registers for all fields
|
||||
extraIntArgs = 6;
|
||||
fields = createFieldsForStruct(4, StructFieldType.FLOAT);
|
||||
paramTypes = createParameterTypesForStruct(extraIntArgs);
|
||||
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
|
||||
|
||||
// Test struct with 2 doubles without enough registers for all fields
|
||||
extraIntArgs = 7;
|
||||
fields = createFieldsForStruct(2, StructFieldType.DOUBLE);
|
||||
paramTypes = createParameterTypesForStruct(extraIntArgs);
|
||||
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
|
||||
|
||||
// Test struct with 2 ints without enough registers for all fields
|
||||
fields = createFieldsForStruct(2, StructFieldType.INT);
|
||||
paramTypes = createParameterTypesForStruct(extraIntArgs);
|
||||
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
|
||||
|
||||
return downcalls.toArray(new Object[0][]);
|
||||
}
|
||||
|
||||
private static List<Arg> makeArgs(List<ParamType> paramTypes, List<StructFieldType> fields) throws ReflectiveOperationException {
|
||||
List<Arg> args = new ArrayList<>();
|
||||
for (ParamType pType : paramTypes) {
|
||||
@ -262,6 +319,7 @@ public class TestVarArgs extends CallGeneratorHelper {
|
||||
S_PPF,
|
||||
S_PPD,
|
||||
S_PPP,
|
||||
S_FFFF,
|
||||
;
|
||||
|
||||
public static NativeType of(String type) {
|
||||
|
@ -30,7 +30,7 @@
|
||||
* java.base/jdk.internal.foreign.abi
|
||||
* java.base/jdk.internal.foreign.abi.aarch64
|
||||
* @build CallArrangerTestBase
|
||||
* @run testng TestAarch64CallArranger
|
||||
* @run testng TestLinuxAArch64CallArranger
|
||||
*/
|
||||
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
@ -59,7 +59,7 @@ import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class TestAarch64CallArranger extends CallArrangerTestBase {
|
||||
public class TestLinuxAArch64CallArranger extends CallArrangerTestBase {
|
||||
|
||||
private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
|
||||
private static final VMStorage RETURN_BUFFER_STORAGE = StubLocations.RETURN_BUFFER.storage(StorageType.PLACEHOLDER);
|
||||
@ -426,224 +426,4 @@ public class TestAarch64CallArranger extends CallArrangerTestBase {
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVarArgsOnStack() {
|
||||
MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT);
|
||||
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT);
|
||||
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fdExpected);
|
||||
|
||||
// The two variadic arguments should be allocated on the stack
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, int.class) },
|
||||
{ vmStore(stackStorage((short) 4, 0), int.class) },
|
||||
{ vmStore(stackStorage((short) 4, 8), float.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacArgsOnStack() {
|
||||
MethodType mt = MethodType.methodType(void.class,
|
||||
int.class, int.class, int.class, int.class,
|
||||
int.class, int.class, int.class, int.class,
|
||||
int.class, int.class, short.class, byte.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
|
||||
C_INT, C_INT, C_INT, C_INT,
|
||||
C_INT, C_INT, C_INT, C_INT,
|
||||
C_INT, C_INT, C_SHORT, C_CHAR);
|
||||
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, int.class) },
|
||||
{ vmStore(r1, int.class) },
|
||||
{ vmStore(r2, int.class) },
|
||||
{ vmStore(r3, int.class) },
|
||||
{ vmStore(r4, int.class) },
|
||||
{ vmStore(r5, int.class) },
|
||||
{ vmStore(r6, int.class) },
|
||||
{ vmStore(r7, int.class) },
|
||||
{ vmStore(stackStorage((short) 4, 0), int.class) },
|
||||
{ vmStore(stackStorage((short) 4, 4), int.class) },
|
||||
{ cast(short.class, int.class), vmStore(stackStorage((short) 2, 8), int.class) },
|
||||
{ cast(byte.class, int.class), vmStore(stackStorage((short) 1, 10), int.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacArgsOnStack2() {
|
||||
StructLayout struct = MemoryLayout.structLayout(
|
||||
C_FLOAT,
|
||||
C_FLOAT
|
||||
);
|
||||
MethodType mt = MethodType.methodType(void.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
int.class, MemorySegment.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
C_INT, struct);
|
||||
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, long.class) },
|
||||
{ vmStore(r1, long.class) },
|
||||
{ vmStore(r2, long.class) },
|
||||
{ vmStore(r3, long.class) },
|
||||
{ vmStore(r4, long.class) },
|
||||
{ vmStore(r5, long.class) },
|
||||
{ vmStore(r6, long.class) },
|
||||
{ vmStore(r7, long.class) },
|
||||
{ vmStore(v0, double.class) },
|
||||
{ vmStore(v1, double.class) },
|
||||
{ vmStore(v2, double.class) },
|
||||
{ vmStore(v3, double.class) },
|
||||
{ vmStore(v4, double.class) },
|
||||
{ vmStore(v5, double.class) },
|
||||
{ vmStore(v6, double.class) },
|
||||
{ vmStore(v7, double.class) },
|
||||
{ vmStore(stackStorage((short) 4, 0), int.class) },
|
||||
{
|
||||
dup(),
|
||||
bufferLoad(0, int.class),
|
||||
vmStore(stackStorage((short) 4, 4), int.class),
|
||||
bufferLoad(4, int.class),
|
||||
vmStore(stackStorage((short) 4, 8), int.class),
|
||||
}
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacArgsOnStack3() {
|
||||
StructLayout struct = MemoryLayout.structLayout(
|
||||
C_POINTER,
|
||||
C_POINTER
|
||||
);
|
||||
MethodType mt = MethodType.methodType(void.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
MemorySegment.class, float.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
struct, C_FLOAT);
|
||||
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, long.class) },
|
||||
{ vmStore(r1, long.class) },
|
||||
{ vmStore(r2, long.class) },
|
||||
{ vmStore(r3, long.class) },
|
||||
{ vmStore(r4, long.class) },
|
||||
{ vmStore(r5, long.class) },
|
||||
{ vmStore(r6, long.class) },
|
||||
{ vmStore(r7, long.class) },
|
||||
{ vmStore(v0, double.class) },
|
||||
{ vmStore(v1, double.class) },
|
||||
{ vmStore(v2, double.class) },
|
||||
{ vmStore(v3, double.class) },
|
||||
{ vmStore(v4, double.class) },
|
||||
{ vmStore(v5, double.class) },
|
||||
{ vmStore(v6, double.class) },
|
||||
{ vmStore(v7, double.class) },
|
||||
{ dup(),
|
||||
bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 0), long.class),
|
||||
bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 8), long.class) },
|
||||
{ vmStore(stackStorage((short) 4, 16), float.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacArgsOnStack4() {
|
||||
StructLayout struct = MemoryLayout.structLayout(
|
||||
C_INT,
|
||||
C_INT,
|
||||
C_POINTER
|
||||
);
|
||||
MethodType mt = MethodType.methodType(void.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
float.class, MemorySegment.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
C_FLOAT, struct);
|
||||
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, long.class) },
|
||||
{ vmStore(r1, long.class) },
|
||||
{ vmStore(r2, long.class) },
|
||||
{ vmStore(r3, long.class) },
|
||||
{ vmStore(r4, long.class) },
|
||||
{ vmStore(r5, long.class) },
|
||||
{ vmStore(r6, long.class) },
|
||||
{ vmStore(r7, long.class) },
|
||||
{ vmStore(v0, double.class) },
|
||||
{ vmStore(v1, double.class) },
|
||||
{ vmStore(v2, double.class) },
|
||||
{ vmStore(v3, double.class) },
|
||||
{ vmStore(v4, double.class) },
|
||||
{ vmStore(v5, double.class) },
|
||||
{ vmStore(v6, double.class) },
|
||||
{ vmStore(v7, double.class) },
|
||||
{ vmStore(stackStorage((short) 4, 0), float.class) },
|
||||
{ dup(),
|
||||
bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 8), long.class),
|
||||
bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 16), long.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
}
|
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @enablePreview
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* java.base/jdk.internal.foreign.abi
|
||||
* java.base/jdk.internal.foreign.abi.aarch64
|
||||
* @build CallArrangerTestBase
|
||||
* @run testng TestMacOsAArch64CallArranger
|
||||
*/
|
||||
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.StructLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import jdk.internal.foreign.abi.Binding;
|
||||
import jdk.internal.foreign.abi.CallingSequence;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
import jdk.internal.foreign.abi.StubLocations;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import static java.lang.foreign.Linker.Option.firstVariadicArg;
|
||||
import static java.lang.foreign.ValueLayout.ADDRESS;
|
||||
import static jdk.internal.foreign.PlatformLayouts.AArch64.*;
|
||||
import static jdk.internal.foreign.abi.Binding.*;
|
||||
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
|
||||
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class TestMacOsAArch64CallArranger extends CallArrangerTestBase {
|
||||
|
||||
private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
|
||||
|
||||
@Test
|
||||
public void testVarArgsOnStack() {
|
||||
MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT);
|
||||
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT);
|
||||
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fdExpected);
|
||||
|
||||
// The two variadic arguments should be allocated on the stack
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, int.class) },
|
||||
{ vmStore(stackStorage((short) 4, 0), int.class) },
|
||||
{ vmStore(stackStorage((short) 4, 8), float.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacArgsOnStack() {
|
||||
MethodType mt = MethodType.methodType(void.class,
|
||||
int.class, int.class, int.class, int.class,
|
||||
int.class, int.class, int.class, int.class,
|
||||
int.class, int.class, short.class, byte.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
|
||||
C_INT, C_INT, C_INT, C_INT,
|
||||
C_INT, C_INT, C_INT, C_INT,
|
||||
C_INT, C_INT, C_SHORT, C_CHAR);
|
||||
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, int.class) },
|
||||
{ vmStore(r1, int.class) },
|
||||
{ vmStore(r2, int.class) },
|
||||
{ vmStore(r3, int.class) },
|
||||
{ vmStore(r4, int.class) },
|
||||
{ vmStore(r5, int.class) },
|
||||
{ vmStore(r6, int.class) },
|
||||
{ vmStore(r7, int.class) },
|
||||
{ vmStore(stackStorage((short) 4, 0), int.class) },
|
||||
{ vmStore(stackStorage((short) 4, 4), int.class) },
|
||||
{ cast(short.class, int.class), vmStore(stackStorage((short) 2, 8), int.class) },
|
||||
{ cast(byte.class, int.class), vmStore(stackStorage((short) 1, 10), int.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacArgsOnStack2() {
|
||||
StructLayout struct = MemoryLayout.structLayout(
|
||||
C_FLOAT,
|
||||
C_FLOAT
|
||||
);
|
||||
MethodType mt = MethodType.methodType(void.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
int.class, MemorySegment.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
C_INT, struct);
|
||||
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, long.class) },
|
||||
{ vmStore(r1, long.class) },
|
||||
{ vmStore(r2, long.class) },
|
||||
{ vmStore(r3, long.class) },
|
||||
{ vmStore(r4, long.class) },
|
||||
{ vmStore(r5, long.class) },
|
||||
{ vmStore(r6, long.class) },
|
||||
{ vmStore(r7, long.class) },
|
||||
{ vmStore(v0, double.class) },
|
||||
{ vmStore(v1, double.class) },
|
||||
{ vmStore(v2, double.class) },
|
||||
{ vmStore(v3, double.class) },
|
||||
{ vmStore(v4, double.class) },
|
||||
{ vmStore(v5, double.class) },
|
||||
{ vmStore(v6, double.class) },
|
||||
{ vmStore(v7, double.class) },
|
||||
{ vmStore(stackStorage((short) 4, 0), int.class) },
|
||||
{
|
||||
dup(),
|
||||
bufferLoad(0, int.class),
|
||||
vmStore(stackStorage((short) 4, 4), int.class),
|
||||
bufferLoad(4, int.class),
|
||||
vmStore(stackStorage((short) 4, 8), int.class),
|
||||
}
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacArgsOnStack3() {
|
||||
StructLayout struct = MemoryLayout.structLayout(
|
||||
C_POINTER,
|
||||
C_POINTER
|
||||
);
|
||||
MethodType mt = MethodType.methodType(void.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
MemorySegment.class, float.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
struct, C_FLOAT);
|
||||
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, long.class) },
|
||||
{ vmStore(r1, long.class) },
|
||||
{ vmStore(r2, long.class) },
|
||||
{ vmStore(r3, long.class) },
|
||||
{ vmStore(r4, long.class) },
|
||||
{ vmStore(r5, long.class) },
|
||||
{ vmStore(r6, long.class) },
|
||||
{ vmStore(r7, long.class) },
|
||||
{ vmStore(v0, double.class) },
|
||||
{ vmStore(v1, double.class) },
|
||||
{ vmStore(v2, double.class) },
|
||||
{ vmStore(v3, double.class) },
|
||||
{ vmStore(v4, double.class) },
|
||||
{ vmStore(v5, double.class) },
|
||||
{ vmStore(v6, double.class) },
|
||||
{ vmStore(v7, double.class) },
|
||||
{ dup(),
|
||||
bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 0), long.class),
|
||||
bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 8), long.class) },
|
||||
{ vmStore(stackStorage((short) 4, 16), float.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacArgsOnStack4() {
|
||||
StructLayout struct = MemoryLayout.structLayout(
|
||||
C_INT,
|
||||
C_INT,
|
||||
C_POINTER
|
||||
);
|
||||
MethodType mt = MethodType.methodType(void.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
long.class, long.class, long.class, long.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
double.class, double.class, double.class, double.class,
|
||||
float.class, MemorySegment.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
|
||||
C_FLOAT, struct);
|
||||
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, long.class) },
|
||||
{ vmStore(r1, long.class) },
|
||||
{ vmStore(r2, long.class) },
|
||||
{ vmStore(r3, long.class) },
|
||||
{ vmStore(r4, long.class) },
|
||||
{ vmStore(r5, long.class) },
|
||||
{ vmStore(r6, long.class) },
|
||||
{ vmStore(r7, long.class) },
|
||||
{ vmStore(v0, double.class) },
|
||||
{ vmStore(v1, double.class) },
|
||||
{ vmStore(v2, double.class) },
|
||||
{ vmStore(v3, double.class) },
|
||||
{ vmStore(v4, double.class) },
|
||||
{ vmStore(v5, double.class) },
|
||||
{ vmStore(v6, double.class) },
|
||||
{ vmStore(v7, double.class) },
|
||||
{ vmStore(stackStorage((short) 4, 0), float.class) },
|
||||
{ dup(),
|
||||
bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 8), long.class),
|
||||
bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 16), long.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
}
|
@ -0,0 +1,350 @@
|
||||
/*
|
||||
* 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @enablePreview
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* java.base/jdk.internal.foreign.abi
|
||||
* java.base/jdk.internal.foreign.abi.aarch64
|
||||
* @build CallArrangerTestBase
|
||||
* @run testng TestWindowsAArch64CallArranger
|
||||
*/
|
||||
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.StructLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import jdk.internal.foreign.abi.Binding;
|
||||
import jdk.internal.foreign.abi.CallingSequence;
|
||||
import jdk.internal.foreign.abi.LinkerOptions;
|
||||
import jdk.internal.foreign.abi.StubLocations;
|
||||
import jdk.internal.foreign.abi.VMStorage;
|
||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.invoke.MethodType;
|
||||
|
||||
import static java.lang.foreign.Linker.Option.firstVariadicArg;
|
||||
import static java.lang.foreign.ValueLayout.ADDRESS;
|
||||
import static jdk.internal.foreign.PlatformLayouts.AArch64.*;
|
||||
import static jdk.internal.foreign.abi.Binding.*;
|
||||
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
|
||||
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
public class TestWindowsAArch64CallArranger extends CallArrangerTestBase {
|
||||
|
||||
private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
|
||||
private static final VMStorage RETURN_BUFFER_STORAGE = StubLocations.RETURN_BUFFER.storage(StorageType.PLACEHOLDER);
|
||||
|
||||
@Test
|
||||
public void testWindowsArgsInRegs() {
|
||||
MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class, double.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT, C_DOUBLE);
|
||||
CallArranger.Bindings bindings = CallArranger.WINDOWS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, int.class) },
|
||||
{ vmStore(r1, int.class) },
|
||||
{ vmStore(v0, float.class) },
|
||||
{ vmStore(v1, double.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsVarArgsInRegs() {
|
||||
MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class, double.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT, C_DOUBLE);
|
||||
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT, C_DOUBLE);
|
||||
CallArranger.Bindings bindings = CallArranger.WINDOWS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fdExpected);
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, int.class) },
|
||||
{ vmStore(r1, int.class) },
|
||||
{ vmStore(r2, float.class) },
|
||||
{ vmStore(r3, double.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsArgsInRegsAndOnStack() {
|
||||
MethodType mt = MethodType.methodType(void.class, double.class, int.class, float.class,
|
||||
double.class, float.class, float.class, double.class,
|
||||
float.class, float.class, float.class, int.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_DOUBLE, C_INT, C_FLOAT,
|
||||
C_DOUBLE, C_FLOAT, C_FLOAT, C_DOUBLE,
|
||||
C_FLOAT, C_FLOAT, C_FLOAT, C_INT);
|
||||
CallArranger.Bindings bindings = CallArranger.WINDOWS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(v0, double.class) },
|
||||
{ vmStore(r0, int.class) },
|
||||
{ vmStore(v1, float.class) },
|
||||
{ vmStore(v2, double.class) },
|
||||
{ vmStore(v3, float.class) },
|
||||
{ vmStore(v4, float.class) },
|
||||
{ vmStore(v5, double.class) },
|
||||
{ vmStore(v6, float.class) },
|
||||
{ vmStore(v7, float.class) },
|
||||
{ vmStore(stackStorage((short) 4, 0), float.class) },
|
||||
{ vmStore(r1, int.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsVarArgsInRegsAndOnStack() {
|
||||
MethodType mt = MethodType.methodType(void.class, double.class, int.class, float.class,
|
||||
double.class, float.class, float.class, double.class,
|
||||
float.class, float.class, float.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_DOUBLE, C_INT, C_FLOAT,
|
||||
C_DOUBLE, C_FLOAT, C_FLOAT, C_DOUBLE,
|
||||
C_FLOAT, C_FLOAT, C_FLOAT);
|
||||
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_DOUBLE, C_INT, C_FLOAT, C_DOUBLE, C_FLOAT, C_FLOAT, C_DOUBLE, C_FLOAT, C_FLOAT, C_FLOAT);
|
||||
CallArranger.Bindings bindings = CallArranger.WINDOWS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fdExpected);
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ vmStore(r0, double.class) },
|
||||
{ vmStore(r1, int.class) },
|
||||
{ vmStore(r2, float.class) },
|
||||
{ vmStore(r3, double.class) },
|
||||
{ vmStore(r4, float.class) },
|
||||
{ vmStore(r5, float.class) },
|
||||
{ vmStore(r6, double.class) },
|
||||
{ vmStore(r7, float.class) },
|
||||
{ vmStore(stackStorage((short) 4, 0), float.class) },
|
||||
{ vmStore(stackStorage((short) 4, 8), float.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsHfa4FloatsInFloatRegs() {
|
||||
MemoryLayout struct = MemoryLayout.structLayout(C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT);
|
||||
|
||||
MethodType mt = MethodType.methodType(void.class, MemorySegment.class, int.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, C_INT);
|
||||
CallArranger.Bindings bindings = CallArranger.WINDOWS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{
|
||||
dup(),
|
||||
bufferLoad(0, float.class),
|
||||
vmStore(v0, float.class),
|
||||
dup(),
|
||||
bufferLoad(4, float.class),
|
||||
vmStore(v1, float.class),
|
||||
dup(),
|
||||
bufferLoad(8, float.class),
|
||||
vmStore(v2, float.class),
|
||||
bufferLoad(12, float.class),
|
||||
vmStore(v3, float.class),
|
||||
},
|
||||
{ vmStore(r0, int.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsVariadicHfa4FloatsInIntRegs() {
|
||||
MemoryLayout struct = MemoryLayout.structLayout(C_FLOAT, C_FLOAT, C_FLOAT, C_FLOAT);
|
||||
|
||||
MethodType mt = MethodType.methodType(void.class, MemorySegment.class, int.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, C_INT);
|
||||
CallArranger.Bindings bindings = CallArranger.WINDOWS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(0)));
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{
|
||||
dup(),
|
||||
bufferLoad(0, long.class),
|
||||
vmStore(r0, long.class),
|
||||
bufferLoad(8, long.class),
|
||||
vmStore(r1, long.class),
|
||||
},
|
||||
{ vmStore(r2, int.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsHfa2DoublesInFloatRegs() {
|
||||
MemoryLayout struct = MemoryLayout.structLayout(C_DOUBLE, C_DOUBLE);
|
||||
|
||||
MethodType mt = MethodType.methodType(
|
||||
void.class, MemorySegment.class, int.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, C_INT);
|
||||
CallArranger.Bindings bindings = CallArranger.WINDOWS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{
|
||||
dup(),
|
||||
bufferLoad(0, double.class),
|
||||
vmStore(v0, double.class),
|
||||
bufferLoad(8, double.class),
|
||||
vmStore(v1, double.class),
|
||||
},
|
||||
{ vmStore(r0, int.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsVariadicHfa2DoublesInIntRegs() {
|
||||
MemoryLayout struct = MemoryLayout.structLayout(C_DOUBLE, C_DOUBLE);
|
||||
|
||||
MethodType mt = MethodType.methodType(
|
||||
void.class, MemorySegment.class, int.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, C_INT);
|
||||
CallArranger.Bindings bindings = CallArranger.WINDOWS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(0)));
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{
|
||||
dup(),
|
||||
bufferLoad(0, long.class),
|
||||
vmStore(r0, long.class),
|
||||
bufferLoad(8, long.class),
|
||||
vmStore(r1, long.class),
|
||||
},
|
||||
{ vmStore(r2, int.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsHfa3DoublesInFloatRegs() {
|
||||
MemoryLayout struct = MemoryLayout.structLayout(C_DOUBLE, C_DOUBLE, C_DOUBLE);
|
||||
|
||||
MethodType mt = MethodType.methodType(
|
||||
void.class, MemorySegment.class, int.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, C_INT);
|
||||
CallArranger.Bindings bindings = CallArranger.WINDOWS.getBindings(mt, fd, false);
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{
|
||||
dup(),
|
||||
bufferLoad(0, double.class),
|
||||
vmStore(v0, double.class),
|
||||
dup(),
|
||||
bufferLoad(8, double.class),
|
||||
vmStore(v1, double.class),
|
||||
bufferLoad(16, double.class),
|
||||
vmStore(v2, double.class),
|
||||
},
|
||||
{ vmStore(r0, int.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWindowsVariadicHfa3DoublesAsReferenceStruct() {
|
||||
MemoryLayout struct = MemoryLayout.structLayout(C_DOUBLE, C_DOUBLE, C_DOUBLE);
|
||||
|
||||
MethodType mt = MethodType.methodType(
|
||||
void.class, MemorySegment.class, int.class);
|
||||
FunctionDescriptor fd = FunctionDescriptor.ofVoid(struct, C_INT);
|
||||
CallArranger.Bindings bindings = CallArranger.WINDOWS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(0)));
|
||||
|
||||
assertFalse(bindings.isInMemoryReturn());
|
||||
CallingSequence callingSequence = bindings.callingSequence();
|
||||
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
|
||||
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
|
||||
|
||||
checkArgumentBindings(callingSequence, new Binding[][]{
|
||||
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
|
||||
{ copy(struct), unboxAddress(), vmStore(r0, long.class) },
|
||||
{ vmStore(r1, int.class) },
|
||||
});
|
||||
|
||||
checkReturnBindings(callingSequence, new Binding[]{});
|
||||
}
|
||||
}
|
@ -128,6 +128,7 @@ enum NativeType {
|
||||
T_S_PPF,
|
||||
T_S_PPD,
|
||||
T_S_PPP,
|
||||
T_S_FFFF,
|
||||
};
|
||||
|
||||
// need to pass `num` separately as last argument preceding varargs according to spec (and for MSVC)
|
||||
@ -227,6 +228,7 @@ EXPORT void varargs(call_info* info, int num, ...) {
|
||||
CASE(T_S_PPF, struct S_PPF)
|
||||
CASE(T_S_PPD, struct S_PPD)
|
||||
CASE(T_S_PPP, struct S_PPP)
|
||||
CASE(T_S_FFFF, struct S_FFFF)
|
||||
default: exit(-1); // invalid id
|
||||
}
|
||||
}
|
||||
|
@ -119,3 +119,4 @@ struct S_PPI { void* p0; void* p1; int p2; };
|
||||
struct S_PPF { void* p0; void* p1; float p2; };
|
||||
struct S_PPD { void* p0; void* p1; double p2; };
|
||||
struct S_PPP { void* p0; void* p1; void* p2; };
|
||||
struct S_FFFF { float p0; float p1; float p2; float p3; };
|
||||
|
Loading…
x
Reference in New Issue
Block a user