8303409: Add Windows AArch64 ABI support to the Foreign Function & Memory API

Reviewed-by: jvernee
This commit is contained in:
Saint Wesonga 2023-03-02 13:26:27 +00:00 committed by Jorn Vernee
parent c9afd55ed6
commit fb13063943
19 changed files with 1302 additions and 250 deletions

View File

@ -176,6 +176,10 @@ static void move_stack(MacroAssembler* masm, Register tmp_reg, int in_stk_bias,
static void move_v128(MacroAssembler* masm, int out_stk_bias,
FloatRegister from_reg, VMStorage to_reg) {
switch (to_reg.type()) {
case StorageType::INTEGER:
assert(to_reg.segment_mask() == REG64_MASK, "only moves to 64-bit registers supported");
masm->fmovd(as_Register(to_reg), from_reg);
break;
case StorageType::VECTOR:
assert(to_reg.segment_mask() == V128_MASK, "only moves to v128 registers supported");
masm->fmovd(as_FloatRegister(to_reg), from_reg);

View File

@ -32,6 +32,7 @@ import java.util.function.Consumer;
import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList;
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64VaList;
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64VaList;
import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
import jdk.internal.foreign.abi.x64.windows.WinVaList;
@ -105,7 +106,7 @@ import jdk.internal.reflect.Reflection;
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, LinuxRISCV64VaList, SharedUtils.EmptyVaList {
public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, WindowsAArch64VaList, LinuxRISCV64VaList, SharedUtils.EmptyVaList {
/**
* Reads the next value as an {@code int} and advances this variable argument list's position. The behavior of this
@ -300,7 +301,7 @@ public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList
* @since 19
*/
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder, LinuxRISCV64VaList.Builder {
sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder, WindowsAArch64VaList.Builder, LinuxRISCV64VaList.Builder {
/**
* Writes an {@code int} value to the variable argument list being constructed.

View File

@ -33,6 +33,7 @@ public enum CABI {
WIN_64,
LINUX_AARCH_64,
MAC_OS_AARCH_64,
WIN_AARCH_64,
LINUX_RISCV_64;
private static final CABI ABI;
@ -55,6 +56,8 @@ public enum CABI {
} else if (ARCH.equals("aarch64")) {
if (OS.startsWith("Mac")) {
ABI = MAC_OS_AARCH_64;
} else if (OS.startsWith("Windows")) {
ABI = WIN_AARCH_64;
} else {
// The Linux ABI follows the standard AAPCS ABI
ABI = LINUX_AARCH_64;

View File

@ -59,7 +59,7 @@ public final class SystemLookup implements SymbolLookup {
try {
return switch (CABI.current()) {
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> libLookup(libs -> libs.load(jdkLibraryPath("syslookup")));
case WIN_64 -> makeWindowsLookup(); // out of line to workaround javac crash
case WIN_64, WIN_AARCH_64 -> makeWindowsLookup(); // out of line to workaround javac crash
};
} catch (Throwable ex) {
// This can happen in the event of a library loading failure - e.g. if one of the libraries the
@ -120,7 +120,7 @@ public final class SystemLookup implements SymbolLookup {
Path javahome = Path.of(GetPropertyAction.privilegedGetProperty("java.home"));
String lib = switch (CABI.current()) {
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> "lib";
case WIN_64 -> "bin";
case WIN_64, WIN_AARCH_64 -> "bin";
};
String libname = System.mapLibraryName(name);
return javahome.resolve(lib).resolve(libname);

View File

@ -27,6 +27,7 @@ package jdk.internal.foreign.abi;
import jdk.internal.foreign.SystemLookup;
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker;
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
@ -44,7 +45,7 @@ import java.lang.invoke.MethodType;
import java.util.Objects;
public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker,
SysVx64Linker, Windowsx64Linker, LinuxRISCV64Linker {
SysVx64Linker, WindowsAArch64Linker, Windowsx64Linker, LinuxRISCV64Linker {
private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {}
private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();

View File

@ -81,6 +81,11 @@ public class LinkerOptions {
return stl == null ? Stream.empty() : stl.saved().stream();
}
public boolean isVariadicFunction() {
FirstVariadicArg fva = getOption(FirstVariadicArg.class);
return fva != null;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;

View File

@ -30,6 +30,7 @@ import jdk.internal.access.SharedSecrets;
import jdk.internal.foreign.CABI;
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker;
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
@ -184,6 +185,7 @@ public final class SharedUtils {
case SYS_V -> SysVx64Linker.getInstance();
case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance();
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance();
case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance();
case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance();
};
}
@ -297,6 +299,7 @@ public final class SharedUtils {
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaList(actions, scope);
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaList(actions, scope);
case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaList(actions, scope);
case WIN_AARCH_64 -> WindowsAArch64Linker.newVaList(actions, scope);
};
}
@ -307,6 +310,7 @@ public final class SharedUtils {
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaListOfAddress(address, scope);
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaListOfAddress(address, scope);
case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaListOfAddress(address, scope);
case WIN_AARCH_64 -> WindowsAArch64Linker.newVaListOfAddress(address, scope);
};
}
@ -317,6 +321,7 @@ public final class SharedUtils {
case LINUX_AARCH_64 -> LinuxAArch64Linker.emptyVaList();
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.emptyVaList();
case LINUX_RISCV_64 -> LinuxRISCV64Linker.emptyVaList();
case WIN_AARCH_64 -> WindowsAArch64Linker.emptyVaList();
};
}

View File

@ -40,6 +40,7 @@ import jdk.internal.foreign.abi.SharedUtils;
import jdk.internal.foreign.abi.VMStorage;
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64CallArranger;
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger;
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64CallArranger;
import jdk.internal.foreign.Utils;
import java.lang.foreign.SegmentScope;
@ -60,7 +61,7 @@ import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
*
* There are minor differences between the ABIs implemented on Linux, macOS, and Windows
* which are handled in sub-classes. Clients should access these through the provided
* public constants CallArranger.LINUX and CallArranger.MACOS.
* public constants CallArranger.LINUX, CallArranger.MACOS, and CallArranger.WINDOWS.
*/
public abstract class CallArranger {
private static final int STACK_SLOT_SIZE = 8;
@ -79,7 +80,7 @@ public abstract class CallArranger {
// Although the AAPCS64 says r0-7 and v0-7 are all valid return
// registers, it's not possible to generate a C function that uses
// r2-7 and v4-7 so they are omitted here.
private static final ABIDescriptor C = abiFor(
protected static final ABIDescriptor C = abiFor(
new VMStorage[] { r0, r1, r2, r3, r4, r5, r6, r7, INDIRECT_RESULT},
new VMStorage[] { v0, v1, v2, v3, v4, v5, v6, v7 },
new VMStorage[] { r0, r1 },
@ -98,6 +99,7 @@ public abstract class CallArranger {
public static final CallArranger LINUX = new LinuxAArch64CallArranger();
public static final CallArranger MACOS = new MacOsAArch64CallArranger();
public static final CallArranger WINDOWS = new WindowsAArch64CallArranger();
/**
* Are variadic arguments assigned to registers as in the standard calling
@ -112,6 +114,31 @@ public abstract class CallArranger {
*/
protected abstract boolean requiresSubSlotStackPacking();
/**
* Are floating point arguments to variadic functions passed in general purpose registers
* instead of floating point registers?
*
* {@return true if this ABI uses general purpose registers for variadic floating point arguments.}
*/
protected abstract boolean useIntRegsForVariadicFloatingPointArgs();
/**
* Should some fields of structs that assigned to registers be passed in registers when there
* are not enough registers for all the fields of the struct?
*
* {@return true if this ABI passes some fields of a struct in registers.}
*/
protected abstract boolean spillsVariadicStructsPartially();
/**
* @return The ABIDescriptor used by the CallArranger for the current platform.
*/
protected abstract ABIDescriptor abiDescriptor();
protected TypeClass getArgumentClassForBindings(MemoryLayout layout, boolean forVariadicFunction) {
return TypeClass.classifyLayout(layout);
}
protected CallArranger() {}
public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
@ -119,10 +146,12 @@ public abstract class CallArranger {
}
public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall, LinkerOptions options) {
CallingSequenceBuilder csb = new CallingSequenceBuilder(C, forUpcall, options);
CallingSequenceBuilder csb = new CallingSequenceBuilder(abiDescriptor(), forUpcall, options);
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true);
BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false);
boolean forVariadicFunction = options.isVariadicFunction();
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true, forVariadicFunction);
BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false, forVariadicFunction) : new BoxBindingCalculator(false);
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
if (returnInMemory) {
@ -149,7 +178,7 @@ public abstract class CallArranger {
public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
Bindings bindings = getBindings(mt, cDesc, false, options);
MethodHandle handle = new DowncallLinker(C, bindings.callingSequence).getBoundMethodHandle();
MethodHandle handle = new DowncallLinker(abiDescriptor(), bindings.callingSequence).getBoundMethodHandle();
if (bindings.isInMemoryReturn) {
handle = SharedUtils.adaptDowncallForIMR(handle, cDesc, bindings.callingSequence);
@ -165,7 +194,7 @@ public abstract class CallArranger {
target = SharedUtils.adaptUpcallForIMR(target, true /* drop return, since we don't have bindings for it */);
}
return UpcallLinker.make(C, target, bindings.callingSequence, session);
return UpcallLinker.make(abiDescriptor(), target, bindings.callingSequence, session);
}
private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) {
@ -177,13 +206,15 @@ public abstract class CallArranger {
class StorageCalculator {
private final boolean forArguments;
private final boolean forVariadicFunction;
private boolean forVarArgs = false;
private final int[] nRegs = new int[] { 0, 0 };
private long stackOffset = 0;
public StorageCalculator(boolean forArguments) {
public StorageCalculator(boolean forArguments, boolean forVariadicFunction) {
this.forArguments = forArguments;
this.forVariadicFunction = forVariadicFunction;
}
void alignStack(long alignment) {
@ -212,8 +243,9 @@ public abstract class CallArranger {
VMStorage[] regAlloc(int type, int count) {
if (nRegs[type] + count <= MAX_REGISTER_ARGUMENTS) {
ABIDescriptor abiDescriptor = abiDescriptor();
VMStorage[] source =
(forArguments ? C.inputStorage : C.outputStorage)[type];
(forArguments ? abiDescriptor.inputStorage : abiDescriptor.outputStorage)[type];
VMStorage[] result = new VMStorage[count];
for (int i = 0; i < count; i++) {
result[i] = source[nRegs[type]++];
@ -228,10 +260,37 @@ public abstract class CallArranger {
}
VMStorage[] regAlloc(int type, MemoryLayout layout) {
return regAlloc(type, (int)Utils.alignUp(layout.byteSize(), 8) / 8);
boolean spillRegistersPartially = forVariadicFunction && spillsVariadicStructsPartially();
return spillRegistersPartially ?
regAllocPartial(type, layout) :
regAlloc(type, requiredRegisters(layout));
}
int requiredRegisters(MemoryLayout layout) {
return (int)Utils.alignUp(layout.byteSize(), 8) / 8;
}
VMStorage[] regAllocPartial(int type, MemoryLayout layout) {
int availableRegisters = MAX_REGISTER_ARGUMENTS - nRegs[type];
if (availableRegisters <= 0) {
return null;
}
int requestRegisters = Math.min(requiredRegisters(layout), availableRegisters);
return regAlloc(type, requestRegisters);
}
VMStorage nextStorage(int type, MemoryLayout layout) {
if (type == StorageType.VECTOR) {
boolean forVariadicFunctionArgs = forArguments && forVariadicFunction;
boolean useIntRegsForFloatingPointArgs = forVariadicFunctionArgs && useIntRegsForVariadicFloatingPointArgs();
if (useIntRegsForFloatingPointArgs) {
type = StorageType.INTEGER;
}
}
VMStorage[] storage = regAlloc(type, 1);
if (storage == null) {
return stackAlloc(layout);
@ -272,8 +331,8 @@ public abstract class CallArranger {
abstract class BindingCalculator {
protected final StorageCalculator storageCalculator;
protected BindingCalculator(boolean forArguments) {
this.storageCalculator = new StorageCalculator(forArguments);
protected BindingCalculator(boolean forArguments, boolean forVariadicFunction) {
this.storageCalculator = new StorageCalculator(forArguments, forVariadicFunction);
}
protected void spillStructUnbox(Binding.Builder bindings, MemoryLayout layout) {
@ -282,7 +341,10 @@ public abstract class CallArranger {
// struct, it must be passed on the stack. I.e. not split
// between registers and stack.
long offset = 0;
spillPartialStructUnbox(bindings, layout, 0);
}
protected void spillPartialStructUnbox(Binding.Builder bindings, MemoryLayout layout, long offset) {
while (offset < layout.byteSize()) {
long copy = Math.min(layout.byteSize() - offset, STACK_SLOT_SIZE);
VMStorage storage =
@ -334,8 +396,13 @@ public abstract class CallArranger {
}
class UnboxBindingCalculator extends BindingCalculator {
UnboxBindingCalculator(boolean forArguments) {
super(forArguments);
protected final boolean forArguments;
protected final boolean forVariadicFunction;
UnboxBindingCalculator(boolean forArguments, boolean forVariadicFunction) {
super(forArguments, forVariadicFunction);
this.forArguments = forArguments;
this.forVariadicFunction = forVariadicFunction;
}
@Override
@ -348,21 +415,21 @@ public abstract class CallArranger {
@Override
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
TypeClass argumentClass = TypeClass.classifyLayout(layout);
TypeClass argumentClass = getArgumentClassForBindings(layout, forVariadicFunction);
Binding.Builder bindings = Binding.builder();
switch (argumentClass) {
case STRUCT_REGISTER: {
assert carrier == MemorySegment.class;
VMStorage[] regs = storageCalculator.regAlloc(
StorageType.INTEGER, layout);
VMStorage[] regs = storageCalculator.regAlloc(StorageType.INTEGER, layout);
if (regs != null) {
int regIndex = 0;
long offset = 0;
while (offset < layout.byteSize()) {
while (offset < layout.byteSize() && regIndex < regs.length) {
final long copy = Math.min(layout.byteSize() - offset, 8);
VMStorage storage = regs[regIndex++];
boolean useFloat = storage.type() == StorageType.VECTOR;
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, useFloat);
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, false);
if (offset + copy < layout.byteSize()) {
bindings.dup();
}
@ -370,6 +437,11 @@ public abstract class CallArranger {
.vmStore(storage, type);
offset += copy;
}
final long bytesLeft = Math.min(layout.byteSize() - offset, 8);
if (bytesLeft > 0) {
spillPartialStructUnbox(bindings, layout, offset);
}
} else {
spillStructUnbox(bindings, layout);
}
@ -435,7 +507,7 @@ public abstract class CallArranger {
class BoxBindingCalculator extends BindingCalculator {
BoxBindingCalculator(boolean forArguments) {
super(forArguments);
super(forArguments, false);
}
@Override

View File

@ -26,6 +26,7 @@
package jdk.internal.foreign.abi.aarch64.linux;
import jdk.internal.foreign.abi.aarch64.CallArranger;
import jdk.internal.foreign.abi.ABIDescriptor;
/**
* AArch64 CallArranger specialized for Linux ABI.
@ -43,4 +44,19 @@ public class LinuxAArch64CallArranger extends CallArranger {
return false;
}
@Override
protected ABIDescriptor abiDescriptor() {
return C;
}
@Override
protected boolean useIntRegsForVariadicFloatingPointArgs() {
return false;
}
@Override
protected boolean spillsVariadicStructsPartially() {
return false;
}
}

View File

@ -26,6 +26,7 @@
package jdk.internal.foreign.abi.aarch64.macos;
import jdk.internal.foreign.abi.aarch64.CallArranger;
import jdk.internal.foreign.abi.ABIDescriptor;
/**
* AArch64 CallArranger specialized for macOS ABI.
@ -43,4 +44,19 @@ public class MacOsAArch64CallArranger extends CallArranger {
return true;
}
@Override
protected ABIDescriptor abiDescriptor() {
return C;
}
@Override
protected boolean useIntRegsForVariadicFloatingPointArgs() {
return false;
}
@Override
protected boolean spillsVariadicStructsPartially() {
return false;
}
}

View File

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

View File

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

View File

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

View File

@ -32,9 +32,13 @@
import java.lang.foreign.Arena;
import java.lang.foreign.Linker;
import java.lang.foreign.FunctionDescriptor;
import java.lang.foreign.GroupLayout;
import java.lang.foreign.MemoryLayout;
import java.lang.foreign.MemorySegment;
import java.lang.foreign.PaddingLayout;
import java.lang.foreign.ValueLayout;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.lang.invoke.MethodHandle;
@ -46,6 +50,7 @@ import java.util.List;
import java.util.function.Consumer;
import static java.lang.foreign.MemoryLayout.PathElement.*;
import static org.testng.Assert.*;
public class TestVarArgs extends CallGeneratorHelper {
@ -65,7 +70,7 @@ public class TestVarArgs extends CallGeneratorHelper {
static final MemorySegment VARARGS_ADDR = findNativeOrThrow("varargs");
@Test(dataProvider = "functions")
@Test(dataProvider = "variadicFunctions")
public void testVarArgs(int count, String fName, Ret ret, // ignore this stuff
List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
List<Arg> args = makeArgs(paramTypes, fields);
@ -106,6 +111,58 @@ public class TestVarArgs extends CallGeneratorHelper {
}
}
private static List<ParamType> createParameterTypesForStruct(int extraIntArgs) {
List<ParamType> paramTypes = new ArrayList<ParamType>();
for (int i = 0; i < extraIntArgs; i++) {
paramTypes.add(ParamType.INT);
}
paramTypes.add(ParamType.STRUCT);
return paramTypes;
}
private static List<StructFieldType> createFieldsForStruct(int fieldCount, StructFieldType fieldType) {
List<StructFieldType> fields = new ArrayList<StructFieldType>();
for (int i = 0; i < fieldCount; i++) {
fields.add(fieldType);
}
return fields;
}
@DataProvider(name = "variadicFunctions")
public static Object[][] variadicFunctions() {
List<Object[]> downcalls = new ArrayList<>();
var functionsDowncalls = functions();
for (var array : functionsDowncalls) {
downcalls.add(array);
}
// Test struct with 4 floats
int extraIntArgs = 0;
List<StructFieldType> fields = createFieldsForStruct(4, StructFieldType.FLOAT);
List<ParamType> paramTypes = createParameterTypesForStruct(extraIntArgs);
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
// Test struct with 4 floats without enough registers for all fields
extraIntArgs = 6;
fields = createFieldsForStruct(4, StructFieldType.FLOAT);
paramTypes = createParameterTypesForStruct(extraIntArgs);
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
// Test struct with 2 doubles without enough registers for all fields
extraIntArgs = 7;
fields = createFieldsForStruct(2, StructFieldType.DOUBLE);
paramTypes = createParameterTypesForStruct(extraIntArgs);
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
// Test struct with 2 ints without enough registers for all fields
fields = createFieldsForStruct(2, StructFieldType.INT);
paramTypes = createParameterTypesForStruct(extraIntArgs);
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
return downcalls.toArray(new Object[0][]);
}
private static List<Arg> makeArgs(List<ParamType> paramTypes, List<StructFieldType> fields) throws ReflectiveOperationException {
List<Arg> args = new ArrayList<>();
for (ParamType pType : paramTypes) {
@ -262,6 +319,7 @@ public class TestVarArgs extends CallGeneratorHelper {
S_PPF,
S_PPD,
S_PPP,
S_FFFF,
;
public static NativeType of(String type) {

View File

@ -30,7 +30,7 @@
* java.base/jdk.internal.foreign.abi
* java.base/jdk.internal.foreign.abi.aarch64
* @build CallArrangerTestBase
* @run testng TestAarch64CallArranger
* @run testng TestLinuxAArch64CallArranger
*/
import java.lang.foreign.FunctionDescriptor;
@ -59,7 +59,7 @@ import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
public class TestAarch64CallArranger extends CallArrangerTestBase {
public class TestLinuxAArch64CallArranger extends CallArrangerTestBase {
private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
private static final VMStorage RETURN_BUFFER_STORAGE = StubLocations.RETURN_BUFFER.storage(StorageType.PLACEHOLDER);
@ -426,224 +426,4 @@ public class TestAarch64CallArranger extends CallArrangerTestBase {
checkReturnBindings(callingSequence, new Binding[]{});
}
@Test
public void testVarArgsOnStack() {
MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT);
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT);
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
assertFalse(bindings.isInMemoryReturn());
CallingSequence callingSequence = bindings.callingSequence();
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
assertEquals(callingSequence.functionDesc(), fdExpected);
// The two variadic arguments should be allocated on the stack
checkArgumentBindings(callingSequence, new Binding[][]{
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
{ vmStore(r0, int.class) },
{ vmStore(stackStorage((short) 4, 0), int.class) },
{ vmStore(stackStorage((short) 4, 8), float.class) },
});
checkReturnBindings(callingSequence, new Binding[]{});
}
@Test
public void testMacArgsOnStack() {
MethodType mt = MethodType.methodType(void.class,
int.class, int.class, int.class, int.class,
int.class, int.class, int.class, int.class,
int.class, int.class, short.class, byte.class);
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
C_INT, C_INT, C_INT, C_INT,
C_INT, C_INT, C_INT, C_INT,
C_INT, C_INT, C_SHORT, C_CHAR);
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
assertFalse(bindings.isInMemoryReturn());
CallingSequence callingSequence = bindings.callingSequence();
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
checkArgumentBindings(callingSequence, new Binding[][]{
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
{ vmStore(r0, int.class) },
{ vmStore(r1, int.class) },
{ vmStore(r2, int.class) },
{ vmStore(r3, int.class) },
{ vmStore(r4, int.class) },
{ vmStore(r5, int.class) },
{ vmStore(r6, int.class) },
{ vmStore(r7, int.class) },
{ vmStore(stackStorage((short) 4, 0), int.class) },
{ vmStore(stackStorage((short) 4, 4), int.class) },
{ cast(short.class, int.class), vmStore(stackStorage((short) 2, 8), int.class) },
{ cast(byte.class, int.class), vmStore(stackStorage((short) 1, 10), int.class) },
});
checkReturnBindings(callingSequence, new Binding[]{});
}
@Test
public void testMacArgsOnStack2() {
StructLayout struct = MemoryLayout.structLayout(
C_FLOAT,
C_FLOAT
);
MethodType mt = MethodType.methodType(void.class,
long.class, long.class, long.class, long.class,
long.class, long.class, long.class, long.class,
double.class, double.class, double.class, double.class,
double.class, double.class, double.class, double.class,
int.class, MemorySegment.class);
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
C_INT, struct);
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
assertFalse(bindings.isInMemoryReturn());
CallingSequence callingSequence = bindings.callingSequence();
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
checkArgumentBindings(callingSequence, new Binding[][]{
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
{ vmStore(r0, long.class) },
{ vmStore(r1, long.class) },
{ vmStore(r2, long.class) },
{ vmStore(r3, long.class) },
{ vmStore(r4, long.class) },
{ vmStore(r5, long.class) },
{ vmStore(r6, long.class) },
{ vmStore(r7, long.class) },
{ vmStore(v0, double.class) },
{ vmStore(v1, double.class) },
{ vmStore(v2, double.class) },
{ vmStore(v3, double.class) },
{ vmStore(v4, double.class) },
{ vmStore(v5, double.class) },
{ vmStore(v6, double.class) },
{ vmStore(v7, double.class) },
{ vmStore(stackStorage((short) 4, 0), int.class) },
{
dup(),
bufferLoad(0, int.class),
vmStore(stackStorage((short) 4, 4), int.class),
bufferLoad(4, int.class),
vmStore(stackStorage((short) 4, 8), int.class),
}
});
checkReturnBindings(callingSequence, new Binding[]{});
}
@Test
public void testMacArgsOnStack3() {
StructLayout struct = MemoryLayout.structLayout(
C_POINTER,
C_POINTER
);
MethodType mt = MethodType.methodType(void.class,
long.class, long.class, long.class, long.class,
long.class, long.class, long.class, long.class,
double.class, double.class, double.class, double.class,
double.class, double.class, double.class, double.class,
MemorySegment.class, float.class);
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
struct, C_FLOAT);
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
assertFalse(bindings.isInMemoryReturn());
CallingSequence callingSequence = bindings.callingSequence();
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
checkArgumentBindings(callingSequence, new Binding[][]{
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
{ vmStore(r0, long.class) },
{ vmStore(r1, long.class) },
{ vmStore(r2, long.class) },
{ vmStore(r3, long.class) },
{ vmStore(r4, long.class) },
{ vmStore(r5, long.class) },
{ vmStore(r6, long.class) },
{ vmStore(r7, long.class) },
{ vmStore(v0, double.class) },
{ vmStore(v1, double.class) },
{ vmStore(v2, double.class) },
{ vmStore(v3, double.class) },
{ vmStore(v4, double.class) },
{ vmStore(v5, double.class) },
{ vmStore(v6, double.class) },
{ vmStore(v7, double.class) },
{ dup(),
bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 0), long.class),
bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 8), long.class) },
{ vmStore(stackStorage((short) 4, 16), float.class) },
});
checkReturnBindings(callingSequence, new Binding[]{});
}
@Test
public void testMacArgsOnStack4() {
StructLayout struct = MemoryLayout.structLayout(
C_INT,
C_INT,
C_POINTER
);
MethodType mt = MethodType.methodType(void.class,
long.class, long.class, long.class, long.class,
long.class, long.class, long.class, long.class,
double.class, double.class, double.class, double.class,
double.class, double.class, double.class, double.class,
float.class, MemorySegment.class);
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
C_FLOAT, struct);
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
assertFalse(bindings.isInMemoryReturn());
CallingSequence callingSequence = bindings.callingSequence();
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
checkArgumentBindings(callingSequence, new Binding[][]{
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
{ vmStore(r0, long.class) },
{ vmStore(r1, long.class) },
{ vmStore(r2, long.class) },
{ vmStore(r3, long.class) },
{ vmStore(r4, long.class) },
{ vmStore(r5, long.class) },
{ vmStore(r6, long.class) },
{ vmStore(r7, long.class) },
{ vmStore(v0, double.class) },
{ vmStore(v1, double.class) },
{ vmStore(v2, double.class) },
{ vmStore(v3, double.class) },
{ vmStore(v4, double.class) },
{ vmStore(v5, double.class) },
{ vmStore(v6, double.class) },
{ vmStore(v7, double.class) },
{ vmStore(stackStorage((short) 4, 0), float.class) },
{ dup(),
bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 8), long.class),
bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 16), long.class) },
});
checkReturnBindings(callingSequence, new Binding[]{});
}
}

View File

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

View File

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

View File

@ -128,6 +128,7 @@ enum NativeType {
T_S_PPF,
T_S_PPD,
T_S_PPP,
T_S_FFFF,
};
// need to pass `num` separately as last argument preceding varargs according to spec (and for MSVC)
@ -227,6 +228,7 @@ EXPORT void varargs(call_info* info, int num, ...) {
CASE(T_S_PPF, struct S_PPF)
CASE(T_S_PPD, struct S_PPD)
CASE(T_S_PPP, struct S_PPP)
CASE(T_S_FFFF, struct S_FFFF)
default: exit(-1); // invalid id
}
}

View File

@ -119,3 +119,4 @@ struct S_PPI { void* p0; void* p1; int p2; };
struct S_PPF { void* p0; void* p1; float p2; };
struct S_PPD { void* p0; void* p1; double p2; };
struct S_PPP { void* p0; void* p1; void* p2; };
struct S_FFFF { float p0; float p1; float p2; float p3; };