diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifyAArch64RegisterUsages.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifyAArch64RegisterUsages.java index a0d35c1c0bdd..b4bfa7e65dea 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifyAArch64RegisterUsages.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifyAArch64RegisterUsages.java @@ -62,6 +62,7 @@ protected void verify(StructuredGraph graph, CoreProviders context) { case "jdk.graal.compiler.hotspot.aarch64.AArch64HotSpotMacroAssembler.": case "jdk.graal.compiler.hotspot.aarch64.AArch64HotSpotBackend.emitCodePrefix": case "com.oracle.svm.core.aarch64.SubstrateAArch64MacroAssembler.": + case "com.oracle.svm.core.graal.aarch64.SubstrateAArch64RegisterConfig.getCallingConvention": // Exempted cases return; default: diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/aarch64/AArch64Address.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/aarch64/AArch64Address.java index 72ef6a8e4ed6..efbe763cae6f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/aarch64/AArch64Address.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/aarch64/AArch64Address.java @@ -309,7 +309,11 @@ public static AArch64Address createExtendedRegisterOffsetAddress(int bitMemoryTr * @param bitMemoryTransferSize Memory operation size. */ public static AArch64Address createPCLiteralAddress(int bitMemoryTransferSize) { - return new AArch64Address(bitMemoryTransferSize, zr, zr, 0, false, null, AddressingMode.PC_LITERAL); + return createPCLiteralAddress(bitMemoryTransferSize, 0); + } + + public static AArch64Address createPCLiteralAddress(int bitMemoryTransferSize, int immediate) { + return new AArch64Address(bitMemoryTransferSize, zr, zr, immediate, false, null, AddressingMode.PC_LITERAL); } /** diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 11fe4c9b3237..d63dcbb8e85c 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -763,10 +763,14 @@ "jdk.internal.foreign.abi.x64", "jdk.internal.foreign.abi.x64.sysv", "jdk.internal.foreign.abi.x64.windows", + "jdk.internal.foreign.abi.aarch64", + "jdk.internal.foreign.abi.aarch64.macos", + "jdk.internal.foreign.abi.aarch64.linux", "jdk.internal.loader", "jdk.internal.reflect", ], "jdk.internal.vm.ci" : [ + "jdk.vm.ci.aarch64", "jdk.vm.ci.amd64", "jdk.vm.ci.code", "jdk.vm.ci.meta" @@ -800,9 +804,13 @@ "jdk.internal.foreign.abi", "jdk.internal.foreign.abi.x64.windows", "jdk.internal.foreign.abi.x64.sysv", + "jdk.internal.foreign.abi.aarch64", + "jdk.internal.foreign.abi.aarch64.macos", + "jdk.internal.foreign.abi.aarch64.linux", "jdk.internal.foreign.layout", ], "jdk.internal.vm.ci" : [ + "jdk.vm.ci.aarch64", "jdk.vm.ci.code", "jdk.vm.ci.meta", "jdk.vm.ci.amd64", @@ -2430,6 +2438,7 @@ "jdk.vm.ci.meta", "jdk.vm.ci.code", "jdk.vm.ci.amd64", + "jdk.vm.ci.aarch64", ], "java.base": [ "jdk.internal.foreign", @@ -2437,6 +2446,9 @@ "jdk.internal.foreign.abi.x64", "jdk.internal.foreign.abi.x64.sysv", "jdk.internal.foreign.abi.x64.windows", + "jdk.internal.foreign.abi.aarch64", + "jdk.internal.foreign.abi.aarch64.macos", + "jdk.internal.foreign.abi.aarch64.linux", ], }, }, diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/AbiUtils.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/AbiUtils.java index e9f8fd56e142..c0beb958c974 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/AbiUtils.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/AbiUtils.java @@ -25,6 +25,7 @@ package com.oracle.svm.core.foreign; import static com.oracle.svm.core.util.VMError.unsupportedFeature; +import static jdk.graal.compiler.asm.aarch64.AArch64Address.AddressingMode.IMMEDIATE_SIGNED_UNSCALED; import static jdk.vm.ci.amd64.AMD64.rax; import java.lang.foreign.FunctionDescriptor; @@ -49,6 +50,7 @@ import org.graalvm.word.Pointer; import com.oracle.svm.core.SubstrateTargetDescription; +import com.oracle.svm.core.aarch64.SubstrateAArch64MacroAssembler; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.foreign.AbiUtils.Adapter.Adaptation; import com.oracle.svm.core.graal.code.AssignedLocation; @@ -59,6 +61,9 @@ import com.oracle.svm.core.util.VMError; import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.asm.Label; +import jdk.graal.compiler.asm.aarch64.AArch64Address; +import jdk.graal.compiler.asm.aarch64.AArch64MacroAssembler; import jdk.graal.compiler.asm.amd64.AMD64Address; import jdk.graal.compiler.asm.amd64.AMD64Assembler; import jdk.graal.compiler.asm.amd64.AMD64BaseAssembler; @@ -75,8 +80,10 @@ import jdk.internal.foreign.abi.LinkerOptions; import jdk.internal.foreign.abi.NativeEntryPoint; import jdk.internal.foreign.abi.VMStorage; +import jdk.internal.foreign.abi.aarch64.AArch64Architecture; import jdk.internal.foreign.abi.x64.X86_64Architecture; import jdk.internal.foreign.abi.x64.sysv.CallArranger; +import jdk.vm.ci.aarch64.AArch64; import jdk.vm.ci.amd64.AMD64; import jdk.vm.ci.code.Register; import jdk.vm.ci.meta.JavaKind; @@ -472,6 +479,8 @@ public static AbiUtils create() { return switch (CABI.current()) { case SYS_V -> new ABIs.SysV(); case WIN_64 -> new ABIs.Win64(); + case MAC_OS_AARCH_64 -> new ABIs.MacOsAArch64(); + case LINUX_AARCH_64 -> new ABIs.LinuxAArch64(); default -> new ABIs.Unsupported(CABI.current().name()); }; } @@ -481,13 +490,55 @@ public static AbiUtils singleton() { return ImageSingletons.lookup(AbiUtils.class); } + protected abstract CallingSequence makeCallingSequence(MethodType type, FunctionDescriptor desc, boolean forUpcall, LinkerOptions options); + /** * This method re-implements a part of the logic from the JDK so that we can get the callee-type * (i.e. the ABI low-level type) of a function from its descriptor. */ - public abstract NativeEntryPointInfo makeNativeEntrypoint(FunctionDescriptor desc, Linker.Option... options); + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+11/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java#L99") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+11/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java#L71-L85") + public NativeEntryPointInfo makeNativeEntrypoint(FunctionDescriptor desc, Linker.Option... options) { + // From Linker.downcallHandle implemented in AbstractLinker.downcallHandle: + // From AbstractLinker.downcallHandle0 + LinkerOptions optionSet = LinkerOptions.forDowncall(desc, options); + MethodType type = desc.toMethodType(); + + // makeCallingSequence calls platform specific code + var callingSequence = makeCallingSequence(type, desc, false, optionSet); + + // From DowncallLinker.getBoundMethodHandle + var argMoveBindings = ABIs.Downcalls.argMoveBindingsStream(callingSequence).toArray(Binding.VMStore[]::new); + var argMoves = ABIs.Downcalls.toStorageArray(argMoveBindings); + var returnMoves = ABIs.Downcalls.toStorageArray(ABIs.Downcalls.retMoveBindings(callingSequence)); + var boundaryType = callingSequence.calleeMethodType(); + var needsReturnBuffer = callingSequence.needsReturnBuffer(); + + // From NativeEntrypoint.make + return NativeEntryPointInfo.make(argMoves, returnMoves, boundaryType, needsReturnBuffer, callingSequence.capturedStateMask(), callingSequence.needsTransition(), + optionSet.allowsHeapAccess()); + } - public abstract JavaEntryPointInfo makeJavaEntryPoint(FunctionDescriptor desc, Linker.Option... options); + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+11/src/java.base/share/classes/jdk/internal/foreign/abi/AbstractLinker.java#L124") + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+11/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java#L62-L110") + public JavaEntryPointInfo makeJavaEntryPoint(FunctionDescriptor desc, Linker.Option... options) { + // Linker.upcallStub implemented in AbstractLinker.upcallStub + MethodType type = desc.toMethodType(); + LinkerOptions optionSet = LinkerOptions.forUpcall(desc, options); + + // From CallArranger.arrangeUpcall + var callingSequence = makeCallingSequence(type, desc, true, optionSet); + + // From SharedUtil.arrangeUpcallHelper + // From UpcallLinker.makeFactory + Binding.VMLoad[] argMoves = ABIs.Upcalls.argMoveBindings(callingSequence); + Binding.VMStore[] retMoves = ABIs.Upcalls.retMoveBindings(callingSequence); + VMStorage[] args = Arrays.stream(argMoves).map(Binding.Move::storage).toArray(VMStorage[]::new); + VMStorage[] rets = Arrays.stream(retMoves).map(Binding.Move::storage).toArray(VMStorage[]::new); + Target_jdk_internal_foreign_abi_UpcallLinker_CallRegs cr = new Target_jdk_internal_foreign_abi_UpcallLinker_CallRegs(args, rets); + + return JavaEntryPointInfo.make(callingSequence.callerMethodType(), cr, callingSequence.needsReturnBuffer(), callingSequence.returnBufferSize()); + } /** * Generate a register allocation for SubstrateVM from the one generated by and for HotSpot. @@ -629,12 +680,7 @@ private String name() { } @Override - public NativeEntryPointInfo makeNativeEntrypoint(FunctionDescriptor desc, Linker.Option... options) { - return fail(); - } - - @Override - public JavaEntryPointInfo makeJavaEntryPoint(FunctionDescriptor desc, Linker.Option... options) { + protected CallingSequence makeCallingSequence(MethodType type, FunctionDescriptor desc, boolean forUpcall, LinkerOptions options) { return fail(); } @@ -674,97 +720,169 @@ public TrampolineTemplate generateTrampolineTemplate() { } } - @BasedOnJDKClass(X86_64Architecture.class) + @BasedOnJDKClass(AArch64Architecture.class) @BasedOnJDKClass(jdk.internal.foreign.abi.DowncallLinker.class) @BasedOnJDKClass(jdk.internal.foreign.abi.UpcallLinker.class) - abstract static class X86_64 extends AbiUtils { - @BasedOnJDKClass(jdk.internal.foreign.abi.DowncallLinker.class) - static class Downcalls { - protected static Stream argMoveBindingsStream(CallingSequence callingSequence) { - return callingSequence.argumentBindings() - .filter(Binding.VMStore.class::isInstance) - .map(Binding.VMStore.class::cast); - } + abstract static class ARM64 extends AbiUtils { + @Override + public Registers upcallSpecialArgumentsRegisters() { + return new Registers(SubstrateAArch64MacroAssembler.scratch1, SubstrateAArch64MacroAssembler.scratch2); + } - protected static Stream retMoveBindingsStream(CallingSequence callingSequence) { - return callingSequence.returnBindings().stream() - .filter(Binding.VMLoad.class::isInstance) - .map(Binding.VMLoad.class::cast); + @Override + public AssignedLocation[] toMemoryAssignment(VMStorage[] argMoves, boolean forReturn) { + AssignedLocation[] storages = new AssignedLocation[argMoves.length]; + int i = 0; + for (VMStorage move : argMoves) { + if (move == null) { + storages[i++] = AssignedLocation.placeholder(); + continue; + } + storages[i++] = switch (move.type()) { + case AArch64Architecture.StorageType.PLACEHOLDER -> AssignedLocation.placeholder(); + case AArch64Architecture.StorageType.INTEGER -> { + Register reg = AArch64.cpuRegisters.get(move.indexOrOffset()); + assert reg.name.equals(move.debugName()); + assert reg.getRegisterCategory().equals(AArch64.CPU); + yield AssignedLocation.forRegister(reg, JavaKind.Long); + } + case AArch64Architecture.StorageType.VECTOR -> { + Register reg = AArch64.simdRegisters.get(move.indexOrOffset()); + assert reg.name.equals(move.debugName()); + assert reg.getRegisterCategory().equals(AArch64.SIMD); + yield AssignedLocation.forRegister(reg, JavaKind.Double); + } + case AArch64Architecture.StorageType.STACK -> AssignedLocation.forStack(move.indexOrOffset()); + default -> throw unsupportedFeature("Unhandled VMStorage: " + move); + }; } + assert i == storages.length; - protected static Binding.VMLoad[] retMoveBindings(CallingSequence callingSequence) { - return retMoveBindingsStream(callingSequence).toArray(Binding.VMLoad[]::new); - } + return storages; + } - static VMStorage[] toStorageArray(Binding.Move[] moves) { - return Arrays.stream(moves).map(Binding.Move::storage).toArray(VMStorage[]::new); - } + @Override + public Map canonicalLayouts() { + return Map.ofEntries( + // specified canonical layouts + Map.entry("bool", ValueLayout.JAVA_BOOLEAN), + Map.entry("char", ValueLayout.JAVA_BYTE), + Map.entry("short", ValueLayout.JAVA_SHORT), + Map.entry("int", ValueLayout.JAVA_INT), + Map.entry("float", ValueLayout.JAVA_FLOAT), + Map.entry("long", (ValueLayout) ValueLayout.JAVA_LONG), + Map.entry("long long", ValueLayout.JAVA_LONG), + Map.entry("double", ValueLayout.JAVA_DOUBLE), + Map.entry("void*", ValueLayout.ADDRESS), + Map.entry("size_t", (ValueLayout) ValueLayout.JAVA_LONG), + Map.entry("wchar_t", (ValueLayout) ValueLayout.JAVA_INT), + // unspecified size-dependent layouts + Map.entry("int8_t", ValueLayout.JAVA_BYTE), + Map.entry("int16_t", ValueLayout.JAVA_SHORT), + Map.entry("int32_t", ValueLayout.JAVA_INT), + Map.entry("int64_t", ValueLayout.JAVA_LONG), + // unspecified JNI layouts + Map.entry("jboolean", ValueLayout.JAVA_BOOLEAN), + Map.entry("jchar", ValueLayout.JAVA_CHAR), + Map.entry("jbyte", ValueLayout.JAVA_BYTE), + Map.entry("jshort", ValueLayout.JAVA_SHORT), + Map.entry("jint", ValueLayout.JAVA_INT), + Map.entry("jlong", ValueLayout.JAVA_LONG), + Map.entry("jfloat", ValueLayout.JAVA_FLOAT), + Map.entry("jdouble", ValueLayout.JAVA_DOUBLE)); } - @BasedOnJDKClass(jdk.internal.foreign.abi.UpcallLinker.class) - static class Upcalls { - static Binding.VMLoad[] argMoveBindings(CallingSequence callingSequence) { - return callingSequence.argumentBindings() - .filter(Binding.VMLoad.class::isInstance) - .map(Binding.VMLoad.class::cast) - .toArray(Binding.VMLoad[]::new); - } + @Override + public int trampolineSize() { + return 64; + } - static Binding.VMStore[] retMoveBindings(CallingSequence callingSequence) { - return callingSequence.returnBindings().stream() - .filter(Binding.VMStore.class::isInstance) - .map(Binding.VMStore.class::cast) - .toArray(Binding.VMStore[]::new); - } + @Platforms(Platform.HOSTED_ONLY.class) + @Override + public TrampolineTemplate generateTrampolineTemplate() { + AArch64MacroAssembler masm = new SubstrateAArch64MacroAssembler(ConfigurationValues.getTarget()); + + Register mhRegister = upcallSpecialArgumentsRegisters().methodHandle(); + Register isolateRegister = upcallSpecialArgumentsRegisters().isolate(); + + Label loadIsolate = new Label(); + masm.jmp(loadIsolate); + int posIsolate = masm.position(); + masm.emitLong(0x1111_2222_3333_4444L); + int posMHArray = masm.position(); + masm.emitLong(0x5555_6666_7777_8888L); + int posCallTarget = masm.position(); + masm.emitLong(0x9999_aaaa_bbbb_ccccL); + + masm.bind(loadIsolate); + /* r10 contains the isolate address */ + masm.ldr(64, isolateRegister, AArch64Address.createPCLiteralAddress(64, posIsolate - masm.position())); + + masm.ldr(64, mhRegister, AArch64Address.createPCLiteralAddress(64, posMHArray - masm.position())); + /* r9 contains the method handle */ + masm.ldr(64, mhRegister, AArch64Address.createImmediateAddress(64, IMMEDIATE_SIGNED_UNSCALED, mhRegister, 0)); + + /* + * NOTE: do not use r8, it's part of the CallArranger ABI ("indirect result register"), + * also do not use scratch registers (r9/r10 on SVM). + */ + Register scratch = AArch64.r11; + assert !scratch.equals(mhRegister) && !scratch.equals(isolateRegister); + masm.ldr(64, scratch, AArch64Address.createPCLiteralAddress(64, posCallTarget - masm.position())); + /* deref it */ + masm.ldr(64, scratch, AArch64Address.createImmediateAddress(64, IMMEDIATE_SIGNED_UNSCALED, scratch, 0)); + + /* jump into the target */ + masm.jmp(scratch); + + assert trampolineSize() >= masm.position(); + masm.align(trampolineSize()); + + byte[] assembly = masm.close(true); + assert assembly.length == trampolineSize(); + + return new TrampolineTemplate(assembly, posIsolate, posMHArray, posCallTarget); } + } - protected abstract CallingSequence makeCallingSequence(MethodType type, FunctionDescriptor desc, boolean forUpcall, LinkerOptions options); + @BasedOnJDKClass(jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker.class) + @BasedOnJDKClass(jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64CallArranger.class) + static final class LinuxAArch64 extends ARM64 { @Override - public NativeEntryPointInfo makeNativeEntrypoint(FunctionDescriptor desc, Linker.Option... options) { - // From Linker.downcallHandle implemented in AbstractLinker.downcallHandle: - // From AbstractLinker.downcallHandle0 - LinkerOptions optionSet = LinkerOptions.forDowncall(desc, options); - MethodType type = desc.toMethodType(); - - // OS specific! - // From AbstractLinker.arrangeDowncall implemented in SysVx64Linker.arrangeDowncall - // or Windowsx64Linker.arrangeDowncall: - // From CallArranger.arrangeDowncall - var callingSequence = makeCallingSequence(type, desc, false, optionSet); - - // From DowncallLinker.getBoundMethodHandle - var argMoveBindings = Downcalls.argMoveBindingsStream(callingSequence).toArray(Binding.VMStore[]::new); - var argMoves = Downcalls.toStorageArray(argMoveBindings); - var returnMoves = Downcalls.toStorageArray(Downcalls.retMoveBindings(callingSequence)); - var boundaryType = callingSequence.calleeMethodType(); - var needsReturnBuffer = callingSequence.needsReturnBuffer(); - - // From NativeEntrypoint.make - return NativeEntryPointInfo.make(argMoves, returnMoves, boundaryType, needsReturnBuffer, callingSequence.capturedStateMask(), callingSequence.needsTransition(), - optionSet.allowsHeapAccess()); + protected CallingSequence makeCallingSequence(MethodType type, FunctionDescriptor desc, boolean forUpcall, LinkerOptions options) { + return jdk.internal.foreign.abi.aarch64.CallArranger.LINUX.getBindings(type, desc, forUpcall, options).callingSequence(); } @Override - public JavaEntryPointInfo makeJavaEntryPoint(FunctionDescriptor desc, Linker.Option... options) { - // Linker.upcallStub implemented in - // AbstractLinker.upcallStub - MethodType type = desc.toMethodType(); + public void checkLibrarySupport() { + String name = "Linux AArch64"; + VMError.guarantee(LibC.isSupported(), "Foreign functions feature requires LibC support on %s", name); + } + } - // From CallArranger.arrangeUpcall - LinkerOptions optionSet = LinkerOptions.forUpcall(desc, options); - var callingSequence = makeCallingSequence(type, desc, true, optionSet); + @BasedOnJDKClass(jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker.class) + @BasedOnJDKClass(jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64CallArranger.class) + static final class MacOsAArch64 extends ARM64 { - // From SharedUtil.arrangeUpcallHelper + @Override + protected CallingSequence makeCallingSequence(MethodType type, FunctionDescriptor desc, boolean forUpcall, LinkerOptions options) { + return jdk.internal.foreign.abi.aarch64.CallArranger.MACOS.getBindings(type, desc, forUpcall, options).callingSequence(); + } - // From UpcallLinker.makeFactory - Binding.VMLoad[] argMoves = Upcalls.argMoveBindings(callingSequence); - Binding.VMStore[] retMoves = Upcalls.retMoveBindings(callingSequence); - VMStorage[] args = Arrays.stream(argMoves).map(Binding.Move::storage).toArray(VMStorage[]::new); - VMStorage[] rets = Arrays.stream(retMoves).map(Binding.Move::storage).toArray(VMStorage[]::new); - Target_jdk_internal_foreign_abi_UpcallLinker_CallRegs cr = new Target_jdk_internal_foreign_abi_UpcallLinker_CallRegs(args, rets); - return JavaEntryPointInfo.make(callingSequence.callerMethodType(), cr, callingSequence.needsReturnBuffer(), callingSequence.returnBufferSize()); + @Override + public void checkLibrarySupport() { + String name = "Darwin AArch64"; + VMError.guarantee(LibC.isSupported(), "Foreign functions feature requires LibC support on %s", name); } + } + + @BasedOnJDKClass(X86_64Architecture.class) + @BasedOnJDKClass(jdk.internal.foreign.abi.DowncallLinker.class) + @BasedOnJDKClass(jdk.internal.foreign.abi.UpcallLinker.class) + abstract static class X86_64 extends AbiUtils { + + protected abstract CallingSequence makeCallingSequence(MethodType type, FunctionDescriptor desc, boolean forUpcall, LinkerOptions options); @Override public AssignedLocation[] toMemoryAssignment(VMStorage[] argMoves, boolean forReturn) { @@ -999,4 +1117,44 @@ public Map canonicalLayouts() { return canonicalLayouts(ValueLayout.JAVA_INT, ValueLayout.JAVA_LONG, ValueLayout.JAVA_CHAR); } } + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+11/src/java.base/share/classes/jdk/internal/foreign/abi/DowncallLinker.java#L122-L140") + static class Downcalls { + protected static Stream argMoveBindingsStream(CallingSequence callingSequence) { + return callingSequence.argumentBindings() + .filter(Binding.VMStore.class::isInstance) + .map(Binding.VMStore.class::cast); + } + + protected static Stream retMoveBindingsStream(CallingSequence callingSequence) { + return callingSequence.returnBindings().stream() + .filter(Binding.VMLoad.class::isInstance) + .map(Binding.VMLoad.class::cast); + } + + protected static Binding.VMLoad[] retMoveBindings(CallingSequence callingSequence) { + return retMoveBindingsStream(callingSequence).toArray(Binding.VMLoad[]::new); + } + + static VMStorage[] toStorageArray(Binding.Move[] moves) { + return Arrays.stream(moves).map(Binding.Move::storage).toArray(VMStorage[]::new); + } + } + + @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+11/src/java.base/share/classes/jdk/internal/foreign/abi/UpcallLinker.java#L124-L134") + static class Upcalls { + static Binding.VMLoad[] argMoveBindings(CallingSequence callingSequence) { + return callingSequence.argumentBindings() + .filter(Binding.VMLoad.class::isInstance) + .map(Binding.VMLoad.class::cast) + .toArray(Binding.VMLoad[]::new); + } + + static Binding.VMStore[] retMoveBindings(CallingSequence callingSequence) { + return callingSequence.returnBindings().stream() + .filter(Binding.VMStore.class::isInstance) + .map(Binding.VMStore.class::cast) + .toArray(Binding.VMStore[]::new); + } + } } diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/RuntimeSystemLookup.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/RuntimeSystemLookup.java index 93a9c401a44e..44359df6f9ae 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/RuntimeSystemLookup.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/RuntimeSystemLookup.java @@ -93,6 +93,8 @@ public static SymbolLookup makeSystemLookup() { } return lookup; + } else if (OS.DARWIN.isCurrent()) { + return Util_java_lang_foreign_SymbolLookup.libraryLookup(LookupNativeLibraries::loadLibraryPlatformSpecific, List.of("/usr/lib/libSystem.B.dylib")); } else { /* * This list of libraries was obtained by examining the dependencies of libsystemlookup, diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_abi_AbstractLinker.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_abi_AbstractLinker.java index 2505a994d9ce..a9314e915c54 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_abi_AbstractLinker.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_abi_AbstractLinker.java @@ -41,6 +41,8 @@ import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.AbstractLinker.UpcallStubFactory; import jdk.internal.foreign.abi.LinkerOptions; +import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker; +import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker; import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker; import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker; @@ -108,7 +110,20 @@ UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor functi } } -/* - * GR-58659, GR-58660: add substitutions for LinuxAArch64Linker and MacOsAArch64Linker here once we - * support them. - */ +@TargetClass(value = MacOsAArch64Linker.class, onlyWith = ForeignFunctionsEnabled.class) +final class Target_jdk_internal_foreign_abi_aarch64_macos_MacOsAArch64Linker { + + @Substitute + UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + return new UpcallStubFactoryDecorator(jdk.internal.foreign.abi.aarch64.CallArranger.MACOS.arrangeUpcall(targetType, function, options)); + } +} + +@TargetClass(value = LinuxAArch64Linker.class, onlyWith = ForeignFunctionsEnabled.class) +final class Target_jdk_internal_foreign_abi_aarch64_linux_LinuxAArch64Linker { + + @Substitute + UpcallStubFactory arrangeUpcall(MethodType targetType, FunctionDescriptor function, LinkerOptions options) { + return new UpcallStubFactoryDecorator(jdk.internal.foreign.abi.aarch64.CallArranger.LINUX.arrangeUpcall(targetType, function, options)); + } +} diff --git a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java index 084edb225468..bfa0253b2a38 100755 --- a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java +++ b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64Backend.java @@ -28,6 +28,7 @@ import static com.oracle.svm.core.graal.code.SubstrateBackend.SubstrateMarkId.PROLOGUE_DECD_RSP; import static com.oracle.svm.core.graal.code.SubstrateBackend.SubstrateMarkId.PROLOGUE_END; import static com.oracle.svm.core.util.VMError.shouldNotReachHere; +import static com.oracle.svm.core.util.VMError.unsupportedFeature; import static jdk.graal.compiler.core.common.GraalOptions.ZapStackOnMethodEntry; import static jdk.graal.compiler.lir.LIRInstruction.OperandFlag.REG; import static jdk.graal.compiler.lir.LIRValueUtil.asConstantValue; @@ -36,18 +37,20 @@ import static jdk.vm.ci.aarch64.AArch64.sp; import static jdk.vm.ci.code.ValueUtil.asRegister; +import java.util.Arrays; +import java.util.Collection; import java.util.function.BiConsumer; -import com.oracle.svm.core.interpreter.InterpreterSupport; -import com.oracle.svm.core.aarch64.SubstrateAArch64MacroAssembler; import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.FrameAccess; import com.oracle.svm.core.ReservedRegisters; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.aarch64.SubstrateAArch64MacroAssembler; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.deopt.Deoptimizer; +import com.oracle.svm.core.graal.code.AssignedLocation; import com.oracle.svm.core.graal.code.PatchConsumerFactory; import com.oracle.svm.core.graal.code.SubstrateBackend; import com.oracle.svm.core.graal.code.SubstrateCallingConvention; @@ -68,6 +71,7 @@ import com.oracle.svm.core.heap.ReferenceAccess; import com.oracle.svm.core.heap.SubstrateReferenceMapBuilder; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.interpreter.InterpreterSupport; import com.oracle.svm.core.meta.CompressedNullConstant; import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.core.meta.SharedMethod; @@ -243,9 +247,11 @@ public static class SubstrateAArch64IndirectCallOp extends AArch64Call.IndirectC @Temp({REG}) private Value linkReg; private final BiConsumer offsetRecorder; + @Def({REG}) private Value[] multipleResults; + public SubstrateAArch64IndirectCallOp(ResolvedJavaMethod callTarget, Value result, Value[] parameters, Value[] temps, Value targetAddress, LIRFrameState state, Value javaFrameAnchor, int newThreadStatus, boolean destroysCallerSavedRegisters, Value exceptionTemp, - BiConsumer offsetRecorder) { + BiConsumer offsetRecorder, Value[] multipleResults) { super(TYPE, callTarget, result, parameters, temps, targetAddress, state); this.javaFrameAnchor = javaFrameAnchor; this.newThreadStatus = newThreadStatus; @@ -253,6 +259,7 @@ public SubstrateAArch64IndirectCallOp(ResolvedJavaMethod callTarget, Value resul this.exceptionTemp = exceptionTemp; this.linkReg = lr.asValue(LIRKind.value(AArch64Kind.QWORD)); this.offsetRecorder = offsetRecorder; + this.multipleResults = multipleResults; } @Override @@ -561,8 +568,9 @@ protected void emitForeignCallOp(ForeignCallLinkage linkage, Value targetAddress if (shouldEmitOnlyIndirectCalls()) { RegisterValue targetRegister = AArch64.lr.asValue(FrameAccess.getWordStamp().getLIRKind(getLIRKindTool())); emitMove(targetRegister, targetAddress); + Value[] multipleResults = new Value[0]; append(new SubstrateAArch64IndirectCallOp(targetMethod, result, arguments, temps, targetRegister, info, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, - getDestroysCallerSavedRegisters(targetMethod), exceptionTemp, null)); + getDestroysCallerSavedRegisters(targetMethod), exceptionTemp, null, multipleResults)); } else { assert targetAddress == null; append(new SubstrateAArch64DirectCallOp(targetMethod, result, arguments, temps, info, Value.ILLEGAL, StatusSupport.STATUS_ILLEGAL, @@ -714,6 +722,25 @@ protected DebugInfoBuilder createDebugInfoBuilder(StructuredGraph graph, NodeVal return new SubstrateDebugInfoBuilder(graph, gen.getProviders().getMetaAccessExtensionProvider(), nodeValueMap); } + @Override + public Value[] visitInvokeArguments(CallingConvention invokeCc, Collection arguments) { + Value[] values = super.visitInvokeArguments(invokeCc, arguments); + SubstrateCallingConventionType type = (SubstrateCallingConventionType) ((SubstrateCallingConvention) invokeCc).getType(); + + if (type.usesReturnBuffer()) { + /* + * We save the return buffer so that it can be accessed after the call. + */ + assert values.length > 0; + Value returnBuffer = values[0]; + Variable saved = gen.newVariable(returnBuffer.getValueKind()); + gen.append(gen.getSpillMoveFactory().createMove(saved, returnBuffer)); + values[0] = saved; + } + + return values; + } + private boolean getDestroysCallerSavedRegisters(ResolvedJavaMethod targetMethod) { return ((SubstrateAArch64LIRGenerator) gen).getDestroysCallerSavedRegisters(targetMethod); } @@ -761,14 +788,44 @@ public BiConsumer getOffsetRecorder(@Suppress return null; } + private static AllocatableValue asReturnedValue(AssignedLocation assignedLocation) { + assert assignedLocation.assignsToRegister(); + Register.RegisterCategory category = assignedLocation.register().getRegisterCategory(); + LIRKind kind; + if (category.equals(AArch64.CPU)) { + kind = LIRKind.value(AArch64Kind.QWORD); + } else if (category.equals(AArch64.SIMD)) { + kind = LIRKind.value(AArch64Kind.V128_QWORD); + } else { + throw unsupportedFeature("Register category " + category + " should not be used for returns spanning multiple registers."); + } + return assignedLocation.register().asValue(kind); + } + @Override protected void emitInvoke(LoweredCallTargetNode callTarget, Value[] parameters, LIRFrameState callState, Value result) { + var cc = (SubstrateCallingConventionType) callTarget.callType(); verifyCallTarget(callTarget); if (callTarget instanceof ComputedIndirectCallTargetNode) { + assert !cc.customABI(); emitComputedIndirectCall((ComputedIndirectCallTargetNode) callTarget, result, parameters, AllocatableValue.NONE, callState); } else { super.emitInvoke(callTarget, parameters, callState, result); } + if (cc.usesReturnBuffer()) { + /* + * The buffer argument was saved in visitInvokeArguments, so that the value was not + * killed by the call. + */ + Value returnBuffer = parameters[0]; + long offset = 0; + for (AssignedLocation ret : cc.returnSaving) { + Value saveLocation = gen.getArithmetic().emitAdd(returnBuffer, gen.emitJavaConstant(JavaConstant.forLong(offset)), false); + AllocatableValue returnedValue = asReturnedValue(ret); + gen.getArithmetic().emitStore(returnedValue.getValueKind(), saveLocation, returnedValue, callState, MemoryOrderMode.PLAIN); + offset += returnedValue.getPlatformKind().getSizeInBytes(); + } + } } @Override @@ -785,8 +842,17 @@ protected void emitIndirectCall(IndirectCallTargetNode callTarget, Value result, AllocatableValue targetAddress = targetRegister.asValue(FrameAccess.getWordStamp().getLIRKind(getLIRGeneratorTool().getLIRKindTool())); gen.emitMove(targetAddress, operand(callTarget.computedAddress())); ResolvedJavaMethod targetMethod = callTarget.targetMethod(); + SubstrateCallingConventionType cc = (SubstrateCallingConventionType) callTarget.callType(); + + Value[] multipleResults = new Value[0]; + if (cc.customABI() && cc.usesReturnBuffer()) { + multipleResults = Arrays.stream(cc.returnSaving) + .map(SubstrateAArch64NodeLIRBuilder::asReturnedValue) + .toList().toArray(new Value[0]); + } + append(new SubstrateAArch64IndirectCallOp(targetMethod, result, parameters, temps, targetAddress, callState, setupJavaFrameAnchor(callTarget), - getNewThreadStatus(callTarget), getDestroysCallerSavedRegisters(targetMethod), getExceptionTemp(callTarget), getOffsetRecorder(callTarget))); + getNewThreadStatus(callTarget), getDestroysCallerSavedRegisters(targetMethod), getExceptionTemp(callTarget), getOffsetRecorder(callTarget), multipleResults)); } protected void emitComputedIndirectCall(ComputedIndirectCallTargetNode callTarget, Value result, Value[] parameters, Value[] temps, LIRFrameState callState) { @@ -1380,7 +1446,9 @@ protected void resetForEmittingCode(CompilationResultBuilder crb) { @Override public LIRGenerationResult newLIRGenerationResult(CompilationIdentifier compilationId, LIR lir, RegisterAllocationConfig registerAllocationConfig, StructuredGraph graph, Object stub) { SharedMethod method = (SharedMethod) graph.method(); - CallingConvention callingConvention = CodeUtil.getCallingConvention(getCodeCache(), method.getCallingConventionKind().toType(false), method, this); + SubstrateCallingConventionKind ccKind = method.getCallingConventionKind(); + SubstrateCallingConventionType ccType = ccKind.isCustom() ? method.getCustomCallingConventionType() : ccKind.toType(false); + CallingConvention callingConvention = CodeUtil.getCallingConvention(getCodeCache(), ccType, method, this); LIRGenerationResult lirGenerationResult = new SubstrateLIRGenerationResult(compilationId, lir, newFrameMapBuilder(registerAllocationConfig.getRegisterConfig()), registerAllocationConfig, callingConvention, method); diff --git a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64RegisterConfig.java b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64RegisterConfig.java index 5e83ccb274fe..b5f71341bd92 100755 --- a/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64RegisterConfig.java +++ b/substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/SubstrateAArch64RegisterConfig.java @@ -67,12 +67,15 @@ import static jdk.vm.ci.aarch64.AArch64.zr; import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; -import com.oracle.svm.core.aarch64.SubstrateAArch64MacroAssembler; import org.graalvm.nativeimage.Platform; import com.oracle.svm.core.ReservedRegisters; +import com.oracle.svm.core.aarch64.SubstrateAArch64MacroAssembler; import com.oracle.svm.core.config.ObjectLayout; +import com.oracle.svm.core.graal.code.AssignedLocation; import com.oracle.svm.core.graal.code.SubstrateCallingConvention; import com.oracle.svm.core.graal.code.SubstrateCallingConventionKind; import com.oracle.svm.core.graal.code.SubstrateCallingConventionType; @@ -278,16 +281,40 @@ private static int darwinNativeStackParameterAssignment(ValueKindFactory valu @Override public CallingConvention getCallingConvention(Type t, JavaType returnType, JavaType[] parameterTypes, ValueKindFactory valueKindFactory) { SubstrateCallingConventionType type = (SubstrateCallingConventionType) t; - if (type.fixedParameterAssignment != null || type.returnSaving != null) { - throw unsupportedFeature("Fixed parameter assignments and return saving are not yet supported on this platform."); - } - boolean isEntryPoint = type.nativeABI() && !type.outgoing; AllocatableValue[] locations = new AllocatableValue[parameterTypes.length]; + JavaKind[] kinds = new JavaKind[locations.length]; - int currentGeneral = 0; - int currentFP = 0; + int firstActualArgument = 0; + if (type.usesReturnBuffer()) { + VMError.guarantee(type.fixedParameterAssignment != null); + VMError.guarantee(type.fixedParameterAssignment[0].isPlaceholder()); + /* + * returnSaving implies an additional (prefix) parameter pointing to the buffer to use + * for saving. This argument is not actually used by the function, so it will be ignored + * in the remainder of this method. + */ + firstActualArgument = 1; + /* + * Ideally, we would just pretend this argument never existed and would not give it a + * location. In practice, it is not so simple, as the generated calling convention is + * expected to match the arguments, and not just ignore one of them. It might be + * possible to implement this using some kind of "SinkValue" as the location of the + * argument. In the meantime, we put it in r8. + */ + JavaKind kind = ObjectLayout.getCallSignatureKind(isEntryPoint, parameterTypes[0], metaAccess, target); + kinds[0] = kind; + ValueKind paramValueKind = valueKindFactory.getValueKind(isEntryPoint ? kind : kind.getStackKind()); + locations[0] = AArch64.r8.asValue(paramValueKind); + + for (int i = 1; i < type.fixedParameterAssignment.length; i++) { + AssignedLocation storage = type.fixedParameterAssignment[i]; + if (storage.assignsToRegister()) { + assert !storage.register().equals(AArch64.r8); + } + } + } /* * We have to reserve a slot between return address and outgoing parameters for the @@ -295,64 +322,96 @@ public CallingConvention getCallingConvention(Type t, JavaType returnType, JavaT * deoptimization). Exception: calls to native methods. */ int currentStackOffset = (type.nativeABI() ? nativeParamsStackOffset : target.wordSize); - - JavaKind[] kinds = new JavaKind[locations.length]; boolean isDarwinPlatform = Platform.includedIn(Platform.DARWIN.class); - for (int i = 0; i < parameterTypes.length; i++) { - JavaKind kind = ObjectLayout.getCallSignatureKind(isEntryPoint, parameterTypes[i], metaAccess, target); - kinds[i] = kind; - - Register register = null; - if (type.kind == SubstrateCallingConventionKind.ForwardReturnValue) { - VMError.guarantee(i == 0, "Method with calling convention ForwardReturnValue cannot have more than one parameter"); - register = getReturnRegister(kind); - } else { - switch (kind) { - case Byte: - case Boolean: - case Short: - case Char: - case Int: - case Long: - case Object: - if (currentGeneral < generalParameterRegs.size()) { - register = generalParameterRegs.get(currentGeneral++); - } - break; - case Float: - case Double: - if (currentFP < fpParameterRegs.size()) { - register = fpParameterRegs.get(currentFP++); + + if (!type.customABI()) { + int currentGeneral = 0; + int currentFP = 0; + + for (int i = 0; i < parameterTypes.length; i++) { + JavaKind kind = ObjectLayout.getCallSignatureKind(isEntryPoint, parameterTypes[i], metaAccess, target); + kinds[i] = kind; + + Register register = null; + if (type.kind == SubstrateCallingConventionKind.ForwardReturnValue) { + VMError.guarantee(i == 0, "Method with calling convention ForwardReturnValue cannot have more than one parameter"); + register = getReturnRegister(kind); + } else { + switch (kind) { + case Byte: + case Boolean: + case Short: + case Char: + case Int: + case Long: + case Object: + if (currentGeneral < generalParameterRegs.size()) { + register = generalParameterRegs.get(currentGeneral++); + } + break; + case Float: + case Double: + if (currentFP < fpParameterRegs.size()) { + register = fpParameterRegs.get(currentFP++); + } + break; + default: + throw shouldNotReachHereUnexpectedInput(kind); // ExcludeFromJacocoGeneratedReport + } + + } + if (register != null) { + /* + * The AArch64 procedure call standard does not require subword (i.e., boolean, + * byte, char, short) values to be extended to 32 bits. Hence, for incoming + * native calls, we can only assume the bits sizes as specified in the standard. + * + * Since within the graal compiler subwords are already extended to 32 bits, we + * save extended values in outgoing calls. + * + * Darwin deviates from the call standard and requires the caller to extend + * subword values. + */ + boolean useJavaKind = isEntryPoint && !isDarwinPlatform; + locations[i] = register.asValue(valueKindFactory.getValueKind(useJavaKind ? kind : kind.getStackKind())); + } else { + if (type.nativeABI()) { + if (isDarwinPlatform) { + currentStackOffset = darwinNativeStackParameterAssignment(valueKindFactory, locations, i, kind, currentStackOffset, type.outgoing); + } else { + currentStackOffset = linuxNativeStackParameterAssignment(valueKindFactory, locations, i, kind, currentStackOffset, type.outgoing); } - break; - default: - throw shouldNotReachHereUnexpectedInput(kind); // ExcludeFromJacocoGeneratedReport + } else { + currentStackOffset = javaStackParameterAssignment(valueKindFactory, locations, i, kind, currentStackOffset, type.outgoing); + } } - } - if (register != null) { - /* - * The AArch64 procedure call standard does not require subword (i.e., boolean, - * byte, char, short) values to be extended to 32 bits. Hence, for incoming native - * calls, we can only assume the bits sizes as specified in the standard. - * - * Since within the graal compiler subwords are already extended to 32 bits, we save - * extended values in outgoing calls. - * - * Darwin deviates from the call standard and requires the caller to extend subword - * values. - */ - boolean useJavaKind = isEntryPoint && !isDarwinPlatform; - locations[i] = register.asValue(valueKindFactory.getValueKind(useJavaKind ? kind : kind.getStackKind())); - } else { - if (type.nativeABI()) { - if (isDarwinPlatform) { - currentStackOffset = darwinNativeStackParameterAssignment(valueKindFactory, locations, i, kind, currentStackOffset, type.outgoing); - } else { - currentStackOffset = linuxNativeStackParameterAssignment(valueKindFactory, locations, i, kind, currentStackOffset, type.outgoing); + } else { + Set usedRegisters = new HashSet<>(); + VMError.guarantee(parameterTypes.length == type.fixedParameterAssignment.length, "Parameters/assignments size mismatch."); + + for (int i = firstActualArgument; i < locations.length; i++) { + JavaKind kind = ObjectLayout.getCallSignatureKind(isEntryPoint, parameterTypes[i], metaAccess, target); + kinds[i] = kind; + + ValueKind paramValueKind = valueKindFactory.getValueKind(isEntryPoint ? kind : kind.getStackKind()); + + AssignedLocation storage = type.fixedParameterAssignment[i]; + if (storage.assignsToRegister()) { + if (!kind.isNumericInteger() && !kind.isNumericFloat()) { + throw unsupportedFeature("Unsupported storage/kind pair - Storage: " + storage + " ; Kind: " + kind); } + Register reg = storage.register(); + VMError.guarantee(target.arch.canStoreValue(reg.getRegisterCategory(), paramValueKind.getPlatformKind()), "Cannot assign value to register."); + locations[i] = reg.asValue(paramValueKind); + VMError.guarantee(!usedRegisters.contains(reg), "Register was already used."); + usedRegisters.add(reg); + } else if (storage.assignsToStack()) { + assert currentStackOffset <= storage.stackOffset() : "currentStackOffset=" + currentStackOffset + ", stackOffset=" + storage.stackOffset(); + locations[i] = StackSlot.get(valueKindFactory.getValueKind(kind), storage.stackOffset(), !type.outgoing); + currentStackOffset = storage.stackOffset(); } else { - currentStackOffset = javaStackParameterAssignment(valueKindFactory, locations, i, kind, currentStackOffset, type.outgoing); + throw VMError.shouldNotReachHere("Placeholder assignment."); } } } diff --git a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java index bfe159f45ac0..442e971d5e4e 100644 --- a/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java +++ b/substratevm/src/com.oracle.svm.hosted.foreign/src/com/oracle/svm/hosted/foreign/ForeignFunctionsFeature.java @@ -60,6 +60,7 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.LinkToNativeSupport; +import com.oracle.svm.core.OS; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.configure.ConfigurationFile; @@ -97,10 +98,13 @@ @Platforms(Platform.HOSTED_ONLY.class) public class ForeignFunctionsFeature implements InternalFeature { private static final Map REQUIRES_CONCEALED = Map.of( - "jdk.internal.vm.ci", new String[]{"jdk.vm.ci.code", "jdk.vm.ci.meta", "jdk.vm.ci.amd64"}, + "jdk.internal.vm.ci", new String[]{"jdk.vm.ci.code", "jdk.vm.ci.meta", "jdk.vm.ci.amd64", "jdk.vm.ci.aarch64"}, "java.base", new String[]{ "jdk.internal.foreign", "jdk.internal.foreign.abi", + "jdk.internal.foreign.abi.aarch64", + "jdk.internal.foreign.abi.aarch64.macos", + "jdk.internal.foreign.abi.aarch64.linux", "jdk.internal.foreign.abi.x64", "jdk.internal.foreign.abi.x64.sysv", "jdk.internal.foreign.abi.x64.windows"}); @@ -218,7 +222,12 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { return false; } UserError.guarantee(JavaVersionUtil.JAVA_SPEC >= 22, "Support for the Foreign Function and Memory API is available only with JDK 22 and later."); - UserError.guarantee(SubstrateUtil.getArchitectureName().contains("amd64"), "Support for the Foreign Function and Memory API is currently available only on the AMD64 architecture."); + boolean isLinuxAmd64 = OS.LINUX.isCurrent() && SubstrateUtil.getArchitectureName().contains("amd64"); + boolean isWindowsAmd64 = OS.WINDOWS.isCurrent() && SubstrateUtil.getArchitectureName().contains("amd64"); + boolean isDarwinAArch64 = OS.DARWIN.isCurrent() && SubstrateUtil.getArchitectureName().contains("aarch64"); + boolean isLinuxAArch64 = OS.LINUX.isCurrent() && SubstrateUtil.getArchitectureName().contains("aarch64"); + UserError.guarantee(isLinuxAmd64 || isWindowsAmd64 || isDarwinAArch64 || isLinuxAArch64, + "Support for the Foreign Function and Memory API is currently available on Linux AMD64, Windows AMD64, Darwin AArch64 or Linux AArch64."); UserError.guarantee(!SubstrateOptions.useLLVMBackend(), "Support for the Foreign Function and Memory API is not available with the LLVM backend."); return true; }