Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/hotspot/cpu/aarch64/foreignGlobals_aarch64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions src/java.base/share/classes/java/lang/foreign/VaList.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
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;
Expand Down Expand Up @@ -105,7 +106,7 @@
* @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
Expand Down Expand Up @@ -300,7 +301,7 @@ static VaList empty() {
* @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.
Expand Down
3 changes: 3 additions & 0 deletions src/java.base/share/classes/jdk/internal/foreign/CABI.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ private static SymbolLookup makeSystemLookup() {
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
Expand Down Expand Up @@ -120,7 +120,7 @@ private static Path jdkLibraryPath(String name) {
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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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;
Expand All @@ -44,7 +45,7 @@
import java.util.Objects;

public abstract sealed class AbstractLinker implements Linker permits LinuxAArch64Linker, MacOsAArch64Linker,
SysVx64Linker, Windowsx64Linker, LinuxRISCV64Linker {
SysVx64Linker, WindowsAArch64Linker, Windowsx64Linker, LinuxRISCV64Linker {

private record LinkRequest(FunctionDescriptor descriptor, LinkerOptions options) {}
private final SoftReferenceCache<LinkRequest, MethodHandle> DOWNCALL_CACHE = new SoftReferenceCache<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,11 @@ public Stream<CapturableState> capturedCallState() {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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;
Expand Down Expand Up @@ -184,6 +185,7 @@ public static Linker getSystemLinker() {
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();
};
}
Expand Down Expand Up @@ -297,6 +299,7 @@ public static VaList newVaList(Consumer<VaList.Builder> actions, SegmentScope sc
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);
};
}

Expand All @@ -307,6 +310,7 @@ public static VaList newVaListOfAddress(long address, SegmentScope scope) {
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);
};
}

Expand All @@ -317,6 +321,7 @@ public static VaList emptyVaList() {
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();
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
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;
Expand All @@ -60,7 +61,7 @@
*
* 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;
Expand All @@ -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 },
Expand All @@ -98,6 +99,7 @@ public record Bindings(CallingSequence callingSequence,

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
Expand All @@ -112,17 +114,44 @@ public record Bindings(CallingSequence callingSequence,
*/
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) {
return getBindings(mt, cDesc, forUpcall, LinkerOptions.empty());
}

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) {
Expand All @@ -149,7 +178,7 @@ public Bindings getBindings(MethodType mt, FunctionDescriptor cDesc, boolean for
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);
Expand All @@ -165,7 +194,7 @@ public MemorySegment arrangeUpcall(MethodHandle target, MethodType mt, FunctionD
target = SharedUtils.adaptUpcallForIMR(target, true /* drop return, since we don't have bindings for it */);
}

return UpcallLinker.make(C, target, bindings.callingSequence, session);
return UpcallLinker.make(abiDescriptor(), target, bindings.callingSequence, session);
}

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

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) {
Expand Down Expand Up @@ -212,8 +243,9 @@ VMStorage stackAlloc(MemoryLayout layout) {

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]++];
Expand All @@ -228,10 +260,37 @@ VMStorage[] regAlloc(int type, int count) {
}

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);
Expand Down Expand Up @@ -272,8 +331,8 @@ void adjustForVarArgs() {
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) {
Expand All @@ -282,7 +341,10 @@ protected void spillStructUnbox(Binding.Builder bindings, MemoryLayout layout) {
// 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 =
Expand Down Expand Up @@ -334,8 +396,13 @@ protected void spillStructBox(Binding.Builder bindings, MemoryLayout layout) {
}

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
Expand All @@ -348,28 +415,33 @@ List<Binding> getIndirectBindings() {

@Override
List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {
TypeClass argumentClass = TypeClass.classifyLayout(layout);
TypeClass argumentClass = getArgumentClassForBindings(layout, forVariadicFunction);
Binding.Builder bindings = Binding.builder();

switch (argumentClass) {
case STRUCT_REGISTER: {
assert carrier == MemorySegment.class;
VMStorage[] regs = storageCalculator.regAlloc(
StorageType.INTEGER, layout);
VMStorage[] regs = storageCalculator.regAlloc(StorageType.INTEGER, layout);

if (regs != null) {
int regIndex = 0;
long offset = 0;
while (offset < layout.byteSize()) {
while (offset < layout.byteSize() && regIndex < regs.length) {
final long copy = Math.min(layout.byteSize() - offset, 8);
VMStorage storage = regs[regIndex++];
boolean useFloat = storage.type() == StorageType.VECTOR;
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, useFloat);
Class<?> type = SharedUtils.primitiveCarrierForSize(copy, false);
if (offset + copy < layout.byteSize()) {
bindings.dup();
}
bindings.bufferLoad(offset, type)
.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);
}
Expand Down Expand Up @@ -435,7 +507,7 @@ List<Binding> getBindings(Class<?> carrier, MemoryLayout layout) {

class BoxBindingCalculator extends BindingCalculator {
BoxBindingCalculator(boolean forArguments) {
super(forArguments);
super(forArguments, false);
}

@Override
Expand Down
Loading