diff --git a/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp b/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp index e20c7e40601..90c1942c32d 100644 --- a/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp +++ b/src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp @@ -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); diff --git a/src/java.base/share/classes/java/lang/foreign/VaList.java b/src/java.base/share/classes/java/lang/foreign/VaList.java index 53c8c5ed598..5ce79bd2ae2 100644 --- a/src/java.base/share/classes/java/lang/foreign/VaList.java +++ b/src/java.base/share/classes/java/lang/foreign/VaList.java @@ -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. diff --git a/src/java.base/share/classes/jdk/internal/foreign/CABI.java b/src/java.base/share/classes/jdk/internal/foreign/CABI.java index 0248177d190..fa1661ff5a6 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/CABI.java +++ b/src/java.base/share/classes/jdk/internal/foreign/CABI.java @@ -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; diff --git a/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java index f9be03661a0..b4681e0814a 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java +++ b/src/java.base/share/classes/jdk/internal/foreign/SystemLookup.java @@ -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); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java index 512b44418a8..42669d9a92e 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java @@ -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 DOWNCALL_CACHE = new SoftReferenceCache<>(); diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java index 7e78f0d5002..7b194cf24fd 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/LinkerOptions.java @@ -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; diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java index a03b33d7aa1..92ced59b9be 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/SharedUtils.java @@ -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(); }; } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java index afc292fdd28..a167b400111 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/CallArranger.java @@ -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 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 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 diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64CallArranger.java index 7947365a1a7..d79b3337f6e 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/linux/LinuxAArch64CallArranger.java @@ -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; + } + } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64CallArranger.java index 7c491d27b9e..e099d8cd93f 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64CallArranger.java +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/macos/MacOsAArch64CallArranger.java @@ -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; + } + } diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64CallArranger.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64CallArranger.java new file mode 100644 index 00000000000..a1caf3da65c --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64CallArranger.java @@ -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; + } +} \ No newline at end of file diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java new file mode 100644 index 00000000000..0c2a28098fe --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64Linker.java @@ -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 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(); + } + +} \ No newline at end of file diff --git a/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64VaList.java b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64VaList.java new file mode 100644 index 00000000000..e6ca31537d9 --- /dev/null +++ b/src/java.base/share/classes/jdk/internal/foreign/abi/aarch64/windows/WindowsAArch64VaList.java @@ -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 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); + } + } +} diff --git a/test/jdk/java/foreign/TestVarArgs.java b/test/jdk/java/foreign/TestVarArgs.java index 6536d886042..ca2545300b8 100644 --- a/test/jdk/java/foreign/TestVarArgs.java +++ b/test/jdk/java/foreign/TestVarArgs.java @@ -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 paramTypes, List fields) throws Throwable { List args = makeArgs(paramTypes, fields); @@ -106,6 +111,58 @@ public class TestVarArgs extends CallGeneratorHelper { } } + private static List createParameterTypesForStruct(int extraIntArgs) { + List paramTypes = new ArrayList(); + for (int i = 0; i < extraIntArgs; i++) { + paramTypes.add(ParamType.INT); + } + paramTypes.add(ParamType.STRUCT); + return paramTypes; + } + + private static List createFieldsForStruct(int fieldCount, StructFieldType fieldType) { + List fields = new ArrayList(); + for (int i = 0; i < fieldCount; i++) { + fields.add(fieldType); + } + return fields; + } + + @DataProvider(name = "variadicFunctions") + public static Object[][] variadicFunctions() { + List downcalls = new ArrayList<>(); + + var functionsDowncalls = functions(); + for (var array : functionsDowncalls) { + downcalls.add(array); + } + + // Test struct with 4 floats + int extraIntArgs = 0; + List fields = createFieldsForStruct(4, StructFieldType.FLOAT); + List 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 makeArgs(List paramTypes, List fields) throws ReflectiveOperationException { List 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) { diff --git a/test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java b/test/jdk/java/foreign/callarranger/TestLinuxAArch64CallArranger.java similarity index 63% rename from test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java rename to test/jdk/java/foreign/callarranger/TestLinuxAArch64CallArranger.java index 6b33f237b60..1cac8403b2b 100644 --- a/test/jdk/java/foreign/callarranger/TestAarch64CallArranger.java +++ b/test/jdk/java/foreign/callarranger/TestLinuxAArch64CallArranger.java @@ -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[]{}); - } } diff --git a/test/jdk/java/foreign/callarranger/TestMacOsAArch64CallArranger.java b/test/jdk/java/foreign/callarranger/TestMacOsAArch64CallArranger.java new file mode 100644 index 00000000000..6a673fb5be8 --- /dev/null +++ b/test/jdk/java/foreign/callarranger/TestMacOsAArch64CallArranger.java @@ -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[]{}); + } +} diff --git a/test/jdk/java/foreign/callarranger/TestWindowsAArch64CallArranger.java b/test/jdk/java/foreign/callarranger/TestWindowsAArch64CallArranger.java new file mode 100644 index 00000000000..9fe3566001c --- /dev/null +++ b/test/jdk/java/foreign/callarranger/TestWindowsAArch64CallArranger.java @@ -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[]{}); + } +} diff --git a/test/jdk/java/foreign/libVarArgs.c b/test/jdk/java/foreign/libVarArgs.c index 708bf27535e..a3d0938ae29 100644 --- a/test/jdk/java/foreign/libVarArgs.c +++ b/test/jdk/java/foreign/libVarArgs.c @@ -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 } } diff --git a/test/jdk/java/foreign/shared.h b/test/jdk/java/foreign/shared.h index 376ad1d811f..1cadc1075d6 100644 --- a/test/jdk/java/foreign/shared.h +++ b/test/jdk/java/foreign/shared.h @@ -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; };