From 2059d283fc16a33c1091539a14538cef746d0f8e Mon Sep 17 00:00:00 2001 From: Florian Angerer Date: Wed, 4 Jun 2025 10:50:41 +0200 Subject: [PATCH] Enable FFM API by default --- .../mx_substratevm_benchmark.py | 3 +- .../com/oracle/svm/core/foreign/AbiUtils.java | 9 ++++ .../core/foreign/ForeignAPIPredicates.java | 41 +++++++++++++++++-- .../core/foreign/ForeignFunctionsRuntime.java | 2 +- .../svm/core/foreign/RuntimeSystemLookup.java | 8 ++-- ...get_jdk_internal_foreign_SystemLookup.java | 15 +++++++ ..._jdk_internal_misc_ScopedMemoryAccess.java | 27 ++++++++---- .../com/oracle/svm/core/ForeignSupport.java | 4 +- .../com/oracle/svm/core/SubstrateOptions.java | 27 +++++++++++- .../jdk/ForeignDisabledSubstitutions.java | 6 +-- ...t_jdk_internal_loader_NativeLibraries.java | 16 +++++++- .../foreign/ForeignFunctionsFeature.java | 35 +++++++++++++--- .../InlineBeforeAnalysisPolicyUtils.java | 2 +- .../phases/SharedGraphBuilderPhase.java | 17 ++++---- .../SubstrateGraphBuilderPlugins.java | 28 +++++++------ 15 files changed, 189 insertions(+), 51 deletions(-) diff --git a/substratevm/mx.substratevm/mx_substratevm_benchmark.py b/substratevm/mx.substratevm/mx_substratevm_benchmark.py index b5670f79dffb..00659e946c58 100644 --- a/substratevm/mx.substratevm/mx_substratevm_benchmark.py +++ b/substratevm/mx.substratevm/mx_substratevm_benchmark.py @@ -936,8 +936,7 @@ def _empty_file(): # 2. Native-image picks a different service provider than the JVM for javax.xml.transform.TransformerFactory. # We can simply remove the jar containing that provider as it is not required for the benchmark to run. 'fop': [f"-Djava.util.logging.config.file={_empty_file()}", - '--initialize-at-run-time=org.apache.fop.render.rtf.rtflib.rtfdoc.RtfList', - '-H:+ForeignAPISupport'], + '--initialize-at-run-time=org.apache.fop.render.rtf.rtflib.rtfdoc.RtfList'], 'batik': [] } 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 58b9a1382d98..b437aae7da13 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 @@ -118,7 +118,9 @@ public enum Extracted { CaptureBufferAddress } + @Platforms(Platform.HOSTED_ONLY.class) public static class Result { + @Platforms(Platform.HOSTED_ONLY.class) public record FullNativeAdaptation( Map extractedArguments, List arguments, @@ -131,10 +133,12 @@ public ValueNode getArgument(Extracted id) { } } + @Platforms(Platform.HOSTED_ONLY.class) public record TypeAdaptation(List parametersAssignment, MethodType callType) { } } + @Platforms(Platform.HOSTED_ONLY.class) public static Result.FullNativeAdaptation adaptToNative(AbiUtils self, List adaptations, List originalArguments, NativeEntryPointInfo nep) { List originalUnmodifiableArguments = Collections.unmodifiableList(originalArguments); @@ -175,6 +179,7 @@ public static Result.FullNativeAdaptation adaptToNative(AbiUtils self, List adaptations, JavaEntryPointInfo jep) { AssignedLocation[] originalAssignment = self.toMemoryAssignment(jep.parametersAssignment(), false); @@ -622,6 +627,7 @@ protected List generateAdaptations(NativeEntryPointInfo nep) } @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+27/src/java.base/share/classes/jdk/internal/foreign/abi/x64/sysv/CallArranger.java#L280-L290") + @Platforms(Platform.HOSTED_ONLY.class) private static void handleCriticalWithHeapAccess(NativeEntryPointInfo nep, int i, List adaptations, Adaptation adaptation) { VMError.guarantee(nep.allowHeapAccess(), "A storage may only be null when the Linker.Option.critical(true) option is passed."); VMError.guarantee( @@ -709,6 +715,7 @@ public AssignedLocation[] toMemoryAssignment(VMStorage[] moves, boolean forRetur } @Override + @Platforms(Platform.HOSTED_ONLY.class) protected List generateAdaptations(NativeEntryPointInfo nep) { return fail(); } @@ -1017,6 +1024,7 @@ protected CallingSequence makeCallingSequence(MethodType type, FunctionDescripto } @Override + @Platforms(Platform.HOSTED_ONLY.class) protected List generateAdaptations(NativeEntryPointInfo nep) { var adaptations = super.generateAdaptations(nep); var assignments = nep.parametersAssignment(); @@ -1088,6 +1096,7 @@ protected CallingSequence makeCallingSequence(MethodType type, FunctionDescripto * assignments of float/double parameters to a cpu register. */ @Override + @Platforms(Platform.HOSTED_ONLY.class) protected List generateAdaptations(NativeEntryPointInfo nep) { List adaptations = super.generateAdaptations(nep); diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignAPIPredicates.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignAPIPredicates.java index 19928958a2ae..65d9537d4895 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignAPIPredicates.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignAPIPredicates.java @@ -26,7 +26,12 @@ import java.util.function.BooleanSupplier; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.option.SubstrateOptionsParser; +import com.oracle.svm.core.util.VMError; /** * Set of predicates used to control activation of substitutions (depending on method @@ -39,21 +44,51 @@ public final class ForeignAPIPredicates { public static final class Enabled implements BooleanSupplier { @Override public boolean getAsBoolean() { - return SubstrateOptions.ForeignAPISupport.getValue(); + return SubstrateOptions.isForeignAPIEnabled(); } } public static final class FunctionCallsSupported implements BooleanSupplier { @Override public boolean getAsBoolean() { - return SubstrateOptions.ForeignAPISupport.getValue() && ForeignFunctionsRuntime.areFunctionCallsSupported(); + return SubstrateOptions.isForeignAPIEnabled() && ForeignFunctionsRuntime.areFunctionCallsSupported(); } } public static final class FunctionCallsUnsupported implements BooleanSupplier { @Override public boolean getAsBoolean() { - return SubstrateOptions.ForeignAPISupport.getValue() && !ForeignFunctionsRuntime.areFunctionCallsSupported(); + return SubstrateOptions.isForeignAPIEnabled() && !ForeignFunctionsRuntime.areFunctionCallsSupported(); + } + } + + @Platforms(Platform.HOSTED_ONLY.class) + public static final class SharedArenasEnabled implements BooleanSupplier { + public static boolean getValue() { + return SubstrateOptions.isForeignAPIEnabled() && SubstrateOptions.SharedArenaSupport.getValue(); + } + + @Override + public boolean getAsBoolean() { + return SharedArenasEnabled.getValue(); + } + } + + public static final class SharedArenasDisabled implements BooleanSupplier { + private static final String SHARED_ARENA_SUPPORT_OPTION_NAME = SubstrateOptionsParser.commandArgument(SubstrateOptions.SharedArenaSupport, "+"); + + @Platforms(Platform.HOSTED_ONLY.class) + SharedArenasDisabled() { + } + + @Override + public boolean getAsBoolean() { + return !SharedArenasEnabled.getValue(); + } + + public static RuntimeException fail() { + assert !SharedArenasEnabled.getValue(); + throw VMError.unsupportedFeature("Support for Arena.ofShared is not active: enable with " + SHARED_ARENA_SUPPORT_OPTION_NAME); } } } diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java index add7af3358d7..937ab88bd5a0 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/ForeignFunctionsRuntime.java @@ -100,7 +100,7 @@ public static boolean areFunctionCallsSupported() { } public static RuntimeException functionCallsUnsupported() { - assert SubstrateOptions.ForeignAPISupport.getValue(); + assert SubstrateOptions.isForeignAPIEnabled(); throw VMError.unsupportedFeature("Calling foreign functions is currently not supported on platform: " + (OS.getCurrent().className + "-" + SubstrateUtil.getArchitectureName()).toLowerCase(Locale.ROOT)); } 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 44359df6f9ae..2d7a56a95c1e 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 @@ -33,6 +33,8 @@ import java.util.Optional; import java.util.function.Function; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platform.DARWIN; import org.graalvm.nativeimage.Platform.WINDOWS; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CFunction; @@ -40,7 +42,6 @@ import org.graalvm.nativeimage.c.function.CLibrary; import org.graalvm.word.Pointer; -import com.oracle.svm.core.OS; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.util.BasedOnJDKClass; @@ -56,7 +57,7 @@ public final class RuntimeSystemLookup { static final SymbolLookup INSTANCE = makeSystemLookup(); public static SymbolLookup makeSystemLookup() { - if (OS.WINDOWS.isCurrent()) { + if (Platform.includedIn(WINDOWS.class)) { /* * Windows support has some subtleties: one would ideally load ucrtbase.dll, but some * old installs might not have it, in which case msvcrt.dll should be loaded instead. If @@ -93,7 +94,7 @@ public static SymbolLookup makeSystemLookup() { } return lookup; - } else if (OS.DARWIN.isCurrent()) { + } else if (Platform.includedIn(DARWIN.class)) { return Util_java_lang_foreign_SymbolLookup.libraryLookup(LookupNativeLibraries::loadLibraryPlatformSpecific, List.of("/usr/lib/libSystem.B.dylib")); } else { /* @@ -114,6 +115,7 @@ public static SymbolLookup makeSystemLookup() { @CFunction(value = "__svm_get_syslookup_func", transition = Transition.NO_TRANSITION) public static native Pointer getSyslookupFunc(int i, int nExpected); + @Platforms(WINDOWS.class) private static Pointer getWindowsFallbackSymbol(String name) { try { assert Target_jdk_internal_foreign_SystemLookup_WindowsFallbackSymbols.class.isEnum(); diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_SystemLookup.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_SystemLookup.java index 9165ee27bf60..175de2ac2cdf 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_SystemLookup.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_foreign_SystemLookup.java @@ -25,8 +25,12 @@ package com.oracle.svm.core.foreign; import java.lang.foreign.MemorySegment; +import java.lang.foreign.SymbolLookup; import java.util.Optional; +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; @@ -37,11 +41,22 @@ */ @TargetClass(className = "jdk.internal.foreign.SystemLookup", onlyWith = ForeignAPIPredicates.Enabled.class) public final class Target_jdk_internal_foreign_SystemLookup { + // Checkstyle: stop + + /* + * This field must be cleared because on Windows, it references a closure which contains a + * native memory segment. + */ + @Alias // + @RecomputeFieldValue(isFinal = true, kind = Kind.Reset) // + static SymbolLookup SYSTEM_LOOKUP; + @SuppressWarnings("static-method") @Substitute public Optional find(String name) { return RuntimeSystemLookup.INSTANCE.find(name); } + // Checkstyle: resume } /* diff --git a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_misc_ScopedMemoryAccess.java b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_misc_ScopedMemoryAccess.java index 7310ca624d7e..76b0254e77c3 100644 --- a/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_misc_ScopedMemoryAccess.java +++ b/substratevm/src/com.oracle.svm.core.foreign/src/com/oracle/svm/core/foreign/Target_jdk_internal_misc_ScopedMemoryAccess.java @@ -33,6 +33,9 @@ import com.oracle.svm.core.ArenaIntrinsics; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.annotate.TargetElement; +import com.oracle.svm.core.foreign.ForeignAPIPredicates.SharedArenasDisabled; +import com.oracle.svm.core.foreign.ForeignAPIPredicates.SharedArenasEnabled; import com.oracle.svm.core.nodes.foreign.MemoryArenaValidInScopeNode; import com.oracle.svm.core.util.BasedOnJDKFile; @@ -96,7 +99,8 @@ static void registerNatives() { @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+20/src/java.base/share/classes/java/nio/MappedMemoryUtils.java#L50-L77") @SuppressWarnings("static-method") @Substitute - @Target_jdk_internal_misc_ScopedMemoryAccess_Scoped + @TargetElement(onlyWith = SharedArenasEnabled.class) + @SVMScoped @AlwaysInline("Safepoints must be visible in caller") public void loadInternal(MemorySessionImpl session, MappedMemoryUtilsProxy mappedUtils, long address, boolean isSync, long size) { SubstrateForeignUtil.checkIdentity(mappedUtils, Target_java_nio_MappedMemoryUtils.PROXY); @@ -116,7 +120,8 @@ public void loadInternal(MemorySessionImpl session, MappedMemoryUtilsProxy mappe @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+14/src/java.base/share/classes/java/nio/MappedMemoryUtils.java#L182-L185") @SuppressWarnings("static-method") @Substitute - @Target_jdk_internal_misc_ScopedMemoryAccess_Scoped + @TargetElement(onlyWith = SharedArenasEnabled.class) + @SVMScoped @AlwaysInline("Safepoints must be visible in caller") public boolean isLoadedInternal(MemorySessionImpl session, MappedMemoryUtilsProxy mappedUtils, long address, boolean isSync, long size) { SubstrateForeignUtil.checkIdentity(mappedUtils, Target_java_nio_MappedMemoryUtils.PROXY); @@ -137,7 +142,8 @@ public boolean isLoadedInternal(MemorySessionImpl session, MappedMemoryUtilsProx @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+14/src/java.base/share/classes/java/nio/MappedMemoryUtils.java#L192-L195") @SuppressWarnings("static-method") @Substitute - @Target_jdk_internal_misc_ScopedMemoryAccess_Scoped + @TargetElement(onlyWith = SharedArenasEnabled.class) + @SVMScoped @AlwaysInline("Safepoints must be visible in caller") public void unloadInternal(MemorySessionImpl session, MappedMemoryUtilsProxy mappedUtils, long address, boolean isSync, long size) { SubstrateForeignUtil.checkIdentity(mappedUtils, Target_java_nio_MappedMemoryUtils.PROXY); @@ -159,7 +165,8 @@ public void unloadInternal(MemorySessionImpl session, MappedMemoryUtilsProxy map @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+14/src/java.base/share/classes/java/nio/MappedMemoryUtils.java#L197-L200") @SuppressWarnings("static-method") @Substitute - @Target_jdk_internal_misc_ScopedMemoryAccess_Scoped + @TargetElement(onlyWith = SharedArenasEnabled.class) + @SVMScoped @AlwaysInline("Safepoints must be visible in caller") public void forceInternal(MemorySessionImpl session, MappedMemoryUtilsProxy mappedUtils, FileDescriptor fd, long address, boolean isSync, long index, long length) { SubstrateForeignUtil.checkIdentity(mappedUtils, Target_java_nio_MappedMemoryUtils.PROXY); @@ -196,13 +203,19 @@ public void forceInternal(MemorySessionImpl session, MappedMemoryUtilsProxy mapp @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+20/src/hotspot/share/prims/scopedMemoryAccess.cpp#L215-L218") @SuppressWarnings("static-method") @Substitute + @TargetElement(onlyWith = SharedArenasEnabled.class) void closeScope0(Target_jdk_internal_foreign_MemorySessionImpl session, @SuppressWarnings("unused") Target_jdk_internal_misc_ScopedMemoryAccess_ScopedAccessError error) { new SyncCloseScopeOperation(session).enqueue(); } + + @Substitute + @TargetElement(name = "closeScope0", onlyWith = SharedArenasDisabled.class) + @SuppressWarnings({"unused", "static-method"}) + void closeScope0Unsupported(Target_jdk_internal_foreign_MemorySessionImpl session, Target_jdk_internal_misc_ScopedMemoryAccess_ScopedAccessError error) { + throw SharedArenasDisabled.fail(); + } } @Retention(RetentionPolicy.RUNTIME) -@TargetClass(className = "jdk.internal.misc.ScopedMemoryAccess$Scoped", onlyWith = ForeignAPIPredicates.Enabled.class) -@interface Target_jdk_internal_misc_ScopedMemoryAccess_Scoped { - +@interface SVMScoped { } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ForeignSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ForeignSupport.java index 1769b17d7042..8d0c0bae5359 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ForeignSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/ForeignSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -34,7 +34,7 @@ public interface ForeignSupport { @Fold static boolean isAvailable() { boolean result = ImageSingletons.contains(ForeignSupport.class); - assert result || !SubstrateOptions.ForeignAPISupport.getValue(); + assert result || !SubstrateOptions.isForeignAPIEnabled(); return result; } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 11388d667c10..1e4a9851b7d1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -51,8 +51,10 @@ import com.oracle.svm.core.c.libc.LibCBase; import com.oracle.svm.core.c.libc.MuslLibC; import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.graal.RuntimeCompilation; import com.oracle.svm.core.heap.ReferenceHandler; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.jdk.VectorAPIEnabled; import com.oracle.svm.core.option.APIOption; import com.oracle.svm.core.option.APIOptionGroup; import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue; @@ -1367,11 +1369,34 @@ public enum ReportingMode { public static final HostedOptionKey UseBaseLayerInclusionPolicy = new HostedOptionKey<>(false); @Option(help = "Support for calls via the Java Foreign Function and Memory API", type = Expert) // - public static final HostedOptionKey ForeignAPISupport = new HostedOptionKey<>(false); + public static final HostedOptionKey ForeignAPISupport = new HostedOptionKey<>(true); + + @Fold + public static boolean isForeignAPIEnabled() { + /* + * FFM API should be enabled by default only if running on a supported and tested platform. + * However, if the option is explicitly enabled, we still return 'true'. + */ + return SubstrateOptions.ForeignAPISupport.getValue() && + (SubstrateOptions.ForeignAPISupport.hasBeenSet() || Platform.includedIn(PLATFORM_JNI.class) && Platform.includedIn(NATIVE_ONLY.class)); + } @Option(help = "Support for intrinsics from the Java Vector API", type = Expert) // public static final HostedOptionKey VectorAPISupport = new HostedOptionKey<>(false); + @Option(help = "Enable support for Arena.ofShared ", type = Expert)// + public static final HostedOptionKey SharedArenaSupport = new HostedOptionKey<>(false, key -> { + if (key.getValue()) { + // GR-65268: Shared arenas cannot be used together with runtime compilations + UserError.guarantee(!RuntimeCompilation.isEnabled(), "Arena.ofShared is not supported with runtime compilations. " + + "Replace usages of Arena.ofShared with Arena.ofAuto and disable shared arena support."); + // GR-65162: Shared arenas cannot be used together with Vector API support + UserError.guarantee(!VectorAPIEnabled.getValue(), "Support for Arena.ofShared is not available with Vector API support. " + + "Either disable Vector API support using %s or replace usages of Arena.ofShared with Arena.ofAuto and disable shared arena support.", + SubstrateOptionsParser.commandArgument(VectorAPISupport, "-")); + } + }); + @Option(help = "Assume new types cannot be added after analysis", type = OptionType.Expert) // public static final HostedOptionKey ClosedTypeWorld = new HostedOptionKey<>(true) { @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ForeignDisabledSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ForeignDisabledSubstitutions.java index 36154d80724e..4fe93ae73b2e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ForeignDisabledSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/ForeignDisabledSubstitutions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -45,7 +45,7 @@ final class ForeignDisabled implements BooleanSupplier { @Override public boolean getAsBoolean() { - return !SubstrateOptions.ForeignAPISupport.getValue(); + return !SubstrateOptions.isForeignAPIEnabled(); } } @@ -173,7 +173,7 @@ final class ForeignDisabledSubstitutions { private static final String OPTION_NAME = SubstrateOptionsParser.commandArgument(SubstrateOptions.ForeignAPISupport, "+"); static RuntimeException fail() { - assert !SubstrateOptions.ForeignAPISupport.getValue(); + assert !SubstrateOptions.isForeignAPIEnabled(); throw VMError.unsupportedFeature("Support for the Java Foreign Function and Memory API is not active: enable with " + OPTION_NAME); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_loader_NativeLibraries.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_loader_NativeLibraries.java index 5cc76ce14def..8a03c6f26fdb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_loader_NativeLibraries.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Target_jdk_internal_loader_NativeLibraries.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2025, 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 @@ -24,7 +24,13 @@ */ package com.oracle.svm.core.jdk; +import java.util.Deque; +import java.util.Map; + +import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Delete; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; @@ -54,3 +60,11 @@ public long find(String name) { @TargetClass(value = jdk.internal.loader.NativeLibraries.class, innerClass = "NativeLibraryImpl") final class Target_jdk_internal_loader_NativeLibraries_NativeLibraryImpl { } + +@TargetClass(value = jdk.internal.loader.NativeLibraries.class, innerClass = "NativeLibraryContext") +final class Target_jdk_internal_loader_NativeLibraries_NativeLibraryContext { + + @Alias // + @RecomputeFieldValue(kind = Kind.Reset) // + static Map> nativeLibraryThreadContext; +} 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 60ffcc027588..7d92f9ae9ba2 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 2025, 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 @@ -71,17 +71,21 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.code.FactoryMethodHolder; +import com.oracle.svm.core.code.FactoryThrowMethodHolder; import com.oracle.svm.core.configure.ConfigurationFiles; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.foreign.AbiUtils; +import com.oracle.svm.core.foreign.ForeignAPIPredicates; import com.oracle.svm.core.foreign.ForeignFunctionsRuntime; import com.oracle.svm.core.foreign.JavaEntryPointInfo; import com.oracle.svm.core.foreign.NativeEntryPointInfo; import com.oracle.svm.core.foreign.RuntimeSystemLookup; import com.oracle.svm.core.foreign.SubstrateMappedMemoryUtils; import com.oracle.svm.core.foreign.Target_java_nio_MappedMemoryUtils; +import com.oracle.svm.core.graal.RuntimeCompilation; import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider; +import com.oracle.svm.core.jdk.VectorAPIEnabled; import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.util.BasedOnJDKFile; import com.oracle.svm.core.util.UserError; @@ -106,6 +110,7 @@ import jdk.graal.compiler.phases.common.CanonicalizerPhase; import jdk.graal.compiler.phases.common.IterativeConditionalEliminationPhase; import jdk.graal.compiler.phases.tiers.MidTierContext; +import jdk.internal.foreign.AbstractMemorySegmentImpl; import jdk.internal.foreign.MemorySessionImpl; import jdk.internal.foreign.abi.AbstractLinker; import jdk.internal.foreign.abi.LinkerOptions; @@ -118,6 +123,7 @@ @AutomaticallyRegisteredFeature @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.vm.ci.aarch64"}, "java.base", new String[]{ @@ -242,6 +248,10 @@ private final class SharedArenaSupportImpl implements SharedArenaSupport { @Override public BasePhase createOptimizeSharedArenaAccessPhase() { + VMError.guarantee(ForeignAPIPredicates.SharedArenasEnabled.getValue(), "Support for shared arenas must be enabled"); + VMError.guarantee(!RuntimeCompilation.isEnabled(), "Shared arenas cannot be used together with runtime compilations (GR-65268)"); + VMError.guarantee(!VectorAPIEnabled.getValue(), "Shared arenas cannot be used together with Vector API support (GR-65162)"); + PhaseSuite sharedArenaPhases = new PhaseSuite<>(); sharedArenaPhases.appendPhase(new SubstrateOptimizeSharedArenaAccessPhase(CanonicalizerPhase.create())); /* @@ -255,6 +265,7 @@ public BasePhase createOptimizeSharedArenaAccessPhase() { @Override public void registerSafeArenaAccessorClass(AnalysisMetaAccess metaAccess, Class klass) { + assert ForeignAPIPredicates.SharedArenasEnabled.getValue(); ForeignFunctionsFeature.this.registerSafeArenaAccessorClass(metaAccess, klass); } } @@ -271,7 +282,7 @@ protected ForeignFunctionsFeature() { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { - if (!SubstrateOptions.ForeignAPISupport.getValue()) { + if (!SubstrateOptions.isForeignAPIEnabled()) { return false; } UserError.guarantee(!SubstrateOptions.useLLVMBackend(), "Support for the Foreign Function and Memory API is not available with the LLVM backend."); @@ -292,7 +303,10 @@ public void afterRegistration(AfterRegistrationAccess access) { public void duringSetup(DuringSetupAccess a) { var access = (FeatureImpl.DuringSetupAccessImpl) a; ImageSingletons.add(RuntimeForeignAccessSupport.class, accessSupport); - ImageSingletons.add(SharedArenaSupport.class, new SharedArenaSupportImpl()); + if (SubstrateOptions.SharedArenaSupport.getValue()) { + assert ForeignAPIPredicates.SharedArenasEnabled.getValue(); + ImageSingletons.add(SharedArenaSupport.class, new SharedArenaSupportImpl()); + } ImageClassLoader imageClassLoader = access.getImageClassLoader(); ConfigurationParserUtils.parseAndRegisterConfigurations(getConfigurationParser(imageClassLoader), imageClassLoader, "panama foreign", @@ -641,6 +655,7 @@ protected void initSafeArenaAccessors(BeforeAnalysisAccessImpl access) throws No MetaAccessProvider metaAccess = access.getMetaAccess(); registerSafeArenaAccessorClass(metaAccess, FactoryMethodHolder.class); + registerSafeArenaAccessorClass(metaAccess, FactoryThrowMethodHolder.class); registerSafeArenaAccessorClass(metaAccess, LogUtils.class); /* @@ -665,9 +680,19 @@ protected void initSafeArenaAccessors(BeforeAnalysisAccessImpl access) throws No registerSafeArenaAccessorMethod(metaAccess, ReflectionUtil.lookupMethod(mappedMemoryUtils, "unload", long.class, boolean.class, long.class)); registerSafeArenaAccessorMethod(metaAccess, ReflectionUtil.lookupMethod(SubstrateMappedMemoryUtils.class, "load", long.class, boolean.class, long.class)); - // the actual method checking a valid session state (if not inlined) is also safe as this - // one would yield the error + /* + * The actual method checking a valid session state (if not inlined) is also safe as this + * one would yield the error. + */ registerSafeArenaAccessorMethod(metaAccess, ReflectionUtil.lookupMethod(MemorySessionImpl.class, "checkValidStateRaw")); + + /* + * In case of open type world, methods 'ScopedMemoryAccess.(load|store)*MemorySegment*' do + * virtual calls to 'AbstractMemorySegmentImpl.unsafeGet(Base|Offset)'. Those cannot be + * inlined (since virtual) but we know that those methods do not access native memory. + */ + registerSafeArenaAccessorMethod(metaAccess, ReflectionUtil.lookupMethod(AbstractMemorySegmentImpl.class, "unsafeGetBase")); + registerSafeArenaAccessorMethod(metaAccess, ReflectionUtil.lookupMethod(AbstractMemorySegmentImpl.class, "unsafeGetOffset")); } protected void registerSafeArenaAccessorClass(MetaAccessProvider metaAccess, Class klass) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java index fb7144860545..8140bc69ec02 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java @@ -150,7 +150,7 @@ public static class Options { public final int optionScopedAllowedNodes = Options.InlineBeforeAnalysisScopedAllowedNodes.getValue(); public final int optionScopedAllowedInvokes = Options.InlineBeforeAnalysisScopedAllowedInvokes.getValue(); - public final boolean optionForeignAPISupport = SubstrateOptions.ForeignAPISupport.getValue(); + public final boolean optionForeignAPISupport = SubstrateOptions.isForeignAPIEnabled(); @SuppressWarnings("unchecked") // private static final Class COMPILED_LAMBDA_FORM_ANNOTATION = // diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java index 01685e6f36e9..6f93de99b6f4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/SharedGraphBuilderPhase.java @@ -42,7 +42,6 @@ import java.util.List; import java.util.Objects; -import com.oracle.svm.hosted.substitute.SubstitutionType; import org.graalvm.nativeimage.AnnotationAccess; import com.oracle.graal.pointsto.constraints.TypeInstantiationException; @@ -79,6 +78,7 @@ import com.oracle.svm.hosted.code.FactoryMethodSupport; import com.oracle.svm.hosted.code.SubstrateCompilationDirectives; import com.oracle.svm.hosted.nodes.DeoptProxyNode; +import com.oracle.svm.hosted.substitute.SubstitutionType; import com.oracle.svm.shaded.org.objectweb.asm.Opcodes; import com.oracle.svm.util.ReflectionUtil; @@ -184,13 +184,11 @@ public abstract static class SharedBytecodeParser extends BytecodeParser { private static final Class ABSTRACT_MEMORY_SEGMENT_IMPL_CLASS; static { - SCOPED_SUBSTRATE_ANNOTATION = ReflectionUtil.lookupClass(true, "com.oracle.svm.core.foreign.Target_jdk_internal_misc_ScopedMemoryAccess_Scoped"); - Class substrateForeignUtilClass = ReflectionUtil.lookupClass(true, "com.oracle.svm.core.foreign.SubstrateForeignUtil"); - SESSION_EXCEPTION_HANDLER_METHOD = substrateForeignUtilClass != null - ? ReflectionUtil.lookupMethod(substrateForeignUtilClass, "sessionExceptionHandler", MemorySessionImpl.class, Object.class, long.class) - : null; - MAPPED_MEMORY_UTILS_PROXY_CLASS = ReflectionUtil.lookupClass(true, "jdk.internal.access.foreign.MappedMemoryUtilsProxy"); - ABSTRACT_MEMORY_SEGMENT_IMPL_CLASS = ReflectionUtil.lookupClass(true, "jdk.internal.foreign.AbstractMemorySegmentImpl"); + SCOPED_SUBSTRATE_ANNOTATION = ReflectionUtil.lookupClass("com.oracle.svm.core.foreign.SVMScoped"); + Class substrateForeignUtilClass = ReflectionUtil.lookupClass("com.oracle.svm.core.foreign.SubstrateForeignUtil"); + SESSION_EXCEPTION_HANDLER_METHOD = ReflectionUtil.lookupMethod(substrateForeignUtilClass, "sessionExceptionHandler", MemorySessionImpl.class, Object.class, long.class); + MAPPED_MEMORY_UTILS_PROXY_CLASS = ReflectionUtil.lookupClass("jdk.internal.access.foreign.MappedMemoryUtilsProxy"); + ABSTRACT_MEMORY_SEGMENT_IMPL_CLASS = ReflectionUtil.lookupClass("jdk.internal.foreign.AbstractMemorySegmentImpl"); } protected List scopedMemorySessions; @@ -254,8 +252,7 @@ protected void build(FixedWithNextNode startInstruction, FrameStateBuilder start if (AnnotationAccess.isAnnotationPresent(method, (Class) SCOPED_SUBSTRATE_ANNOTATION) && SharedArenaSupport.isAvailable()) { // substituted, only add the scoped node introduceScopeNodes(); - } - if (AnnotationAccess.isAnnotationPresent(method, SharedArenaSupport.SCOPED_ANNOTATION) && SharedArenaSupport.isAvailable()) { + } else if (AnnotationAccess.isAnnotationPresent(method, SharedArenaSupport.SCOPED_ANNOTATION) && SharedArenaSupport.isAvailable()) { // not substituted, also instrument instrumentScopedMethod(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java index 812908e05fb1..a3d31495cc0f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java @@ -101,6 +101,7 @@ import com.oracle.svm.hosted.FallbackFeature; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.ReachabilityRegistrationNode; +import com.oracle.svm.hosted.SharedArenaSupport; import com.oracle.svm.hosted.code.SubstrateCompilationDirectives; import com.oracle.svm.hosted.nodes.DeoptProxyNode; import com.oracle.svm.hosted.nodes.ReadReservedRegister; @@ -223,18 +224,21 @@ public static void registerInvocationPlugins(AnnotationSubstitutionProcessor ann } private static void registerArenaPlugins(InvocationPlugins plugins) { - Registration r = new Registration(plugins, ArenaIntrinsics.class); - r.register(new RequiredInlineOnlyInvocationPlugin("checkArenaValidInScope", MemorySessionImpl.class, Object.class, long.class) { - @Override - public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode session, ValueNode ptrOne, ValueNode ptrTwo) { - b.setIsParsingScopedMemoryMethod(session); - MemoryArenaValidInScopeNode result = new MemoryArenaValidInScopeNode(session, new FieldLocationIdentity(b.getMetaAccess().lookupJavaField(MemoryArenaValidInScopeNode.STATE_FIELD))); - b.addPush(JavaKind.Long, result); - result.addScopeAssociatedValue(ptrOne); - result.addScopeAssociatedValue(ptrTwo); - return true; - } - }); + if (SharedArenaSupport.isAvailable()) { + Registration r = new Registration(plugins, ArenaIntrinsics.class); + r.register(new RequiredInlineOnlyInvocationPlugin("checkArenaValidInScope", MemorySessionImpl.class, Object.class, long.class) { + @Override + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode session, ValueNode ptrOne, ValueNode ptrTwo) { + b.setIsParsingScopedMemoryMethod(session); + MemoryArenaValidInScopeNode result = new MemoryArenaValidInScopeNode(session, + new FieldLocationIdentity(b.getMetaAccess().lookupJavaField(MemoryArenaValidInScopeNode.STATE_FIELD))); + b.addPush(JavaKind.Long, result); + result.addScopeAssociatedValue(ptrOne); + result.addScopeAssociatedValue(ptrTwo); + return true; + } + }); + } } private static void registerSerializationPlugins(ImageClassLoader loader, InvocationPlugins plugins, ParsingReason reason) {