Skip to content

Commit fb13063

Browse files
swesongaJornVernee
authored andcommittedMar 2, 2023
8303409: Add Windows AArch64 ABI support to the Foreign Function & Memory API
Reviewed-by: jvernee
1 parent c9afd55 commit fb13063

19 files changed

+1302
-250
lines changed
 

‎src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,10 @@ static void move_stack(MacroAssembler* masm, Register tmp_reg, int in_stk_bias,
176176
static void move_v128(MacroAssembler* masm, int out_stk_bias,
177177
FloatRegister from_reg, VMStorage to_reg) {
178178
switch (to_reg.type()) {
179+
case StorageType::INTEGER:
180+
assert(to_reg.segment_mask() == REG64_MASK, "only moves to 64-bit registers supported");
181+
masm->fmovd(as_Register(to_reg), from_reg);
182+
break;
179183
case StorageType::VECTOR:
180184
assert(to_reg.segment_mask() == V128_MASK, "only moves to v128 registers supported");
181185
masm->fmovd(as_FloatRegister(to_reg), from_reg);

‎src/java.base/share/classes/java/lang/foreign/VaList.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import jdk.internal.foreign.abi.SharedUtils;
3333
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64VaList;
3434
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64VaList;
35+
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64VaList;
3536
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64VaList;
3637
import jdk.internal.foreign.abi.x64.sysv.SysVVaList;
3738
import jdk.internal.foreign.abi.x64.windows.WinVaList;
@@ -105,7 +106,7 @@
105106
* @since 19
106107
*/
107108
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
108-
public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, LinuxRISCV64VaList, SharedUtils.EmptyVaList {
109+
public sealed interface VaList permits WinVaList, SysVVaList, LinuxAArch64VaList, MacOsAArch64VaList, WindowsAArch64VaList, LinuxRISCV64VaList, SharedUtils.EmptyVaList {
109110

110111
/**
111112
* Reads the next value as an {@code int} and advances this variable argument list's position. The behavior of this
@@ -300,7 +301,7 @@ static VaList empty() {
300301
* @since 19
301302
*/
302303
@PreviewFeature(feature=PreviewFeature.Feature.FOREIGN)
303-
sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder, LinuxRISCV64VaList.Builder {
304+
sealed interface Builder permits WinVaList.Builder, SysVVaList.Builder, LinuxAArch64VaList.Builder, MacOsAArch64VaList.Builder, WindowsAArch64VaList.Builder, LinuxRISCV64VaList.Builder {
304305

305306
/**
306307
* Writes an {@code int} value to the variable argument list being constructed.

‎src/java.base/share/classes/jdk/internal/foreign/CABI.java

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ public enum CABI {
3333
WIN_64,
3434
LINUX_AARCH_64,
3535
MAC_OS_AARCH_64,
36+
WIN_AARCH_64,
3637
LINUX_RISCV_64;
3738

3839
private static final CABI ABI;
@@ -55,6 +56,8 @@ public enum CABI {
5556
} else if (ARCH.equals("aarch64")) {
5657
if (OS.startsWith("Mac")) {
5758
ABI = MAC_OS_AARCH_64;
59+
} else if (OS.startsWith("Windows")) {
60+
ABI = WIN_AARCH_64;
5861
} else {
5962
// The Linux ABI follows the standard AAPCS ABI
6063
ABI = LINUX_AARCH_64;

‎src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ private static SymbolLookup makeSystemLookup() {
5959
try {
6060
return switch (CABI.current()) {
6161
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> libLookup(libs -> libs.load(jdkLibraryPath("syslookup")));
62-
case WIN_64 -> makeWindowsLookup(); // out of line to workaround javac crash
62+
case WIN_64, WIN_AARCH_64 -> makeWindowsLookup(); // out of line to workaround javac crash
6363
};
6464
} catch (Throwable ex) {
6565
// This can happen in the event of a library loading failure - e.g. if one of the libraries the
@@ -120,7 +120,7 @@ private static Path jdkLibraryPath(String name) {
120120
Path javahome = Path.of(GetPropertyAction.privilegedGetProperty("java.home"));
121121
String lib = switch (CABI.current()) {
122122
case SYS_V, LINUX_AARCH_64, MAC_OS_AARCH_64, LINUX_RISCV_64 -> "lib";
123-
case WIN_64 -> "bin";
123+
case WIN_64, WIN_AARCH_64 -> "bin";
124124
};
125125
String libname = System.mapLibraryName(name);
126126
return javahome.resolve(lib).resolve(libname);

‎src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import jdk.internal.foreign.SystemLookup;
2828
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
2929
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
30+
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker;
3031
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
3132
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
3233
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
@@ -44,7 +45,7 @@
4445
import java.util.Objects;
4546

4647
public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker,
47-
SysVx64Linker, Windowsx64Linker, LinuxRISCV64Linker {
48+
SysVx64Linker, WindowsAArch64Linker, Windowsx64Linker, LinuxRISCV64Linker {
4849

4950
private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {}
5051
private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();

‎src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java

+5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ public Stream<CapturableState> capturedCallState() {
8181
return stl == null ? Stream.empty() : stl.saved().stream();
8282
}
8383

84+
public boolean isVariadicFunction() {
85+
FirstVariadicArg fva = getOption(FirstVariadicArg.class);
86+
return fva != null;
87+
}
88+
8489
@Override
8590
public boolean equals(Object o) {
8691
if (this == o) return true;

‎src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java

+5
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import jdk.internal.foreign.CABI;
3131
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
3232
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
33+
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64Linker;
3334
import jdk.internal.foreign.abi.riscv64.linux.LinuxRISCV64Linker;
3435
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
3536
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
@@ -184,6 +185,7 @@ public static Linker getSystemLinker() {
184185
case SYS_V -> SysVx64Linker.getInstance();
185186
case LINUX_AARCH_64 -> LinuxAArch64Linker.getInstance();
186187
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.getInstance();
188+
case WIN_AARCH_64 -> WindowsAArch64Linker.getInstance();
187189
case LINUX_RISCV_64 -> LinuxRISCV64Linker.getInstance();
188190
};
189191
}
@@ -297,6 +299,7 @@ public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope sc
297299
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaList(actions, scope);
298300
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaList(actions, scope);
299301
case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaList(actions, scope);
302+
case WIN_AARCH_64 -> WindowsAArch64Linker.newVaList(actions, scope);
300303
};
301304
}
302305

@@ -307,6 +310,7 @@ public static VaList newVaListOfAddress(long address, SegmentScope scope) {
307310
case LINUX_AARCH_64 -> LinuxAArch64Linker.newVaListOfAddress(address, scope);
308311
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.newVaListOfAddress(address, scope);
309312
case LINUX_RISCV_64 -> LinuxRISCV64Linker.newVaListOfAddress(address, scope);
313+
case WIN_AARCH_64 -> WindowsAArch64Linker.newVaListOfAddress(address, scope);
310314
};
311315
}
312316

@@ -317,6 +321,7 @@ public static VaList emptyVaList() {
317321
case LINUX_AARCH_64 -> LinuxAArch64Linker.emptyVaList();
318322
case MAC_OS_AARCH_64 -> MacOsAArch64Linker.emptyVaList();
319323
case LINUX_RISCV_64 -> LinuxRISCV64Linker.emptyVaList();
324+
case WIN_AARCH_64 -> WindowsAArch64Linker.emptyVaList();
320325
};
321326
}
322327

‎src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java

+94-22
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import jdk.internal.foreign.abi.VMStorage;
4141
import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64CallArranger;
4242
import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger;
43+
import jdk.internal.foreign.abi.aarch64.windows.WindowsAArch64CallArranger;
4344
import jdk.internal.foreign.Utils;
4445

4546
import java.lang.foreign.SegmentScope;
@@ -60,7 +61,7 @@
6061
*
6162
* There are minor differences between the ABIs implemented on Linux, macOS, and Windows
6263
* which are handled in sub-classes. Clients should access these through the provided
63-
* public constants CallArranger.LINUX and CallArranger.MACOS.
64+
* public constants CallArranger.LINUX, CallArranger.MACOS, and CallArranger.WINDOWS.
6465
*/
6566
public abstract class CallArranger {
6667
private static final int STACK_SLOT_SIZE = 8;
@@ -79,7 +80,7 @@ public abstract class CallArranger {
7980
// Although the AAPCS64 says r0-7 and v0-7 are all valid return
8081
// registers, it's not possible to generate a C function that uses
8182
// r2-7 and v4-7 so they are omitted here.
82-
private static final ABIDescriptor C = abiFor(
83+
protected static final ABIDescriptor C = abiFor(
8384
new VMStorage[] { r0, r1, r2, r3, r4, r5, r6, r7, INDIRECT_RESULT},
8485
new VMStorage[] { v0, v1, v2, v3, v4, v5, v6, v7 },
8586
new VMStorage[] { r0, r1 },
@@ -98,6 +99,7 @@ public record Bindings(CallingSequence callingSequence,
9899

99100
public static final CallArranger LINUX = new LinuxAArch64CallArranger();
100101
public static final CallArranger MACOS = new MacOsAArch64CallArranger();
102+
public static final CallArranger WINDOWS = new WindowsAArch64CallArranger();
101103

102104
/**
103105
* Are variadic arguments assigned to registers as in the standard calling
@@ -112,17 +114,44 @@ public record Bindings(CallingSequence callingSequence,
112114
*/
113115
protected abstract boolean requiresSubSlotStackPacking();
114116

117+
/**
118+
* Are floating point arguments to variadic functions passed in general purpose registers
119+
* instead of floating point registers?
120+
*
121+
* {@return true if this ABI uses general purpose registers for variadic floating point arguments.}
122+
*/
123+
protected abstract boolean useIntRegsForVariadicFloatingPointArgs();
124+
125+
/**
126+
* Should some fields of structs that assigned to registers be passed in registers when there
127+
* are not enough registers for all the fields of the struct?
128+
*
129+
* {@return true if this ABI passes some fields of a struct in registers.}
130+
*/
131+
protected abstract boolean spillsVariadicStructsPartially();
132+
133+
/**
134+
* @return The ABIDescriptor used by the CallArranger for the current platform.
135+
*/
136+
protected abstract ABIDescriptor abiDescriptor();
137+
138+
protected TypeClass getArgumentClassForBindings(MemoryLayout layout, boolean forVariadicFunction) {
139+
return TypeClass.classifyLayout(layout);
140+
}
141+
115142
protected CallArranger() {}
116143

117144
public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall) {
118145
return getBindings(mt, cDesc, forUpcall, LinkerOptions.empty());
119146
}
120147

121148
public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean forUpcall, LinkerOptions options) {
122-
CallingSequenceBuilder csb = new CallingSequenceBuilder(C, forUpcall, options);
149+
CallingSequenceBuilder csb = new CallingSequenceBuilder(abiDescriptor(), forUpcall, options);
123150

124-
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true);
125-
BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false) : new BoxBindingCalculator(false);
151+
boolean forVariadicFunction = options.isVariadicFunction();
152+
153+
BindingCalculator argCalc = forUpcall ? new BoxBindingCalculator(true) : new UnboxBindingCalculator(true, forVariadicFunction);
154+
BindingCalculator retCalc = forUpcall ? new UnboxBindingCalculator(false, forVariadicFunction) : new BoxBindingCalculator(false);
126155

127156
boolean returnInMemory = isInMemoryReturn(cDesc.returnLayout());
128157
if (returnInMemory) {
@@ -149,7 +178,7 @@ public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean for
149178
public MethodHandle arrangeDowncall(MethodType mt, FunctionDescriptor cDesc, LinkerOptions options) {
150179
Bindings bindings = getBindings(mt, cDesc, false, options);
151180

152-
MethodHandle handle = new DowncallLinker(C, bindings.callingSequence).getBoundMethodHandle();
181+
MethodHandle handle = new DowncallLinker(abiDescriptor(), bindings.callingSequence).getBoundMethodHandle();
153182

154183
if (bindings.isInMemoryReturn) {
155184
handle = SharedUtils.adaptDowncallForIMR(handle, cDesc, bindings.callingSequence);
@@ -165,7 +194,7 @@ public MemorySegment arrangeUpcall(MethodHandle target, MethodType mt, FunctionD
165194
target = SharedUtils.adaptUpcallForIMR(target, true /* drop return, since we don't have bindings for it */);
166195
}
167196

168-
return UpcallLinker.make(C, target, bindings.callingSequence, session);
197+
return UpcallLinker.make(abiDescriptor(), target, bindings.callingSequence, session);
169198
}
170199

171200
private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) {
@@ -177,13 +206,15 @@ private static boolean isInMemoryReturn(Optional<MemoryLayout> returnLayout) {
177206

178207
class StorageCalculator {
179208
private final boolean forArguments;
209+
private final boolean forVariadicFunction;
180210
private boolean forVarArgs = false;
181211

182212
private final int[] nRegs = new int[] { 0, 0 };
183213
private long stackOffset = 0;
184214

185-
public StorageCalculator(boolean forArguments) {
215+
public StorageCalculator(boolean forArguments, boolean forVariadicFunction) {
186216
this.forArguments = forArguments;
217+
this.forVariadicFunction = forVariadicFunction;
187218
}
188219

189220
void alignStack(long alignment) {
@@ -212,8 +243,9 @@ VMStorage stackAlloc(MemoryLayout layout) {
212243

213244
VMStorage[] regAlloc(int type, int count) {
214245
if (nRegs[type] + count <= MAX_REGISTER_ARGUMENTS) {
246+
ABIDescriptor abiDescriptor = abiDescriptor();
215247
VMStorage[] source =
216-
(forArguments ? C.inputStorage : C.outputStorage)[type];
248+
(forArguments ? abiDescriptor.inputStorage : abiDescriptor.outputStorage)[type];
217249
VMStorage[] result = new VMStorage[count];
218250
for (int i = 0; i < count; i++) {
219251
result[i] = source[nRegs[type]++];
@@ -228,10 +260,37 @@ VMStorage[] regAlloc(int type, int count) {
228260
}
229261

230262
VMStorage[] regAlloc(int type, MemoryLayout layout) {
231-
return regAlloc(type, (int)Utils.alignUp(layout.byteSize(), 8) / 8);
263+
boolean spillRegistersPartially = forVariadicFunction && spillsVariadicStructsPartially();
264+
265+
return spillRegistersPartially ?
266+
regAllocPartial(type, layout) :
267+
regAlloc(type, requiredRegisters(layout));
268+
}
269+
270+
int requiredRegisters(MemoryLayout layout) {
271+
return (int)Utils.alignUp(layout.byteSize(), 8) / 8;
272+
}
273+
274+
VMStorage[] regAllocPartial(int type, MemoryLayout layout) {
275+
int availableRegisters = MAX_REGISTER_ARGUMENTS - nRegs[type];
276+
if (availableRegisters <= 0) {
277+
return null;
278+
}
279+
280+
int requestRegisters = Math.min(requiredRegisters(layout), availableRegisters);
281+
return regAlloc(type, requestRegisters);
232282
}
233283

234284
VMStorage nextStorage(int type, MemoryLayout layout) {
285+
if (type == StorageType.VECTOR) {
286+
boolean forVariadicFunctionArgs = forArguments && forVariadicFunction;
287+
boolean useIntRegsForFloatingPointArgs = forVariadicFunctionArgs && useIntRegsForVariadicFloatingPointArgs();
288+
289+
if (useIntRegsForFloatingPointArgs) {
290+
type = StorageType.INTEGER;
291+
}
292+
}
293+
235294
VMStorage[] storage = regAlloc(type, 1);
236295
if (storage == null) {
237296
return stackAlloc(layout);
@@ -272,8 +331,8 @@ void adjustForVarArgs() {
272331
abstract class BindingCalculator {
273332
protected final StorageCalculator storageCalculator;
274333

275-
protected BindingCalculator(boolean forArguments) {
276-
this.storageCalculator = new StorageCalculator(forArguments);
334+
protected BindingCalculator(boolean forArguments, boolean forVariadicFunction) {
335+
this.storageCalculator = new StorageCalculator(forArguments, forVariadicFunction);
277336
}
278337

279338
protected void spillStructUnbox(Binding.Builder bindings, MemoryLayout layout) {
@@ -282,7 +341,10 @@ protected void spillStructUnbox(Binding.Builder bindings, MemoryLayout layout) {
282341
// struct, it must be passed on the stack. I.e. not split
283342
// between registers and stack.
284343

285-
long offset = 0;
344+
spillPartialStructUnbox(bindings, layout, 0);
345+
}
346+
347+
protected void spillPartialStructUnbox(Binding.Builder bindings, MemoryLayout layout, long offset) {
286348
while (offset < layout.byteSize()) {
287349
long copy = Math.min(layout.byteSize() - offset, STACK_SLOT_SIZE);
288350
VMStorage storage =
@@ -334,8 +396,13 @@ protected void spillStructBox(Binding.Builder bindings, MemoryLayout layout) {
334396
}
335397

336398
class UnboxBindingCalculator extends BindingCalculator {
337-
UnboxBindingCalculator(boolean forArguments) {
338-
super(forArguments);
399+
protected final boolean forArguments;
400+
protected final boolean forVariadicFunction;
401+
402+
UnboxBindingCalculator(boolean forArguments, boolean forVariadicFunction) {
403+
super(forArguments, forVariadicFunction);
404+
this.forArguments = forArguments;
405+
this.forVariadicFunction = forVariadicFunction;
339406
}
340407

341408
@Override
@@ -348,28 +415,33 @@ List<Binding> getIndirectBindings() {
348415

349416
@Override
350417
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
351-
TypeClass argumentClass = TypeClass.classifyLayout(layout);
418+
TypeClass argumentClass = getArgumentClassForBindings(layout, forVariadicFunction);
352419
Binding.Builder bindings = Binding.builder();
420+
353421
switch (argumentClass) {
354422
case STRUCT_REGISTER: {
355423
assert carrier == MemorySegment.class;
356-
VMStorage[] regs = storageCalculator.regAlloc(
357-
StorageType.INTEGER, layout);
424+
VMStorage[] regs = storageCalculator.regAlloc(StorageType.INTEGER, layout);
425+
358426
if (regs != null) {
359427
int regIndex = 0;
360428
long offset = 0;
361-
while (offset < layout.byteSize()) {
429+
while (offset < layout.byteSize() && regIndex < regs.length) {
362430
final long copy = Math.min(layout.byteSize() - offset, 8);
363431
VMStorage storage = regs[regIndex++];
364-
boolean useFloat = storage.type() == StorageType.VECTOR;
365-
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, useFloat);
432+
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, false);
366433
if (offset + copy < layout.byteSize()) {
367434
bindings.dup();
368435
}
369436
bindings.bufferLoad(offset, type)
370437
.vmStore(storage, type);
371438
offset += copy;
372439
}
440+
441+
final long bytesLeft = Math.min(layout.byteSize() - offset, 8);
442+
if (bytesLeft > 0) {
443+
spillPartialStructUnbox(bindings, layout, offset);
444+
}
373445
} else {
374446
spillStructUnbox(bindings, layout);
375447
}
@@ -435,7 +507,7 @@ List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
435507

436508
class BoxBindingCalculator extends BindingCalculator {
437509
BoxBindingCalculator(boolean forArguments) {
438-
super(forArguments);
510+
super(forArguments, false);
439511
}
440512

441513
@Override

‎src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64CallArranger.java

+16
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
package jdk.internal.foreign.abi.aarch64.linux;
2727

2828
import jdk.internal.foreign.abi.aarch64.CallArranger;
29+
import jdk.internal.foreign.abi.ABIDescriptor;
2930

3031
/**
3132
* AArch64 CallArranger specialized for Linux ABI.
@@ -43,4 +44,19 @@ protected boolean requiresSubSlotStackPacking() {
4344
return false;
4445
}
4546

47+
@Override
48+
protected ABIDescriptor abiDescriptor() {
49+
return C;
50+
}
51+
52+
@Override
53+
protected boolean useIntRegsForVariadicFloatingPointArgs() {
54+
return false;
55+
}
56+
57+
@Override
58+
protected boolean spillsVariadicStructsPartially() {
59+
return false;
60+
}
61+
4662
}

‎src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64CallArranger.java

+16
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
package jdk.internal.foreign.abi.aarch64.macos;
2727

2828
import jdk.internal.foreign.abi.aarch64.CallArranger;
29+
import jdk.internal.foreign.abi.ABIDescriptor;
2930

3031
/**
3132
* AArch64 CallArranger specialized for macOS ABI.
@@ -43,4 +44,19 @@ protected boolean requiresSubSlotStackPacking() {
4344
return true;
4445
}
4546

47+
@Override
48+
protected ABIDescriptor abiDescriptor() {
49+
return C;
50+
}
51+
52+
@Override
53+
protected boolean useIntRegsForVariadicFloatingPointArgs() {
54+
return false;
55+
}
56+
57+
@Override
58+
protected boolean spillsVariadicStructsPartially() {
59+
return false;
60+
}
61+
4662
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2021, Arm Limited. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
package jdk.internal.foreign.abi.aarch64.windows;
27+
28+
import jdk.internal.foreign.abi.aarch64.CallArranger;
29+
import jdk.internal.foreign.abi.aarch64.TypeClass;
30+
import jdk.internal.foreign.abi.ABIDescriptor;
31+
import jdk.internal.foreign.abi.VMStorage;
32+
33+
import java.lang.foreign.MemoryLayout;
34+
35+
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
36+
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
37+
38+
/**
39+
* AArch64 CallArranger specialized for Windows ABI.
40+
*/
41+
public class WindowsAArch64CallArranger extends CallArranger {
42+
43+
private static final VMStorage INDIRECT_RESULT = r8;
44+
45+
// This is derived from the AAPCS64 spec, restricted to what's
46+
// possible when calling to/from C code.
47+
//
48+
// The indirect result register, r8, is used to return a large
49+
// struct by value. It's treated as an input here as the caller is
50+
// responsible for allocating storage and passing this into the
51+
// function.
52+
//
53+
// Although the AAPCS64 says r0-7 and v0-7 are all valid return
54+
// registers, it's not possible to generate a C function that uses
55+
// r2-7 and v4-7 so they are omitted here.
56+
private static final ABIDescriptor WindowsAArch64AbiDescriptor = abiFor(
57+
new VMStorage[] { r0, r1, r2, r3, r4, r5, r6, r7, INDIRECT_RESULT},
58+
new VMStorage[] { v0, v1, v2, v3, v4, v5, v6, v7 },
59+
new VMStorage[] { r0, r1 },
60+
new VMStorage[] { v0, v1, v2, v3 },
61+
new VMStorage[] { r9, r10, r11, r12, r13, r14, r15, r16, r17 },
62+
new VMStorage[] { v16, v17, v18, v19, v20, v21, v22, v23, v24, v25,
63+
v26, v27, v28, v29, v30, v31 },
64+
16, // Stack is always 16 byte aligned on AArch64
65+
0, // No shadow space
66+
r9, // target addr reg
67+
r10 // return buffer addr reg
68+
);
69+
70+
@Override
71+
protected ABIDescriptor abiDescriptor() {
72+
return WindowsAArch64AbiDescriptor;
73+
}
74+
75+
@Override
76+
protected boolean varArgsOnStack() {
77+
return false;
78+
}
79+
80+
@Override
81+
protected boolean requiresSubSlotStackPacking() {
82+
return false;
83+
}
84+
85+
@Override
86+
protected boolean useIntRegsForVariadicFloatingPointArgs() {
87+
// The Windows ABI requires floating point arguments to be passed in
88+
// general purpose registers when calling variadic functions.
89+
return true;
90+
}
91+
92+
@Override
93+
protected boolean spillsVariadicStructsPartially() {
94+
return true;
95+
}
96+
97+
@Override
98+
protected TypeClass getArgumentClassForBindings(MemoryLayout layout, boolean forVariadicFunction) {
99+
TypeClass argumentClass = TypeClass.classifyLayout(layout);
100+
101+
// HFA struct arguments are classified as STRUCT_REGISTER when
102+
// general purpose registers are being used to pass floating point
103+
// arguments. If the HFA is too big to pass entirely in general
104+
// purpose registers, it is classified as an ordinary struct
105+
// (i.e. as a STRUCT_REFERENCE).
106+
if (argumentClass == TypeClass.STRUCT_HFA && forVariadicFunction) {
107+
// The Windows ABI requires the members of the variadic HFA to be
108+
// passed in general purpose registers but only a STRUCT_HFA that
109+
// is at most 16 bytes can be passed in general purpose registers.
110+
argumentClass = layout.byteSize() <= 16 ? TypeClass.STRUCT_REGISTER : TypeClass.STRUCT_REFERENCE;
111+
}
112+
113+
return argumentClass;
114+
}
115+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright (c) 2021, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2021, Arm Limited. All rights reserved.
4+
* Copyright (c) 2021, 2022, Microsoft. All rights reserved.
5+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6+
*
7+
* This code is free software; you can redistribute it and/or modify it
8+
* under the terms of the GNU General Public License version 2 only, as
9+
* published by the Free Software Foundation. Oracle designates this
10+
* particular file as subject to the "Classpath" exception as provided
11+
* by Oracle in the LICENSE file that accompanied this code.
12+
*
13+
* This code is distributed in the hope that it will be useful, but WITHOUT
14+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16+
* version 2 for more details (a copy is included in the LICENSE file that
17+
* accompanied this code).
18+
*
19+
* You should have received a copy of the GNU General Public License version
20+
* 2 along with this work; if not, write to the Free Software Foundation,
21+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
22+
*
23+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
24+
* or visit www.oracle.com if you need additional information or have any
25+
* questions.
26+
*/
27+
package jdk.internal.foreign.abi.aarch64.windows;
28+
29+
import jdk.internal.foreign.abi.AbstractLinker;
30+
import jdk.internal.foreign.abi.LinkerOptions;
31+
import jdk.internal.foreign.abi.aarch64.CallArranger;
32+
33+
import java.lang.foreign.FunctionDescriptor;
34+
import java.lang.foreign.MemorySegment;
35+
import java.lang.foreign.SegmentScope;
36+
import java.lang.foreign.VaList;
37+
import java.lang.invoke.MethodHandle;
38+
import java.lang.invoke.MethodType;
39+
import java.util.function.Consumer;
40+
41+
/**
42+
* ABI implementation for Windows/AArch64. Based on AAPCS with
43+
* changes to va_list.
44+
*/
45+
public final class WindowsAArch64Linker extends AbstractLinker {
46+
private static WindowsAArch64Linker instance;
47+
48+
public static WindowsAArch64Linker getInstance() {
49+
if (instance == null) {
50+
instance = new WindowsAArch64Linker();
51+
}
52+
return instance;
53+
}
54+
55+
@Override
56+
protected MethodHandle arrangeDowncall(MethodType inferredMethodType, FunctionDescriptor function, LinkerOptions options) {
57+
return CallArranger.WINDOWS.arrangeDowncall(inferredMethodType, function, options);
58+
}
59+
60+
@Override
61+
protected MemorySegment arrangeUpcall(MethodHandle target, MethodType targetType, FunctionDescriptor function, SegmentScope scope) {
62+
return CallArranger.WINDOWS.arrangeUpcall(target, targetType, function, scope);
63+
}
64+
65+
public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope scope) {
66+
WindowsAArch64VaList.Builder builder = WindowsAArch64VaList.builder(scope);
67+
actions.accept(builder);
68+
return builder.build();
69+
}
70+
71+
public static VaList newVaListOfAddress(long address, SegmentScope scope) {
72+
return WindowsAArch64VaList.ofAddress(address, scope);
73+
}
74+
75+
public static VaList emptyVaList() {
76+
return WindowsAArch64VaList.empty();
77+
}
78+
79+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
/*
2+
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2021, Arm Limited. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
package jdk.internal.foreign.abi.aarch64.windows;
27+
28+
import java.lang.foreign.GroupLayout;
29+
import java.lang.foreign.MemoryLayout;
30+
import java.lang.foreign.MemorySegment;
31+
import java.lang.foreign.SegmentScope;
32+
import java.lang.foreign.SegmentAllocator;
33+
import java.lang.foreign.VaList;
34+
import java.lang.foreign.ValueLayout;
35+
import jdk.internal.foreign.abi.aarch64.TypeClass;
36+
import jdk.internal.foreign.MemorySessionImpl;
37+
import jdk.internal.foreign.abi.SharedUtils;
38+
import jdk.internal.foreign.abi.SharedUtils.SimpleVaArg;
39+
40+
import java.lang.invoke.VarHandle;
41+
import java.util.ArrayList;
42+
import java.util.List;
43+
import java.util.Objects;
44+
45+
import static jdk.internal.foreign.PlatformLayouts.AArch64.C_POINTER;
46+
import static jdk.internal.foreign.abi.SharedUtils.alignUp;
47+
48+
// see vadefs.h (VC header) for the ARM64 va_arg impl
49+
//
50+
// typedef char* va_list;
51+
//
52+
// #define __crt_va_arg(ap, t) \
53+
// ((sizeof(t) > (2 * sizeof(__int64))) \
54+
// ? **(t**)((ap += sizeof(__int64)) - sizeof(__int64)) \
55+
// : *(t*)((ap += _SLOTSIZEOF(t) + _APALIGN(t,ap)) - _SLOTSIZEOF(t)))
56+
//
57+
public non-sealed class WindowsAArch64VaList implements VaList {
58+
private static final long VA_SLOT_SIZE_BYTES = 8;
59+
private static final VarHandle VH_address = C_POINTER.varHandle();
60+
61+
private static final VaList EMPTY = new SharedUtils.EmptyVaList(MemorySegment.NULL);
62+
63+
private MemorySegment segment;
64+
65+
private WindowsAArch64VaList(MemorySegment segment) {
66+
this.segment = segment;
67+
}
68+
69+
public static VaList empty() {
70+
return EMPTY;
71+
}
72+
73+
@Override
74+
public int nextVarg(ValueLayout.OfInt layout) {
75+
return (int) read(layout);
76+
}
77+
78+
@Override
79+
public long nextVarg(ValueLayout.OfLong layout) {
80+
return (long) read(layout);
81+
}
82+
83+
@Override
84+
public double nextVarg(ValueLayout.OfDouble layout) {
85+
return (double) read(layout);
86+
}
87+
88+
@Override
89+
public MemorySegment nextVarg(ValueLayout.OfAddress layout) {
90+
return (MemorySegment) read(layout);
91+
}
92+
93+
@Override
94+
public MemorySegment nextVarg(GroupLayout layout, SegmentAllocator allocator) {
95+
Objects.requireNonNull(allocator);
96+
return (MemorySegment) read(layout, allocator);
97+
}
98+
99+
private Object read(MemoryLayout layout) {
100+
return read(layout, SharedUtils.THROWING_ALLOCATOR);
101+
}
102+
103+
private Object read(MemoryLayout layout, SegmentAllocator allocator) {
104+
Objects.requireNonNull(layout);
105+
Object res;
106+
if (layout instanceof GroupLayout) {
107+
TypeClass typeClass = TypeClass.classifyLayout(layout);
108+
res = switch (typeClass) {
109+
case STRUCT_REFERENCE -> {
110+
checkElement(layout, VA_SLOT_SIZE_BYTES);
111+
MemorySegment structAddr = (MemorySegment) VH_address.get(segment);
112+
MemorySegment struct = MemorySegment.ofAddress(structAddr.address(), layout.byteSize(), segment.scope());
113+
MemorySegment seg = allocator.allocate(layout);
114+
seg.copyFrom(struct);
115+
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
116+
yield seg;
117+
}
118+
case STRUCT_REGISTER, STRUCT_HFA -> {
119+
long size = alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
120+
checkElement(layout, size);
121+
MemorySegment struct = allocator.allocate(layout)
122+
.copyFrom(segment.asSlice(0, layout.byteSize()));
123+
segment = segment.asSlice(size);
124+
yield struct;
125+
}
126+
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
127+
};
128+
} else {
129+
checkElement(layout, VA_SLOT_SIZE_BYTES);
130+
VarHandle reader = layout.varHandle();
131+
res = reader.get(segment);
132+
segment = segment.asSlice(VA_SLOT_SIZE_BYTES);
133+
}
134+
return res;
135+
}
136+
137+
private static long sizeOf(MemoryLayout layout) {
138+
return switch (TypeClass.classifyLayout(layout)) {
139+
case STRUCT_REGISTER, STRUCT_HFA -> alignUp(layout.byteSize(), VA_SLOT_SIZE_BYTES);
140+
default -> VA_SLOT_SIZE_BYTES;
141+
};
142+
}
143+
144+
@Override
145+
public void skip(MemoryLayout... layouts) {
146+
Objects.requireNonNull(layouts);
147+
((MemorySessionImpl) segment.scope()).checkValidState();
148+
149+
for (MemoryLayout layout : layouts) {
150+
Objects.requireNonNull(layout);
151+
long size = sizeOf(layout);
152+
checkElement(layout, size);
153+
segment = segment.asSlice(size);
154+
}
155+
}
156+
157+
private void checkElement(MemoryLayout layout, long size) {
158+
if (segment.byteSize() < size) {
159+
throw SharedUtils.newVaListNSEE(layout);
160+
}
161+
}
162+
163+
static WindowsAArch64VaList ofAddress(long address, SegmentScope session) {
164+
MemorySegment segment = MemorySegment.ofAddress(address, Long.MAX_VALUE, session);
165+
return new WindowsAArch64VaList(segment);
166+
}
167+
168+
static Builder builder(SegmentScope session) {
169+
return new Builder(session);
170+
}
171+
172+
@Override
173+
public VaList copy() {
174+
((MemorySessionImpl) segment.scope()).checkValidState();
175+
return new WindowsAArch64VaList(segment);
176+
}
177+
178+
@Override
179+
public MemorySegment segment() {
180+
// make sure that returned segment cannot be accessed
181+
return segment.asSlice(0, 0);
182+
}
183+
184+
public static non-sealed class Builder implements VaList.Builder {
185+
186+
private final SegmentScope session;
187+
private final List<SimpleVaArg> args = new ArrayList<>();
188+
189+
public Builder(SegmentScope session) {
190+
((MemorySessionImpl) session).checkValidState();
191+
this.session = session;
192+
}
193+
194+
private Builder arg(MemoryLayout layout, Object value) {
195+
Objects.requireNonNull(layout);
196+
Objects.requireNonNull(value);
197+
args.add(new SimpleVaArg(layout, value));
198+
return this;
199+
}
200+
201+
@Override
202+
public Builder addVarg(ValueLayout.OfInt layout, int value) {
203+
return arg(layout, value);
204+
}
205+
206+
@Override
207+
public Builder addVarg(ValueLayout.OfLong layout, long value) {
208+
return arg(layout, value);
209+
}
210+
211+
@Override
212+
public Builder addVarg(ValueLayout.OfDouble layout, double value) {
213+
return arg(layout, value);
214+
}
215+
216+
@Override
217+
public Builder addVarg(ValueLayout.OfAddress layout, MemorySegment value) {
218+
return arg(layout, value);
219+
}
220+
221+
@Override
222+
public Builder addVarg(GroupLayout layout, MemorySegment value) {
223+
return arg(layout, value);
224+
}
225+
226+
public VaList build() {
227+
if (args.isEmpty()) {
228+
return EMPTY;
229+
}
230+
231+
long allocationSize = args.stream().reduce(0L, (acc, e) -> acc + sizeOf(e.layout), Long::sum);
232+
MemorySegment segment = MemorySegment.allocateNative(allocationSize, session);
233+
MemorySegment cursor = segment;
234+
235+
for (SimpleVaArg arg : args) {
236+
if (arg.layout instanceof GroupLayout) {
237+
MemorySegment msArg = ((MemorySegment) arg.value);
238+
TypeClass typeClass = TypeClass.classifyLayout(arg.layout);
239+
switch (typeClass) {
240+
case STRUCT_REFERENCE -> {
241+
MemorySegment copy = MemorySegment.allocateNative(arg.layout, session);
242+
copy.copyFrom(msArg); // by-value
243+
VH_address.set(cursor, copy);
244+
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
245+
}
246+
case STRUCT_REGISTER, STRUCT_HFA ->
247+
cursor = cursor.copyFrom(msArg.asSlice(0, arg.layout.byteSize()))
248+
.asSlice(alignUp(arg.layout.byteSize(), VA_SLOT_SIZE_BYTES));
249+
default -> throw new IllegalStateException("Unexpected TypeClass: " + typeClass);
250+
}
251+
} else {
252+
VarHandle writer = arg.varHandle();
253+
writer.set(cursor, arg.value);
254+
cursor = cursor.asSlice(VA_SLOT_SIZE_BYTES);
255+
}
256+
}
257+
258+
return new WindowsAArch64VaList(segment);
259+
}
260+
}
261+
}

‎test/jdk/java/foreign/TestVarArgs.java

+59-1
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,13 @@
3232
import java.lang.foreign.Arena;
3333
import java.lang.foreign.Linker;
3434
import java.lang.foreign.FunctionDescriptor;
35+
import java.lang.foreign.GroupLayout;
3536
import java.lang.foreign.MemoryLayout;
3637
import java.lang.foreign.MemorySegment;
38+
import java.lang.foreign.PaddingLayout;
39+
import java.lang.foreign.ValueLayout;
3740

41+
import org.testng.annotations.DataProvider;
3842
import org.testng.annotations.Test;
3943

4044
import java.lang.invoke.MethodHandle;
@@ -46,6 +50,7 @@
4650
import java.util.function.Consumer;
4751

4852
import static java.lang.foreign.MemoryLayout.PathElement.*;
53+
import static org.testng.Assert.*;
4954

5055
public class TestVarArgs extends CallGeneratorHelper {
5156

@@ -65,7 +70,7 @@ public class TestVarArgs extends CallGeneratorHelper {
6570

6671
static final MemorySegment VARARGS_ADDR = findNativeOrThrow("varargs");
6772

68-
@Test(dataProvider = "functions")
73+
@Test(dataProvider = "variadicFunctions")
6974
public void testVarArgs(int count, String fName, Ret ret, // ignore this stuff
7075
List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
7176
List<Arg> args = makeArgs(paramTypes, fields);
@@ -106,6 +111,58 @@ public void testVarArgs(int count, String fName, Ret ret, // ignore this stuff
106111
}
107112
}
108113

114+
private static List<ParamType> createParameterTypesForStruct(int extraIntArgs) {
115+
List<ParamType> paramTypes = new ArrayList<ParamType>();
116+
for (int i = 0; i < extraIntArgs; i++) {
117+
paramTypes.add(ParamType.INT);
118+
}
119+
paramTypes.add(ParamType.STRUCT);
120+
return paramTypes;
121+
}
122+
123+
private static List<StructFieldType> createFieldsForStruct(int fieldCount, StructFieldType fieldType) {
124+
List<StructFieldType> fields = new ArrayList<StructFieldType>();
125+
for (int i = 0; i < fieldCount; i++) {
126+
fields.add(fieldType);
127+
}
128+
return fields;
129+
}
130+
131+
@DataProvider(name = "variadicFunctions")
132+
public static Object[][] variadicFunctions() {
133+
List<Object[]> downcalls = new ArrayList<>();
134+
135+
var functionsDowncalls = functions();
136+
for (var array : functionsDowncalls) {
137+
downcalls.add(array);
138+
}
139+
140+
// Test struct with 4 floats
141+
int extraIntArgs = 0;
142+
List<StructFieldType> fields = createFieldsForStruct(4, StructFieldType.FLOAT);
143+
List<ParamType> paramTypes = createParameterTypesForStruct(extraIntArgs);
144+
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
145+
146+
// Test struct with 4 floats without enough registers for all fields
147+
extraIntArgs = 6;
148+
fields = createFieldsForStruct(4, StructFieldType.FLOAT);
149+
paramTypes = createParameterTypesForStruct(extraIntArgs);
150+
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
151+
152+
// Test struct with 2 doubles without enough registers for all fields
153+
extraIntArgs = 7;
154+
fields = createFieldsForStruct(2, StructFieldType.DOUBLE);
155+
paramTypes = createParameterTypesForStruct(extraIntArgs);
156+
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
157+
158+
// Test struct with 2 ints without enough registers for all fields
159+
fields = createFieldsForStruct(2, StructFieldType.INT);
160+
paramTypes = createParameterTypesForStruct(extraIntArgs);
161+
downcalls.add(new Object[] { 0, "", Ret.VOID, paramTypes, fields });
162+
163+
return downcalls.toArray(new Object[0][]);
164+
}
165+
109166
private static List<Arg> makeArgs(List<ParamType> paramTypes, List<StructFieldType> fields) throws ReflectiveOperationException {
110167
List<Arg> args = new ArrayList<>();
111168
for (ParamType pType : paramTypes) {
@@ -262,6 +319,7 @@ enum NativeType {
262319
S_PPF,
263320
S_PPD,
264321
S_PPP,
322+
S_FFFF,
265323
;
266324

267325
public static NativeType of(String type) {

‎test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java ‎test/jdk/java/foreign/callarranger/TestLinuxAArch64CallArranger.java

+2-222
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
* java.base/jdk.internal.foreign.abi
3131
* java.base/jdk.internal.foreign.abi.aarch64
3232
* @build CallArrangerTestBase
33-
* @run testng TestAarch64CallArranger
33+
* @run testng TestLinuxAArch64CallArranger
3434
*/
3535

3636
import java.lang.foreign.FunctionDescriptor;
@@ -59,7 +59,7 @@
5959
import static org.testng.Assert.assertFalse;
6060
import static org.testng.Assert.assertTrue;
6161

62-
public class TestAarch64CallArranger extends CallArrangerTestBase {
62+
public class TestLinuxAArch64CallArranger extends CallArrangerTestBase {
6363

6464
private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
6565
private static final VMStorage RETURN_BUFFER_STORAGE = StubLocations.RETURN_BUFFER.storage(StorageType.PLACEHOLDER);
@@ -426,224 +426,4 @@ public void testVarArgsInRegs() {
426426

427427
checkReturnBindings(callingSequence, new Binding[]{});
428428
}
429-
430-
@Test
431-
public void testVarArgsOnStack() {
432-
MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
433-
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT);
434-
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT);
435-
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
436-
437-
assertFalse(bindings.isInMemoryReturn());
438-
CallingSequence callingSequence = bindings.callingSequence();
439-
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
440-
assertEquals(callingSequence.functionDesc(), fdExpected);
441-
442-
// The two variadic arguments should be allocated on the stack
443-
checkArgumentBindings(callingSequence, new Binding[][]{
444-
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
445-
{ vmStore(r0, int.class) },
446-
{ vmStore(stackStorage((short) 4, 0), int.class) },
447-
{ vmStore(stackStorage((short) 4, 8), float.class) },
448-
});
449-
450-
checkReturnBindings(callingSequence, new Binding[]{});
451-
}
452-
453-
@Test
454-
public void testMacArgsOnStack() {
455-
MethodType mt = MethodType.methodType(void.class,
456-
int.class, int.class, int.class, int.class,
457-
int.class, int.class, int.class, int.class,
458-
int.class, int.class, short.class, byte.class);
459-
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
460-
C_INT, C_INT, C_INT, C_INT,
461-
C_INT, C_INT, C_INT, C_INT,
462-
C_INT, C_INT, C_SHORT, C_CHAR);
463-
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
464-
465-
assertFalse(bindings.isInMemoryReturn());
466-
CallingSequence callingSequence = bindings.callingSequence();
467-
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
468-
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
469-
470-
checkArgumentBindings(callingSequence, new Binding[][]{
471-
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
472-
{ vmStore(r0, int.class) },
473-
{ vmStore(r1, int.class) },
474-
{ vmStore(r2, int.class) },
475-
{ vmStore(r3, int.class) },
476-
{ vmStore(r4, int.class) },
477-
{ vmStore(r5, int.class) },
478-
{ vmStore(r6, int.class) },
479-
{ vmStore(r7, int.class) },
480-
{ vmStore(stackStorage((short) 4, 0), int.class) },
481-
{ vmStore(stackStorage((short) 4, 4), int.class) },
482-
{ cast(short.class, int.class), vmStore(stackStorage((short) 2, 8), int.class) },
483-
{ cast(byte.class, int.class), vmStore(stackStorage((short) 1, 10), int.class) },
484-
});
485-
486-
checkReturnBindings(callingSequence, new Binding[]{});
487-
}
488-
489-
@Test
490-
public void testMacArgsOnStack2() {
491-
StructLayout struct = MemoryLayout.structLayout(
492-
C_FLOAT,
493-
C_FLOAT
494-
);
495-
MethodType mt = MethodType.methodType(void.class,
496-
long.class, long.class, long.class, long.class,
497-
long.class, long.class, long.class, long.class,
498-
double.class, double.class, double.class, double.class,
499-
double.class, double.class, double.class, double.class,
500-
int.class, MemorySegment.class);
501-
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
502-
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
503-
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
504-
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
505-
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
506-
C_INT, struct);
507-
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
508-
509-
assertFalse(bindings.isInMemoryReturn());
510-
CallingSequence callingSequence = bindings.callingSequence();
511-
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
512-
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
513-
514-
checkArgumentBindings(callingSequence, new Binding[][]{
515-
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
516-
{ vmStore(r0, long.class) },
517-
{ vmStore(r1, long.class) },
518-
{ vmStore(r2, long.class) },
519-
{ vmStore(r3, long.class) },
520-
{ vmStore(r4, long.class) },
521-
{ vmStore(r5, long.class) },
522-
{ vmStore(r6, long.class) },
523-
{ vmStore(r7, long.class) },
524-
{ vmStore(v0, double.class) },
525-
{ vmStore(v1, double.class) },
526-
{ vmStore(v2, double.class) },
527-
{ vmStore(v3, double.class) },
528-
{ vmStore(v4, double.class) },
529-
{ vmStore(v5, double.class) },
530-
{ vmStore(v6, double.class) },
531-
{ vmStore(v7, double.class) },
532-
{ vmStore(stackStorage((short) 4, 0), int.class) },
533-
{
534-
dup(),
535-
bufferLoad(0, int.class),
536-
vmStore(stackStorage((short) 4, 4), int.class),
537-
bufferLoad(4, int.class),
538-
vmStore(stackStorage((short) 4, 8), int.class),
539-
}
540-
});
541-
542-
checkReturnBindings(callingSequence, new Binding[]{});
543-
}
544-
545-
@Test
546-
public void testMacArgsOnStack3() {
547-
StructLayout struct = MemoryLayout.structLayout(
548-
C_POINTER,
549-
C_POINTER
550-
);
551-
MethodType mt = MethodType.methodType(void.class,
552-
long.class, long.class, long.class, long.class,
553-
long.class, long.class, long.class, long.class,
554-
double.class, double.class, double.class, double.class,
555-
double.class, double.class, double.class, double.class,
556-
MemorySegment.class, float.class);
557-
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
558-
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
559-
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
560-
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
561-
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
562-
struct, C_FLOAT);
563-
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
564-
565-
assertFalse(bindings.isInMemoryReturn());
566-
CallingSequence callingSequence = bindings.callingSequence();
567-
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
568-
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
569-
570-
checkArgumentBindings(callingSequence, new Binding[][]{
571-
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
572-
{ vmStore(r0, long.class) },
573-
{ vmStore(r1, long.class) },
574-
{ vmStore(r2, long.class) },
575-
{ vmStore(r3, long.class) },
576-
{ vmStore(r4, long.class) },
577-
{ vmStore(r5, long.class) },
578-
{ vmStore(r6, long.class) },
579-
{ vmStore(r7, long.class) },
580-
{ vmStore(v0, double.class) },
581-
{ vmStore(v1, double.class) },
582-
{ vmStore(v2, double.class) },
583-
{ vmStore(v3, double.class) },
584-
{ vmStore(v4, double.class) },
585-
{ vmStore(v5, double.class) },
586-
{ vmStore(v6, double.class) },
587-
{ vmStore(v7, double.class) },
588-
{ dup(),
589-
bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 0), long.class),
590-
bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 8), long.class) },
591-
{ vmStore(stackStorage((short) 4, 16), float.class) },
592-
});
593-
594-
checkReturnBindings(callingSequence, new Binding[]{});
595-
}
596-
597-
@Test
598-
public void testMacArgsOnStack4() {
599-
StructLayout struct = MemoryLayout.structLayout(
600-
C_INT,
601-
C_INT,
602-
C_POINTER
603-
);
604-
MethodType mt = MethodType.methodType(void.class,
605-
long.class, long.class, long.class, long.class,
606-
long.class, long.class, long.class, long.class,
607-
double.class, double.class, double.class, double.class,
608-
double.class, double.class, double.class, double.class,
609-
float.class, MemorySegment.class);
610-
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
611-
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
612-
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
613-
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
614-
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
615-
C_FLOAT, struct);
616-
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
617-
618-
assertFalse(bindings.isInMemoryReturn());
619-
CallingSequence callingSequence = bindings.callingSequence();
620-
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
621-
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
622-
623-
checkArgumentBindings(callingSequence, new Binding[][]{
624-
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
625-
{ vmStore(r0, long.class) },
626-
{ vmStore(r1, long.class) },
627-
{ vmStore(r2, long.class) },
628-
{ vmStore(r3, long.class) },
629-
{ vmStore(r4, long.class) },
630-
{ vmStore(r5, long.class) },
631-
{ vmStore(r6, long.class) },
632-
{ vmStore(r7, long.class) },
633-
{ vmStore(v0, double.class) },
634-
{ vmStore(v1, double.class) },
635-
{ vmStore(v2, double.class) },
636-
{ vmStore(v3, double.class) },
637-
{ vmStore(v4, double.class) },
638-
{ vmStore(v5, double.class) },
639-
{ vmStore(v6, double.class) },
640-
{ vmStore(v7, double.class) },
641-
{ vmStore(stackStorage((short) 4, 0), float.class) },
642-
{ dup(),
643-
bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 8), long.class),
644-
bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 16), long.class) },
645-
});
646-
647-
checkReturnBindings(callingSequence, new Binding[]{});
648-
}
649429
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,283 @@
1+
/*
2+
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*
23+
*/
24+
25+
/*
26+
* @test
27+
* @enablePreview
28+
* @modules java.base/jdk.internal.foreign
29+
* java.base/jdk.internal.foreign.abi
30+
* java.base/jdk.internal.foreign.abi.aarch64
31+
* @build CallArrangerTestBase
32+
* @run testng TestMacOsAArch64CallArranger
33+
*/
34+
35+
import java.lang.foreign.FunctionDescriptor;
36+
import java.lang.foreign.MemoryLayout;
37+
import java.lang.foreign.StructLayout;
38+
import java.lang.foreign.MemorySegment;
39+
import jdk.internal.foreign.abi.Binding;
40+
import jdk.internal.foreign.abi.CallingSequence;
41+
import jdk.internal.foreign.abi.LinkerOptions;
42+
import jdk.internal.foreign.abi.StubLocations;
43+
import jdk.internal.foreign.abi.VMStorage;
44+
import jdk.internal.foreign.abi.aarch64.CallArranger;
45+
import org.testng.annotations.DataProvider;
46+
import org.testng.annotations.Test;
47+
48+
import java.lang.invoke.MethodType;
49+
50+
import static java.lang.foreign.Linker.Option.firstVariadicArg;
51+
import static java.lang.foreign.ValueLayout.ADDRESS;
52+
import static jdk.internal.foreign.PlatformLayouts.AArch64.*;
53+
import static jdk.internal.foreign.abi.Binding.*;
54+
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.*;
55+
import static jdk.internal.foreign.abi.aarch64.AArch64Architecture.Regs.*;
56+
import static org.testng.Assert.assertEquals;
57+
import static org.testng.Assert.assertFalse;
58+
import static org.testng.Assert.assertTrue;
59+
60+
public class TestMacOsAArch64CallArranger extends CallArrangerTestBase {
61+
62+
private static final VMStorage TARGET_ADDRESS_STORAGE = StubLocations.TARGET_ADDRESS.storage(StorageType.PLACEHOLDER);
63+
64+
@Test
65+
public void testVarArgsOnStack() {
66+
MethodType mt = MethodType.methodType(void.class, int.class, int.class, float.class);
67+
FunctionDescriptor fd = FunctionDescriptor.ofVoid(C_INT, C_INT, C_FLOAT);
68+
FunctionDescriptor fdExpected = FunctionDescriptor.ofVoid(ADDRESS, C_INT, C_INT, C_FLOAT);
69+
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false, LinkerOptions.forDowncall(fd, firstVariadicArg(1)));
70+
71+
assertFalse(bindings.isInMemoryReturn());
72+
CallingSequence callingSequence = bindings.callingSequence();
73+
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
74+
assertEquals(callingSequence.functionDesc(), fdExpected);
75+
76+
// The two variadic arguments should be allocated on the stack
77+
checkArgumentBindings(callingSequence, new Binding[][]{
78+
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
79+
{ vmStore(r0, int.class) },
80+
{ vmStore(stackStorage((short) 4, 0), int.class) },
81+
{ vmStore(stackStorage((short) 4, 8), float.class) },
82+
});
83+
84+
checkReturnBindings(callingSequence, new Binding[]{});
85+
}
86+
87+
@Test
88+
public void testMacArgsOnStack() {
89+
MethodType mt = MethodType.methodType(void.class,
90+
int.class, int.class, int.class, int.class,
91+
int.class, int.class, int.class, int.class,
92+
int.class, int.class, short.class, byte.class);
93+
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
94+
C_INT, C_INT, C_INT, C_INT,
95+
C_INT, C_INT, C_INT, C_INT,
96+
C_INT, C_INT, C_SHORT, C_CHAR);
97+
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
98+
99+
assertFalse(bindings.isInMemoryReturn());
100+
CallingSequence callingSequence = bindings.callingSequence();
101+
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
102+
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
103+
104+
checkArgumentBindings(callingSequence, new Binding[][]{
105+
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
106+
{ vmStore(r0, int.class) },
107+
{ vmStore(r1, int.class) },
108+
{ vmStore(r2, int.class) },
109+
{ vmStore(r3, int.class) },
110+
{ vmStore(r4, int.class) },
111+
{ vmStore(r5, int.class) },
112+
{ vmStore(r6, int.class) },
113+
{ vmStore(r7, int.class) },
114+
{ vmStore(stackStorage((short) 4, 0), int.class) },
115+
{ vmStore(stackStorage((short) 4, 4), int.class) },
116+
{ cast(short.class, int.class), vmStore(stackStorage((short) 2, 8), int.class) },
117+
{ cast(byte.class, int.class), vmStore(stackStorage((short) 1, 10), int.class) },
118+
});
119+
120+
checkReturnBindings(callingSequence, new Binding[]{});
121+
}
122+
123+
@Test
124+
public void testMacArgsOnStack2() {
125+
StructLayout struct = MemoryLayout.structLayout(
126+
C_FLOAT,
127+
C_FLOAT
128+
);
129+
MethodType mt = MethodType.methodType(void.class,
130+
long.class, long.class, long.class, long.class,
131+
long.class, long.class, long.class, long.class,
132+
double.class, double.class, double.class, double.class,
133+
double.class, double.class, double.class, double.class,
134+
int.class, MemorySegment.class);
135+
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
136+
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
137+
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
138+
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
139+
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
140+
C_INT, struct);
141+
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
142+
143+
assertFalse(bindings.isInMemoryReturn());
144+
CallingSequence callingSequence = bindings.callingSequence();
145+
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
146+
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
147+
148+
checkArgumentBindings(callingSequence, new Binding[][]{
149+
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
150+
{ vmStore(r0, long.class) },
151+
{ vmStore(r1, long.class) },
152+
{ vmStore(r2, long.class) },
153+
{ vmStore(r3, long.class) },
154+
{ vmStore(r4, long.class) },
155+
{ vmStore(r5, long.class) },
156+
{ vmStore(r6, long.class) },
157+
{ vmStore(r7, long.class) },
158+
{ vmStore(v0, double.class) },
159+
{ vmStore(v1, double.class) },
160+
{ vmStore(v2, double.class) },
161+
{ vmStore(v3, double.class) },
162+
{ vmStore(v4, double.class) },
163+
{ vmStore(v5, double.class) },
164+
{ vmStore(v6, double.class) },
165+
{ vmStore(v7, double.class) },
166+
{ vmStore(stackStorage((short) 4, 0), int.class) },
167+
{
168+
dup(),
169+
bufferLoad(0, int.class),
170+
vmStore(stackStorage((short) 4, 4), int.class),
171+
bufferLoad(4, int.class),
172+
vmStore(stackStorage((short) 4, 8), int.class),
173+
}
174+
});
175+
176+
checkReturnBindings(callingSequence, new Binding[]{});
177+
}
178+
179+
@Test
180+
public void testMacArgsOnStack3() {
181+
StructLayout struct = MemoryLayout.structLayout(
182+
C_POINTER,
183+
C_POINTER
184+
);
185+
MethodType mt = MethodType.methodType(void.class,
186+
long.class, long.class, long.class, long.class,
187+
long.class, long.class, long.class, long.class,
188+
double.class, double.class, double.class, double.class,
189+
double.class, double.class, double.class, double.class,
190+
MemorySegment.class, float.class);
191+
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
192+
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
193+
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
194+
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
195+
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
196+
struct, C_FLOAT);
197+
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
198+
199+
assertFalse(bindings.isInMemoryReturn());
200+
CallingSequence callingSequence = bindings.callingSequence();
201+
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
202+
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
203+
204+
checkArgumentBindings(callingSequence, new Binding[][]{
205+
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
206+
{ vmStore(r0, long.class) },
207+
{ vmStore(r1, long.class) },
208+
{ vmStore(r2, long.class) },
209+
{ vmStore(r3, long.class) },
210+
{ vmStore(r4, long.class) },
211+
{ vmStore(r5, long.class) },
212+
{ vmStore(r6, long.class) },
213+
{ vmStore(r7, long.class) },
214+
{ vmStore(v0, double.class) },
215+
{ vmStore(v1, double.class) },
216+
{ vmStore(v2, double.class) },
217+
{ vmStore(v3, double.class) },
218+
{ vmStore(v4, double.class) },
219+
{ vmStore(v5, double.class) },
220+
{ vmStore(v6, double.class) },
221+
{ vmStore(v7, double.class) },
222+
{ dup(),
223+
bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 0), long.class),
224+
bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 8), long.class) },
225+
{ vmStore(stackStorage((short) 4, 16), float.class) },
226+
});
227+
228+
checkReturnBindings(callingSequence, new Binding[]{});
229+
}
230+
231+
@Test
232+
public void testMacArgsOnStack4() {
233+
StructLayout struct = MemoryLayout.structLayout(
234+
C_INT,
235+
C_INT,
236+
C_POINTER
237+
);
238+
MethodType mt = MethodType.methodType(void.class,
239+
long.class, long.class, long.class, long.class,
240+
long.class, long.class, long.class, long.class,
241+
double.class, double.class, double.class, double.class,
242+
double.class, double.class, double.class, double.class,
243+
float.class, MemorySegment.class);
244+
FunctionDescriptor fd = FunctionDescriptor.ofVoid(
245+
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
246+
C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG,
247+
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
248+
C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE,
249+
C_FLOAT, struct);
250+
CallArranger.Bindings bindings = CallArranger.MACOS.getBindings(mt, fd, false);
251+
252+
assertFalse(bindings.isInMemoryReturn());
253+
CallingSequence callingSequence = bindings.callingSequence();
254+
assertEquals(callingSequence.callerMethodType(), mt.insertParameterTypes(0, MemorySegment.class));
255+
assertEquals(callingSequence.functionDesc(), fd.insertArgumentLayouts(0, ADDRESS));
256+
257+
checkArgumentBindings(callingSequence, new Binding[][]{
258+
{ unboxAddress(), vmStore(TARGET_ADDRESS_STORAGE, long.class) },
259+
{ vmStore(r0, long.class) },
260+
{ vmStore(r1, long.class) },
261+
{ vmStore(r2, long.class) },
262+
{ vmStore(r3, long.class) },
263+
{ vmStore(r4, long.class) },
264+
{ vmStore(r5, long.class) },
265+
{ vmStore(r6, long.class) },
266+
{ vmStore(r7, long.class) },
267+
{ vmStore(v0, double.class) },
268+
{ vmStore(v1, double.class) },
269+
{ vmStore(v2, double.class) },
270+
{ vmStore(v3, double.class) },
271+
{ vmStore(v4, double.class) },
272+
{ vmStore(v5, double.class) },
273+
{ vmStore(v6, double.class) },
274+
{ vmStore(v7, double.class) },
275+
{ vmStore(stackStorage((short) 4, 0), float.class) },
276+
{ dup(),
277+
bufferLoad(0, long.class), vmStore(stackStorage((short) 8, 8), long.class),
278+
bufferLoad(8, long.class), vmStore(stackStorage((short) 8, 16), long.class) },
279+
});
280+
281+
checkReturnBindings(callingSequence, new Binding[]{});
282+
}
283+
}

‎test/jdk/java/foreign/callarranger/TestWindowsAArch64CallArranger.java

+350
Large diffs are not rendered by default.

‎test/jdk/java/foreign/libVarArgs.c

+2
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ enum NativeType {
128128
T_S_PPF,
129129
T_S_PPD,
130130
T_S_PPP,
131+
T_S_FFFF,
131132
};
132133

133134
// 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, ...) {
227228
CASE(T_S_PPF, struct S_PPF)
228229
CASE(T_S_PPD, struct S_PPD)
229230
CASE(T_S_PPP, struct S_PPP)
231+
CASE(T_S_FFFF, struct S_FFFF)
230232
default: exit(-1); // invalid id
231233
}
232234
}

‎test/jdk/java/foreign/shared.h

+1
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,4 @@ struct S_PPI { void* p0; void* p1; int p2; };
119119
struct S_PPF { void* p0; void* p1; float p2; };
120120
struct S_PPD { void* p0; void* p1; double p2; };
121121
struct S_PPP { void* p0; void* p1; void* p2; };
122+
struct S_FFFF { float p0; float p1; float p2; float p3; };

0 commit comments

Comments
 (0)
Please sign in to comment.