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,
|
static void move_v128(MacroAssembler* masm, int out_stk_bias,
|
||||||
FloatRegister from_reg, VMStorage to_reg) {
|
FloatRegister from_reg, VMStorage to_reg) {
|
||||||
switch (to_reg.type()) {
|
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:
|
case StorageType::VECTOR:
|
||||||
assert(to_reg.segment_mask() == V128_MASK, "only moves to v128 registers supported");
|
assert(to_reg.segment_mask() == V128_MASK, "only moves to v128 registers supported");
|
||||||
masm->fmovd(as_FloatRegister(to_reg), from_reg);
|
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.SharedUtils;
|
||||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList;
|
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList;
|
||||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList;
|
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.riscv64.linux.LinuxRISCV64VaList;
|
||||||
import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
|
import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
|
||||||
import jdk.internal.foreign.abi.x64.windows.WinVaList;
|
import jdk.internal.foreign.abi.x64.windows.WinVaList;
|
||||||
@ -105,7 +106,7 @@ import jdk.internal.reflect.Reflection;
|
|||||||
* @since 19
|
* @since 19
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
@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
|
* 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
|
* @since 19
|
||||||
*/
|
*/
|
||||||
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
|
@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.
|
* Writes an {@code int} value to the variable argument list being constructed.
|
||||||
|
@ -33,6 +33,7 @@ public enum CABI {
|
|||||||
WIN_64,
|
WIN_64,
|
||||||
LINUX_AARCH_64,
|
LINUX_AARCH_64,
|
||||||
MAC_OS_AARCH_64,
|
MAC_OS_AARCH_64,
|
||||||
|
WIN_AARCH_64,
|
||||||
LINUX_RISCV_64;
|
LINUX_RISCV_64;
|
||||||
|
|
||||||
private static final CABI ABI;
|
private static final CABI ABI;
|
||||||
@ -55,6 +56,8 @@ public enum CABI {
|
|||||||
} else if (ARCH.equals("aarch64")) {
|
} else if (ARCH.equals("aarch64")) {
|
||||||
if (OS.startsWith("Mac")) {
|
if (OS.startsWith("Mac")) {
|
||||||
ABI = MAC_OS_AARCH_64;
|
ABI = MAC_OS_AARCH_64;
|
||||||
|
} else if (OS.startsWith("Windows")) {
|
||||||
|
ABI = WIN_AARCH_64;
|
||||||
} else {
|
} else {
|
||||||
// The Linux ABI follows the standard AAPCS ABI
|
// The Linux ABI follows the standard AAPCS ABI
|
||||||
ABI = LINUX_AARCH_64;
|
ABI = LINUX_AARCH_64;
|
||||||
|
@ -59,7 +59,7 @@ public final class SystemLookup implements SymbolLookup {
|
|||||||
try {
|
try {
|
||||||
return switch (CABI.current()) {
|
return switch (CABI.current()) {
|
||||||
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> libLookup(libs -> libs.load(jdkLibraryPath("syslookup")));
|
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) {
|
} catch (Throwable ex) {
|
||||||
// This can happen in the event of a library loading failure - e.g. if one of the libraries the
|
// 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"));
|
Path javahome = Path.of(GetPropertyAction.privilegedGetProperty("java.home"));
|
||||||
String lib = switch (CABI.current()) {
|
String lib = switch (CABI.current()) {
|
||||||
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> "lib";
|
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);
|
String libname = System.mapLibraryName(name);
|
||||||
return javahome.resolve(lib).resolve(libname);
|
return javahome.resolve(lib).resolve(libname);
|
||||||
|
@ -27,6 +27,7 @@ package jdk.internal.foreign.abi;
|
|||||||
import jdk.internal.foreign.SystemLookup;
|
import jdk.internal.foreign.SystemLookup;
|
||||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
|
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
|
||||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
|
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.riscv64.linux.LinuxRISCV64Linker;
|
||||||
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
||||||
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
||||||
@ -44,7 +45,7 @@ import java.lang.invoke.MethodType;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker,
|
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 record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {}
|
||||||
private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();
|
private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();
|
||||||
|
@ -81,6 +81,11 @@ public class LinkerOptions {
|
|||||||
return stl == null ? Stream.empty() : stl.saved().stream();
|
return stl == null ? Stream.empty() : stl.saved().stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isVariadicFunction() {
|
||||||
|
FirstVariadicArg fva = getOption(FirstVariadicArg.class);
|
||||||
|
return fva != null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -30,6 +30,7 @@ import jdk.internal.access.SharedSecrets;
|
|||||||
import jdk.internal.foreign.CABI;
|
import jdk.internal.foreign.CABI;
|
||||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
|
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
|
||||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
|
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.riscv64.linux.LinuxRISCV64Linker;
|
||||||
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
||||||
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
||||||
@ -184,6 +185,7 @@ public final class SharedUtils {
|
|||||||
case SYS_V -> SysVx64Linker.getInstance();
|
case SYS_V -> SysVx64Linker.getInstance();
|
||||||
case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance();
|
case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance();
|
||||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance();
|
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance();
|
||||||
|
case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance();
|
||||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance();
|
case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -297,6 +299,7 @@ public final class SharedUtils {
|
|||||||
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaList(actions, scope);
|
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaList(actions, scope);
|
||||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaList(actions, scope);
|
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaList(actions, scope);
|
||||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.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 LINUX_AARCH_64 -> LinuxAArch64Linker.newVaListOfAddress(address, scope);
|
||||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaListOfAddress(address, scope);
|
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaListOfAddress(address, scope);
|
||||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.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 LINUX_AARCH_64 -> LinuxAArch64Linker.emptyVaList();
|
||||||
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.emptyVaList();
|
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.emptyVaList();
|
||||||
case LINUX_RISCV_64 -> LinuxRISCV64Linker.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.VMStorage;
|
||||||
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64CallArranger;
|
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64CallArranger;
|
||||||
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger;
|
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger;
|
||||||
|
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64CallArranger;
|
||||||
import jdk.internal.foreign.Utils;
|
import jdk.internal.foreign.Utils;
|
||||||
|
|
||||||
import java.lang.foreign.SegmentScope;
|
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
|
* 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
|
* 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 {
|
public abstract class CallArranger {
|
||||||
private static final int STACK_SLOT_SIZE = 8;
|
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
|
// 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
|
// registers, it's not possible to generate a C function that uses
|
||||||
// r2-7 and v4-7 so they are omitted here.
|
// 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[] { r0, r1, r2, r3, r4, r5, r6, r7, INDIRECT_RESULT},
|
||||||
new VMStorage[] { v0, v1, v2, v3, v4, v5, v6, v7 },
|
new VMStorage[] { v0, v1, v2, v3, v4, v5, v6, v7 },
|
||||||
new VMStorage[] { r0, r1 },
|
new VMStorage[] { r0, r1 },
|
||||||
@ -98,6 +99,7 @@ public abstract class CallArranger {
|
|||||||
|
|
||||||
public static final CallArranger LINUX = new LinuxAArch64CallArranger();
|
public static final CallArranger LINUX = new LinuxAArch64CallArranger();
|
||||||
public static final CallArranger MACOS = new MacOsAArch64CallArranger();
|
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
|
* Are variadic arguments assigned to registers as in the standard calling
|
||||||
@ -112,6 +114,31 @@ public abstract class CallArranger {
|
|||||||
*/
|
*/
|
||||||
protected abstract boolean requiresSubSlotStackPacking();
|
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() {}
|
protected CallArranger() {}
|
||||||
|
|
||||||
public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
|
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) {
|
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);
|
boolean forVariadicFunction = options.isVariadicFunction();
|
||||||
BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false);
|
|
||||||
|
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());
|
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
|
||||||
if (returnInMemory) {
|
if (returnInMemory) {
|
||||||
@ -149,7 +178,7 @@ public abstract class CallArranger {
|
|||||||
public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
|
public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
|
||||||
Bindings bindings = getBindings(mt, cDesc, false, 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) {
|
if (bindings.isInMemoryReturn) {
|
||||||
handle = SharedUtils.adaptDowncallForIMR(handle, cDesc, bindings.callingSequence);
|
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 */);
|
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) {
|
private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) {
|
||||||
@ -177,13 +206,15 @@ public abstract class CallArranger {
|
|||||||
|
|
||||||
class StorageCalculator {
|
class StorageCalculator {
|
||||||
private final boolean forArguments;
|
private final boolean forArguments;
|
||||||
|
private final boolean forVariadicFunction;
|
||||||
private boolean forVarArgs = false;
|
private boolean forVarArgs = false;
|
||||||
|
|
||||||
private final int[] nRegs = new int[] { 0, 0 };
|
private final int[] nRegs = new int[] { 0, 0 };
|
||||||
private long stackOffset = 0;
|
private long stackOffset = 0;
|
||||||
|
|
||||||
public StorageCalculator(boolean forArguments) {
|
public StorageCalculator(boolean forArguments, boolean forVariadicFunction) {
|
||||||
this.forArguments = forArguments;
|
this.forArguments = forArguments;
|
||||||
|
this.forVariadicFunction = forVariadicFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
void alignStack(long alignment) {
|
void alignStack(long alignment) {
|
||||||
@ -212,8 +243,9 @@ public abstract class CallArranger {
|
|||||||
|
|
||||||
VMStorage[] regAlloc(int type, int count) {
|
VMStorage[] regAlloc(int type, int count) {
|
||||||
if (nRegs[type] + count <= MAX_REGISTER_ARGUMENTS) {
|
if (nRegs[type] + count <= MAX_REGISTER_ARGUMENTS) {
|
||||||
|
ABIDescriptor abiDescriptor = abiDescriptor();
|
||||||
VMStorage[] source =
|
VMStorage[] source =
|
||||||
(forArguments ? C.inputStorage : C.outputStorage)[type];
|
(forArguments ? abiDescriptor.inputStorage : abiDescriptor.outputStorage)[type];
|
||||||
VMStorage[] result = new VMStorage[count];
|
VMStorage[] result = new VMStorage[count];
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
result[i] = source[nRegs[type]++];
|
result[i] = source[nRegs[type]++];
|
||||||
@ -228,10 +260,37 @@ public abstract class CallArranger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
VMStorage[] regAlloc(int type, MemoryLayout layout) {
|
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) {
|
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);
|
VMStorage[] storage = regAlloc(type, 1);
|
||||||
if (storage == null) {
|
if (storage == null) {
|
||||||
return stackAlloc(layout);
|
return stackAlloc(layout);
|
||||||
@ -272,8 +331,8 @@ public abstract class CallArranger {
|
|||||||
abstract class BindingCalculator {
|
abstract class BindingCalculator {
|
||||||
protected final StorageCalculator storageCalculator;
|
protected final StorageCalculator storageCalculator;
|
||||||
|
|
||||||
protected BindingCalculator(boolean forArguments) {
|
protected BindingCalculator(boolean forArguments, boolean forVariadicFunction) {
|
||||||
this.storageCalculator = new StorageCalculator(forArguments);
|
this.storageCalculator = new StorageCalculator(forArguments, forVariadicFunction);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void spillStructUnbox(Binding.Builder bindings, MemoryLayout layout) {
|
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
|
// struct, it must be passed on the stack. I.e. not split
|
||||||
// between registers and stack.
|
// 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()) {
|
while (offset < layout.byteSize()) {
|
||||||
long copy = Math.min(layout.byteSize() - offset, STACK_SLOT_SIZE);
|
long copy = Math.min(layout.byteSize() - offset, STACK_SLOT_SIZE);
|
||||||
VMStorage storage =
|
VMStorage storage =
|
||||||
@ -334,8 +396,13 @@ public abstract class CallArranger {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class UnboxBindingCalculator extends BindingCalculator {
|
class UnboxBindingCalculator extends BindingCalculator {
|
||||||
UnboxBindingCalculator(boolean forArguments) {
|
protected final boolean forArguments;
|
||||||
super(forArguments);
|
protected final boolean forVariadicFunction;
|
||||||
|
|
||||||
|
UnboxBindingCalculator(boolean forArguments, boolean forVariadicFunction) {
|
||||||
|
super(forArguments, forVariadicFunction);
|
||||||
|
this.forArguments = forArguments;
|
||||||
|
this.forVariadicFunction = forVariadicFunction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -348,21 +415,21 @@ public abstract class CallArranger {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
|
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
|
||||||
TypeClass argumentClass = TypeClass.classifyLayout(layout);
|
TypeClass argumentClass = getArgumentClassForBindings(layout, forVariadicFunction);
|
||||||
Binding.Builder bindings = Binding.builder();
|
Binding.Builder bindings = Binding.builder();
|
||||||
|
|
||||||
switch (argumentClass) {
|
switch (argumentClass) {
|
||||||
case STRUCT_REGISTER: {
|
case STRUCT_REGISTER: {
|
||||||
assert carrier == MemorySegment.class;
|
assert carrier == MemorySegment.class;
|
||||||
VMStorage[] regs = storageCalculator.regAlloc(
|
VMStorage[] regs = storageCalculator.regAlloc(StorageType.INTEGER, layout);
|
||||||
StorageType.INTEGER, layout);
|
|
||||||
if (regs != null) {
|
if (regs != null) {
|
||||||
int regIndex = 0;
|
int regIndex = 0;
|
||||||
long offset = 0;
|
long offset = 0;
|
||||||
while (offset < layout.byteSize()) {
|
while (offset < layout.byteSize() && regIndex < regs.length) {
|
||||||
final long copy = Math.min(layout.byteSize() - offset, 8);
|
final long copy = Math.min(layout.byteSize() - offset, 8);
|
||||||
VMStorage storage = regs[regIndex++];
|
VMStorage storage = regs[regIndex++];
|
||||||
boolean useFloat = storage.type() == StorageType.VECTOR;
|
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, false);
|
||||||
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, useFloat);
|
|
||||||
if (offset + copy < layout.byteSize()) {
|
if (offset + copy < layout.byteSize()) {
|
||||||
bindings.dup();
|
bindings.dup();
|
||||||
}
|
}
|
||||||
@ -370,6 +437,11 @@ public abstract class CallArranger {
|
|||||||
.vmStore(storage, type);
|
.vmStore(storage, type);
|
||||||
offset += copy;
|
offset += copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final long bytesLeft = Math.min(layout.byteSize() - offset, 8);
|
||||||
|
if (bytesLeft > 0) {
|
||||||
|
spillPartialStructUnbox(bindings, layout, offset);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
spillStructUnbox(bindings, layout);
|
spillStructUnbox(bindings, layout);
|
||||||
}
|
}
|
||||||
@ -435,7 +507,7 @@ public abstract class CallArranger {
|
|||||||
|
|
||||||
class BoxBindingCalculator extends BindingCalculator {
|
class BoxBindingCalculator extends BindingCalculator {
|
||||||
BoxBindingCalculator(boolean forArguments) {
|
BoxBindingCalculator(boolean forArguments) {
|
||||||
super(forArguments);
|
super(forArguments, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
package jdk.internal.foreign.abi.aarch64.linux;
|
package jdk.internal.foreign.abi.aarch64.linux;
|
||||||
|
|
||||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||||
|
import jdk.internal.foreign.abi.ABIDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AArch64 CallArranger specialized for Linux ABI.
|
* AArch64 CallArranger specialized for Linux ABI.
|
||||||
@ -43,4 +44,19 @@ public class LinuxAArch64CallArranger extends CallArranger {
|
|||||||
return false;
|
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;
|
package jdk.internal.foreign.abi.aarch64.macos;
|
||||||
|
|
||||||
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
import jdk.internal.foreign.abi.aarch64.CallArranger;
|
||||||
|
import jdk.internal.foreign.abi.ABIDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AArch64 CallArranger specialized for macOS ABI.
|
* AArch64 CallArranger specialized for macOS ABI.
|
||||||
@ -43,4 +44,19 @@ public class MacOsAArch64CallArranger extends CallArranger {
|
|||||||
return true;
|
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.Arena;
|
||||||
import java.lang.foreign.Linker;
|
import java.lang.foreign.Linker;
|
||||||
import java.lang.foreign.FunctionDescriptor;
|
import java.lang.foreign.FunctionDescriptor;
|
||||||
|
import java.lang.foreign.GroupLayout;
|
||||||
import java.lang.foreign.MemoryLayout;
|
import java.lang.foreign.MemoryLayout;
|
||||||
import java.lang.foreign.MemorySegment;
|
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 org.testng.annotations.Test;
|
||||||
|
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
@ -46,6 +50,7 @@ import java.util.List;
|
|||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import static java.lang.foreign.MemoryLayout.PathElement.*;
|
import static java.lang.foreign.MemoryLayout.PathElement.*;
|
||||||
|
import static org.testng.Assert.*;
|
||||||
|
|
||||||
public class TestVarArgs extends CallGeneratorHelper {
|
public class TestVarArgs extends CallGeneratorHelper {
|
||||||
|
|
||||||
@ -65,7 +70,7 @@ public class TestVarArgs extends CallGeneratorHelper {
|
|||||||
|
|
||||||
static final MemorySegment VARARGS_ADDR = findNativeOrThrow("varargs");
|
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
|
public void testVarArgs(int count, String fName, Ret ret, // ignore this stuff
|
||||||
List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
|
List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
|
||||||
List<Arg> args = makeArgs(paramTypes, fields);
|
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 {
|
private static List<Arg> makeArgs(List<ParamType> paramTypes, List<StructFieldType> fields) throws ReflectiveOperationException {
|
||||||
List<Arg> args = new ArrayList<>();
|
List<Arg> args = new ArrayList<>();
|
||||||
for (ParamType pType : paramTypes) {
|
for (ParamType pType : paramTypes) {
|
||||||
@ -262,6 +319,7 @@ public class TestVarArgs extends CallGeneratorHelper {
|
|||||||
S_PPF,
|
S_PPF,
|
||||||
S_PPD,
|
S_PPD,
|
||||||
S_PPP,
|
S_PPP,
|
||||||
|
S_FFFF,
|
||||||
;
|
;
|
||||||
|
|
||||||
public static NativeType of(String type) {
|
public static NativeType of(String type) {
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
* java.base/jdk.internal.foreign.abi
|
* java.base/jdk.internal.foreign.abi
|
||||||
* java.base/jdk.internal.foreign.abi.aarch64
|
* java.base/jdk.internal.foreign.abi.aarch64
|
||||||
* @build CallArrangerTestBase
|
* @build CallArrangerTestBase
|
||||||
* @run testng TestAarch64CallArranger
|
* @run testng TestLinuxAArch64CallArranger
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import java.lang.foreign.FunctionDescriptor;
|
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.assertFalse;
|
||||||
import static org.testng.Assert.assertTrue;
|
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 TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
|
||||||
private static final VMStorage RETURN_BUFFER_STORAGE = StubLocations.RETURN_BUFFER.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[]{});
|
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_PPF,
|
||||||
T_S_PPD,
|
T_S_PPD,
|
||||||
T_S_PPP,
|
T_S_PPP,
|
||||||
|
T_S_FFFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
// need to pass `num` separately as last argument preceding varargs according to spec (and for MSVC)
|
// 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_PPF, struct S_PPF)
|
||||||
CASE(T_S_PPD, struct S_PPD)
|
CASE(T_S_PPD, struct S_PPD)
|
||||||
CASE(T_S_PPP, struct S_PPP)
|
CASE(T_S_PPP, struct S_PPP)
|
||||||
|
CASE(T_S_FFFF, struct S_FFFF)
|
||||||
default: exit(-1); // invalid id
|
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_PPF { void* p0; void* p1; float p2; };
|
||||||
struct S_PPD { void* p0; void* p1; double p2; };
|
struct S_PPD { void* p0; void* p1; double p2; };
|
||||||
struct S_PPP { void* p0; void* p1; void* 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