From 2e90ee5ced3e62e5d925541fe3bc8c2620a8937b Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Tue, 3 Sep 2024 11:39:03 +0200 Subject: [PATCH 1/2] enable constant folding on getAllLayers calls --- .../oracle/svm/core/code/CodeInfoTable.java | 9 +--- .../core/code/RuntimeMetadataDecoderImpl.java | 2 +- .../svm/core/hub/DynamicHubSupport.java | 7 +-- .../svm/core/jdk/StringInternSupport.java | 19 +++---- .../MultiLayeredImageSingleton.java | 10 ++++ .../NonLayeredImageSingletonFeature.java | 46 +++++++++++++--- .../imagelayer/LoadImageSingletonFeature.java | 53 ++++++++++++------- 7 files changed, 93 insertions(+), 53 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java index 25ee6b2eeef1..b18816cc1e85 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoTable.java @@ -45,7 +45,6 @@ import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.heap.RestrictHeapAccess.Access; import com.oracle.svm.core.heap.VMOperationInfos; -import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.meta.SharedMethod; @@ -103,13 +102,7 @@ public static CodeInfo getFirstImageCodeInfo() { @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static CodeInfo getFirstImageCodeInfo(int layerNumber) { - if (ImageLayerBuildingSupport.buildingImageLayer()) { - ImageCodeInfoStorage[] runtimeCodeInfos = MultiLayeredImageSingleton.getAllLayers(ImageCodeInfoStorage.class); - return runtimeCodeInfos[layerNumber].getData(); - } else { - assert layerNumber == 0; - return imageCodeInfo; - } + return MultiLayeredImageSingleton.getForLayer(ImageCodeInfoStorage.class, layerNumber).getData(); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeMetadataDecoderImpl.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeMetadataDecoderImpl.java index 192c05b3d396..f2f3a4bdb9ed 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeMetadataDecoderImpl.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/RuntimeMetadataDecoderImpl.java @@ -96,7 +96,7 @@ public class RuntimeMetadataDecoderImpl implements RuntimeMetadataDecoder { public static final int CLASS_ACCESS_FLAGS_MASK = 0x1FFF; static byte[] getEncoding(DynamicHub hub) { - return MultiLayeredImageSingleton.getAllLayers(RuntimeMetadataEncoding.class)[hub.getLayerId()].getEncoding(); + return MultiLayeredImageSingleton.getForLayer(RuntimeMetadataEncoding.class, hub.getLayerId()).getEncoding(); } static List getEncodings() { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubSupport.java index a9d8b038458d..661a7e8f88f2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHubSupport.java @@ -38,7 +38,6 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; import com.oracle.svm.core.heap.UnknownObjectField; import com.oracle.svm.core.heap.UnknownPrimitiveField; -import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton; @@ -57,11 +56,7 @@ public static DynamicHubSupport singleton() { @AlwaysInline("Performance") @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) public static DynamicHubSupport forLayer(int layerIndex) { - if (!ImageLayerBuildingSupport.buildingImageLayer()) { - return ImageSingletons.lookup(DynamicHubSupport.class); - } - DynamicHubSupport[] supports = MultiLayeredImageSingleton.getAllLayers(DynamicHubSupport.class); - return supports[layerIndex]; + return MultiLayeredImageSingleton.getForLayer(DynamicHubSupport.class, layerIndex); } @Platforms(Platform.HOSTED_ONLY.class) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StringInternSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StringInternSupport.java index 15cde40f7045..9d42e499a978 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StringInternSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/StringInternSupport.java @@ -152,20 +152,13 @@ String intern(String str) { private String doIntern(String str) { String result = str; - if (ImageLayerBuildingSupport.buildingImageLayer()) { - StringInternSupport[] layers = MultiLayeredImageSingleton.getAllLayers(StringInternSupport.class); - for (StringInternSupport layer : layers) { - String[] layerImageInternedStrings = layer.imageInternedStrings; - int imageIdx = Arrays.binarySearch(layerImageInternedStrings, str); - if (imageIdx >= 0) { - result = layerImageInternedStrings[imageIdx]; - break; - } - } - } else { - int imageIdx = Arrays.binarySearch(imageInternedStrings, str); + StringInternSupport[] layers = MultiLayeredImageSingleton.getAllLayers(StringInternSupport.class); + for (StringInternSupport layer : layers) { + String[] layerImageInternedStrings = layer.imageInternedStrings; + int imageIdx = Arrays.binarySearch(layerImageInternedStrings, str); if (imageIdx >= 0) { - result = imageInternedStrings[imageIdx]; + result = layerImageInternedStrings[imageIdx]; + break; } } String oldValue = internedStrings.putIfAbsent(result, result); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/MultiLayeredImageSingleton.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/MultiLayeredImageSingleton.java index b6f0b8bfe6d1..b27605f8b3f2 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/MultiLayeredImageSingleton.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/MultiLayeredImageSingleton.java @@ -40,6 +40,16 @@ static T[] getAllLayers(Class key) { throw VMError.shouldNotReachHere("This can only be called during runtime"); } + /** + * Retrieve a specific layer from a MultiLayeredImageSingleton. Note if a + * MultiLayeredImageSingleton is not installed in all layers, then the singletons index will not + * match the layer number it was installed in. + */ + @SuppressWarnings("unused") + static T getForLayer(Class key, int index) { + throw VMError.shouldNotReachHere("This can only be called during runtime"); + } + default U getSingletonData(T singleton, T[] singletons, Function getSingletonDataFunction) { if (ImageLayerBuildingSupport.buildingImageLayer()) { for (var layerSingleton : singletons) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/NonLayeredImageSingletonFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/NonLayeredImageSingletonFeature.java index 930f526d9a89..fa31b4a3ec90 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/NonLayeredImageSingletonFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/NonLayeredImageSingletonFeature.java @@ -24,12 +24,16 @@ */ package com.oracle.svm.core.layeredimagesingleton; +import static jdk.graal.compiler.core.common.calc.Condition.NE; + import java.lang.reflect.Array; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.ParsingReason; +import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; @@ -42,11 +46,13 @@ import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugin; import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins; import jdk.graal.compiler.phases.util.Providers; +import jdk.graal.compiler.replacements.InvocationPluginHelper; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaMethod; /** - * Adds support for layered image singleton features within traditional builds. + * Adds support for layered image singleton features within traditional builds. We know traditional + * builds have at most exactly one singleton, so we can optimize these calls accordingly. */ @AutomaticallyRegisteredFeature public class NonLayeredImageSingletonFeature implements InternalFeature, FeatureSingleton { @@ -60,18 +66,23 @@ public boolean isInConfiguration(Feature.IsInConfigurationAccess access) { @Override public void registerInvocationPlugins(Providers providers, GraphBuilderConfiguration.Plugins plugins, ParsingReason reason) { + Function, Object> lookupMultiLayeredImageSingleton = (key) -> { + Object singleton = LayeredImageSingletonSupport.singleton().runtimeLookup(key); + boolean conditions = singleton.getClass().equals(key) && + singleton instanceof MultiLayeredImageSingleton multiLayerSingleton && + multiLayerSingleton.getImageBuilderFlags().contains(LayeredImageSingletonBuilderFlags.RUNTIME_ACCESS); + VMError.guarantee(conditions, "Illegal singleton %s", singleton); + + return singleton; + }; + InvocationPlugins.Registration r = new InvocationPlugins.Registration(plugins.getInvocationPlugins(), MultiLayeredImageSingleton.class); r.register(new InvocationPlugin.RequiredInvocationPlugin("getAllLayers", Class.class) { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode classNode) { Class key = b.getSnippetReflection().asObject(Class.class, classNode.asJavaConstant()); - - Object singleton = LayeredImageSingletonSupport.singleton().runtimeLookup(key); - boolean conditions = singleton.getClass().equals(key) && - singleton instanceof MultiLayeredImageSingleton multiLayerSingleton && - multiLayerSingleton.getImageBuilderFlags().contains(LayeredImageSingletonBuilderFlags.RUNTIME_ACCESS); - VMError.guarantee(conditions, "Illegal singleton %s", singleton); + Object singleton = lookupMultiLayeredImageSingleton.apply(key); var multiLayeredArray = multiLayeredArrays.computeIfAbsent(key, k -> { var result = Array.newInstance(k, 1); @@ -83,5 +94,26 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec return true; } }); + + r.register(new InvocationPlugin.RequiredInvocationPlugin("getForLayer", Class.class, int.class) { + @Override + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode classNode, ValueNode indexNode) { + try (InvocationPluginHelper helper = new InvocationPluginHelper(b, targetMethod)) { + Class key = b.getSnippetReflection().asObject(Class.class, classNode.asJavaConstant()); + Object singleton = lookupMultiLayeredImageSingleton.apply(key); + + /* + * We know this index has to be zero. For performance reasons we validate this + * only when assertions are enabled. + */ + if (SubstrateUtil.assertionsEnabled()) { + helper.intrinsicRangeCheck(indexNode, NE, ConstantNode.forInt(0)); + } + + helper.emitFinalReturn(JavaKind.Object, ConstantNode.forConstant(b.getSnippetReflection().forObject(singleton), b.getMetaAccess(), b.getGraph())); + return true; + } + } + }); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java index db7d68f96b00..a8e1b6d810a9 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java @@ -35,6 +35,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.stream.Stream; @@ -53,7 +54,6 @@ import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.graal.code.CGlobalDataInfo; -import com.oracle.svm.core.graal.nodes.LoadImageSingletonNode; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.imagelayer.LoadImageSingletonFactory; import com.oracle.svm.core.layeredimagesingleton.ApplicationLayerOnlyImageSingleton; @@ -81,7 +81,9 @@ import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext; import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugin; import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins; +import jdk.graal.compiler.nodes.java.LoadIndexedNode; import jdk.graal.compiler.phases.util.Providers; +import jdk.graal.compiler.replacements.InvocationPluginHelper; import jdk.vm.ci.code.BytecodeFrame; import jdk.vm.ci.code.BytecodePosition; import jdk.vm.ci.meta.JavaConstant; @@ -101,7 +103,8 @@ private static CrossLayerSingletonMappingInfo getCrossLayerSingletonMappingInfo( } /* - * Cache for objects created by the calls to getAllLayers within the application layer. + * Cache for objects created by the calls to getAllLayers. This cache is only used in the + * application layer. */ private final Map, JavaConstant> keyToMultiLayerConstantMap = new ConcurrentHashMap<>(); /* @@ -116,29 +119,43 @@ public boolean isInConfiguration(IsInConfigurationAccess access) { @Override public void registerInvocationPlugins(Providers providers, GraphBuilderConfiguration.Plugins plugins, ParsingReason reason) { + BiFunction loadMultiLayeredImageSingleton = (b, classNode) -> { + Class key = b.getSnippetReflection().asObject(Class.class, classNode.asJavaConstant()); + + if (ImageLayerBuildingSupport.buildingSharedLayer()) { + /* + * Load reference to the proper slot within the cross-layer singleton table. + */ + return LoadImageSingletonFactory.loadLayeredImageSingleton(key, b.getMetaAccess()); + } else { + /* + * Can directly load the array of all objects + */ + JavaConstant multiLayerArray = keyToMultiLayerConstantMap.computeIfAbsent(key, + k -> createMultiLayerArray(key, (AnalysisType) b.getMetaAccess().lookupJavaType(k.arrayType()), b.getSnippetReflection())); + return ConstantNode.forConstant(multiLayerArray, 1, true, b.getMetaAccess()); + } + }; + InvocationPlugins.Registration r = new InvocationPlugins.Registration(plugins.getInvocationPlugins(), MultiLayeredImageSingleton.class); r.register(new InvocationPlugin.RequiredInvocationPlugin("getAllLayers", Class.class) { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode classNode) { + b.addPush(JavaKind.Object, loadMultiLayeredImageSingleton.apply(b, classNode)); + return true; + } + }); - Class key = b.getSnippetReflection().asObject(Class.class, classNode.asJavaConstant()); + r.register(new InvocationPlugin.RequiredInvocationPlugin("getForLayer", Class.class, int.class) { + @Override + public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver unused, ValueNode classNode, ValueNode indexNode) { + try (InvocationPluginHelper helper = new InvocationPluginHelper(b, targetMethod)) { + ValueNode layerArray = b.add(loadMultiLayeredImageSingleton.apply(b, classNode)); - if (ImageLayerBuildingSupport.buildingSharedLayer()) { - /* - * Load reference to the proper slot within the cross-layer singleton table. - */ - LoadImageSingletonNode layeredSingleton = LoadImageSingletonFactory.loadLayeredImageSingleton(key, b.getMetaAccess()); - b.addPush(JavaKind.Object, layeredSingleton); - return true; - } else { - /* - * Can directly load the array of all objects - */ - JavaConstant multiLayerArray = keyToMultiLayerConstantMap.computeIfAbsent(key, - k -> createMultiLayerArray(key, (AnalysisType) b.getMetaAccess().lookupJavaType(k.arrayType()), b.getSnippetReflection())); - var node = ConstantNode.forConstant(multiLayerArray, b.getMetaAccess()); - b.addPush(JavaKind.Object, node); + helper.intrinsicArrayRangeCheck(layerArray, indexNode, ConstantNode.forInt(1)); + var arrayElem = LoadIndexedNode.create(null, layerArray, indexNode, null, JavaKind.Object, b.getMetaAccess(), b.getConstantReflection()); + helper.emitFinalReturn(JavaKind.Object, arrayElem); return true; } } From 69fc7db5adde067fa13ec308d8c3c250b732cb53 Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Mon, 11 Nov 2024 16:16:30 +0100 Subject: [PATCH 2/2] do not check safeguards when using open world --- .../src/com/oracle/svm/hosted/code/CompileQueue.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index e6e5c927b329..e0c6e8cc5a07 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -396,7 +396,9 @@ public void finish(DebugContext debug) { parseAll(); } - if (!ImageLayerBuildingSupport.buildingImageLayer() && !PointstoOptions.UseExperimentalReachabilityAnalysis.getValue(universe.hostVM().options())) { + // GR-59742 re-enable for open type world and layered images + if (SubstrateOptions.useClosedTypeWorld() && !ImageLayerBuildingSupport.buildingImageLayer() && + !PointstoOptions.UseExperimentalReachabilityAnalysis.getValue(universe.hostVM().options())) { /* * Reachability Analysis creates call graphs with more edges compared to the * Points-to Analysis, therefore the annotations would have to be added to a lot