Skip to content

Commit ec221fc

Browse files
committed
[GR-65934] Ensure LoadImageSingletonNodes are folded in the app layer
PullRequest: graal/21191
2 parents fc4d6e6 + 355f57e commit ec221fc

File tree

7 files changed

+160
-69
lines changed

7 files changed

+160
-69
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/nodes/LoadImageSingletonNode.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ public Node canonical(CanonicalizerTool tool) {
7474
if (tool.allUsagesAvailable() && hasNoUsages()) {
7575
// can remove this load if it is never used.
7676
return null;
77+
} else if (singletonInfo.isApplicationLayerConstant()) {
78+
return singletonInfo.asApplicationLayerConstant(tool.getMetaAccess(), tool.getSnippetReflection());
7779
}
7880

7981
return this;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/LoadImageSingletonFactory.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import com.oracle.svm.core.graal.code.CGlobalDataInfo;
3030
import com.oracle.svm.core.graal.nodes.LoadImageSingletonNode;
3131

32+
import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
33+
import jdk.graal.compiler.nodes.ConstantNode;
3234
import jdk.vm.ci.meta.MetaAccessProvider;
3335

3436
public abstract class LoadImageSingletonFactory {
@@ -45,6 +47,10 @@ public interface LoadImageSingletonData {
4547
Class<?> getLoadType();
4648

4749
SingletonAccessInfo getAccessInfo();
50+
51+
boolean isApplicationLayerConstant();
52+
53+
ConstantNode asApplicationLayerConstant(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflectionProvider);
4854
}
4955

5056
protected abstract LoadImageSingletonData getApplicationLayerOnlyImageSingletonInfo(Class<?> clazz);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/LayeredImageSingletonBuilderFlags.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626

2727
import java.util.EnumSet;
2828

29+
import com.oracle.svm.core.graal.RuntimeCompilation;
30+
import com.oracle.svm.core.util.VMError;
31+
2932
/**
3033
* Flags used during build time to determine how the native image generator handles the layered
3134
* image singleton.
@@ -76,4 +79,22 @@ public static boolean verifyImageBuilderFlags(LayeredImageSingleton singleton) {
7679

7780
return true;
7881
}
82+
83+
public static void validateRuntimeLookup(Object singleton) {
84+
if (singleton instanceof LayeredImageSingleton layeredSingleton) {
85+
if (!layeredSingleton.getImageBuilderFlags().contains(LayeredImageSingletonBuilderFlags.RUNTIME_ACCESS)) {
86+
/*
87+
* Runtime compilation installs many singletons into the image which are otherwise
88+
* hosted only. Note the platform checks still apply and can be used to ensure
89+
* certain singleton are not installed into the image.
90+
*/
91+
if (!RuntimeCompilation.isEnabled()) {
92+
throw VMError.shouldNotReachHere("Layered image singleton without runtime access is in runtime graph: " + layeredSingleton);
93+
}
94+
}
95+
if (layeredSingleton instanceof MultiLayeredImageSingleton) {
96+
throw VMError.shouldNotReachHere("Forbidden lookup of MultiLayeredImageSingleton in runtime graph: " + layeredSingleton);
97+
}
98+
}
99+
}
79100
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.hosted.imagelayer;
26+
27+
import static com.oracle.svm.hosted.imagelayer.LoadImageSingletonFeature.getCrossLayerSingletonMappingInfo;
28+
29+
import org.graalvm.nativeimage.ImageSingletons;
30+
31+
import com.oracle.svm.core.graal.code.CGlobalDataInfo;
32+
import com.oracle.svm.core.imagelayer.LoadImageSingletonFactory;
33+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
34+
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
35+
import com.oracle.svm.core.util.VMError;
36+
import com.oracle.svm.hosted.c.CGlobalDataFeature;
37+
38+
import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
39+
import jdk.graal.compiler.nodes.ConstantNode;
40+
import jdk.vm.ci.meta.MetaAccessProvider;
41+
42+
class LoadImageSingletonDataImpl implements LoadImageSingletonFactory.LoadImageSingletonData {
43+
44+
private final Class<?> key;
45+
private final SlotRecordKind kind;
46+
private boolean applicationLayerConstant;
47+
48+
LoadImageSingletonDataImpl(Class<?> key, SlotRecordKind kind) {
49+
this.key = key;
50+
this.kind = kind;
51+
}
52+
53+
public Class<?> getKey() {
54+
return key;
55+
}
56+
57+
public SlotRecordKind getKind() {
58+
return kind;
59+
}
60+
61+
@Override
62+
public Class<?> getLoadType() {
63+
return kind == SlotRecordKind.APPLICATION_LAYER_SINGLETON ? key : key.arrayType();
64+
}
65+
66+
@Override
67+
public LoadImageSingletonFactory.SingletonAccessInfo getAccessInfo() {
68+
VMError.guarantee(!applicationLayerConstant, "this node should instead be constant folded");
69+
CrossLayerSingletonMappingInfo singleton = getCrossLayerSingletonMappingInfo();
70+
assert singleton.singletonTableStart != null;
71+
CGlobalDataInfo cglobal = CGlobalDataFeature.singleton().registerAsAccessedOrGet(singleton.singletonTableStart);
72+
int slotNum = singleton.currentKeyToSlotInfoMap.get(key).slotNum();
73+
return new LoadImageSingletonFactory.SingletonAccessInfo(cglobal, slotNum * singleton.referenceSize);
74+
}
75+
76+
void setApplicationLayerConstant() {
77+
applicationLayerConstant = true;
78+
}
79+
80+
@Override
81+
public boolean isApplicationLayerConstant() {
82+
return applicationLayerConstant;
83+
}
84+
85+
@Override
86+
public ConstantNode asApplicationLayerConstant(MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflectionProvider) {
87+
VMError.guarantee(isApplicationLayerConstant());
88+
return switch (kind) {
89+
case APPLICATION_LAYER_SINGLETON -> {
90+
Object singleton = LayeredImageSingletonSupport.singleton().lookup(key, true, false);
91+
LayeredImageSingletonBuilderFlags.validateRuntimeLookup(singleton);
92+
yield ConstantNode.forConstant(snippetReflectionProvider.forObject(singleton), metaAccess);
93+
}
94+
case MULTI_LAYERED_SINGLETON -> {
95+
var multiLayerArray = ImageSingletons.lookup(LoadImageSingletonFeature.class).getMultiLayerConstant(key, metaAccess, snippetReflectionProvider);
96+
yield ConstantNode.forConstant(multiLayerArray, 1, true, metaAccess);
97+
}
98+
};
99+
}
100+
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java

Lines changed: 20 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@
2525
package com.oracle.svm.hosted.imagelayer;
2626

2727
import static com.oracle.svm.hosted.imagelayer.LoadImageSingletonFeature.CROSS_LAYER_SINGLETON_TABLE_SYMBOL;
28-
import static com.oracle.svm.hosted.imagelayer.LoadImageSingletonFeature.getCrossLayerSingletonMappingInfo;
2928

3029
import java.util.ArrayList;
3130
import java.util.Comparator;
@@ -55,7 +54,6 @@
5554
import com.oracle.svm.core.config.ConfigurationValues;
5655
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
5756
import com.oracle.svm.core.feature.InternalFeature;
58-
import com.oracle.svm.core.graal.code.CGlobalDataInfo;
5957
import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo;
6058
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
6159
import com.oracle.svm.core.imagelayer.LoadImageSingletonFactory;
@@ -72,9 +70,9 @@
7270
import com.oracle.svm.core.util.UserError;
7371
import com.oracle.svm.core.util.VMError;
7472
import com.oracle.svm.hosted.FeatureImpl;
75-
import com.oracle.svm.hosted.c.CGlobalDataFeature;
7673
import com.oracle.svm.hosted.image.NativeImageHeap;
7774
import com.oracle.svm.hosted.meta.HostedMetaAccess;
75+
import com.oracle.svm.hosted.meta.HostedType;
7876
import com.oracle.svm.hosted.meta.HostedUniverse;
7977
import com.oracle.svm.util.ReflectionUtil;
8078

@@ -93,7 +91,9 @@
9391
import jdk.vm.ci.code.BytecodePosition;
9492
import jdk.vm.ci.meta.JavaConstant;
9593
import jdk.vm.ci.meta.JavaKind;
94+
import jdk.vm.ci.meta.MetaAccessProvider;
9695
import jdk.vm.ci.meta.ResolvedJavaMethod;
96+
import jdk.vm.ci.meta.ResolvedJavaType;
9797

9898
/**
9999
* Tracks metdata {@link MultiLayeredImageSingleton} and {@link ApplicationLayerOnlyImageSingleton}
@@ -136,8 +136,7 @@ public void registerInvocationPlugins(Providers providers, GraphBuilderConfigura
136136
/*
137137
* Can directly load the array of all objects
138138
*/
139-
JavaConstant multiLayerArray = keyToMultiLayerConstantMap.computeIfAbsent(key,
140-
k -> createMultiLayerArray(key, (AnalysisType) b.getMetaAccess().lookupJavaType(k.arrayType()), b.getSnippetReflection()));
139+
JavaConstant multiLayerArray = getMultiLayerConstant(key, b.getMetaAccess(), b.getSnippetReflection());
141140
return ConstantNode.forConstant(multiLayerArray, 1, true, b.getMetaAccess());
142141
}
143142
};
@@ -297,7 +296,19 @@ public void beforeCompilation(BeforeCompilationAccess access) {
297296
getCrossLayerSingletonMappingInfo().assignSlots(config.getMetaAccess());
298297
}
299298

300-
ImageHeapObjectArray createMultiLayerArray(Class<?> key, AnalysisType arrayType, SnippetReflectionProvider snippetReflectionProvider) {
299+
private static AnalysisType getAnalysisType(ResolvedJavaType type) {
300+
if (type instanceof HostedType hostedType) {
301+
return hostedType.getWrapped();
302+
} else {
303+
return (AnalysisType) type;
304+
}
305+
}
306+
307+
JavaConstant getMultiLayerConstant(Class<?> key, MetaAccessProvider metaAccess, SnippetReflectionProvider snippetReflectionProvider) {
308+
return keyToMultiLayerConstantMap.computeIfAbsent(key, k -> createMultiLayerArray(key, getAnalysisType(metaAccess.lookupJavaType(k.arrayType())), snippetReflectionProvider));
309+
}
310+
311+
private ImageHeapObjectArray createMultiLayerArray(Class<?> key, AnalysisType arrayType, SnippetReflectionProvider snippetReflectionProvider) {
301312
List<Integer> priorIds = getCrossLayerSingletonMappingInfo().getPriorLayerObjectIDs(key);
302313
var layerInfo = DynamicImageLayerInfo.singleton();
303314

@@ -390,20 +401,10 @@ private void addInitialObjects(NativeImageHeap heap, HostedUniverse hUniverse) {
390401
}
391402
case MULTI_LAYERED_SINGLETON -> {
392403
/*
393-
* Check if we already created this object via an intrinsification.
404+
* Ensure the multi-layer constant is created and installed within the heap.
394405
*/
395-
JavaConstant multiLayerArray = keyToMultiLayerConstantMap.get(slotInfo.keyClass());
396-
if (multiLayerArray == null) {
397-
/*
398-
* Need to install the array which points to all installed singletons.
399-
*/
400-
ImageHeapObjectArray imageHeapArray = createMultiLayerArray(slotInfo.keyClass(), heap.hMetaAccess.lookupJavaType(slotInfo.keyClass().arrayType()).getWrapped(),
401-
hUniverse.getSnippetReflection());
402-
403-
heap.addConstant(imageHeapArray, true, addReason);
404-
405-
multiLayerArray = imageHeapArray;
406-
}
406+
JavaConstant multiLayerArray = getMultiLayerConstant(slotInfo.keyClass(), heap.hMetaAccess, hUniverse.getSnippetReflection());
407+
heap.addConstant(multiLayerArray, true, addReason);
407408

408409
yield multiLayerArray;
409410
}
@@ -700,32 +701,3 @@ public static Object createFromLoader(ImageSingletonLoader loader) {
700701
return new CrossLayerSingletonMappingInfo(Map.copyOf(keyClassToSlotInfoMap), Map.copyOf(keyClassToObjectIDListMap));
701702
}
702703
}
703-
704-
class LoadImageSingletonDataImpl implements LoadImageSingletonFactory.LoadImageSingletonData {
705-
706-
private final Class<?> key;
707-
private final SlotRecordKind kind;
708-
709-
LoadImageSingletonDataImpl(Class<?> key, SlotRecordKind kind) {
710-
this.key = key;
711-
this.kind = kind;
712-
}
713-
714-
public SlotRecordKind getKind() {
715-
return kind;
716-
}
717-
718-
@Override
719-
public Class<?> getLoadType() {
720-
return kind == SlotRecordKind.APPLICATION_LAYER_SINGLETON ? key : key.arrayType();
721-
}
722-
723-
@Override
724-
public LoadImageSingletonFactory.SingletonAccessInfo getAccessInfo() {
725-
CrossLayerSingletonMappingInfo singleton = getCrossLayerSingletonMappingInfo();
726-
assert singleton.singletonTableStart != null;
727-
CGlobalDataInfo cglobal = CGlobalDataFeature.singleton().registerAsAccessedOrGet(singleton.singletonTableStart);
728-
int slotNum = singleton.currentKeyToSlotInfoMap.get(key).slotNum();
729-
return new LoadImageSingletonFactory.SingletonAccessInfo(cglobal, slotNum * singleton.referenceSize);
730-
}
731-
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import com.oracle.svm.core.classinitialization.ClassInitializationInfo;
8585
import com.oracle.svm.core.graal.code.CGlobalDataInfo;
8686
import com.oracle.svm.core.hub.DynamicHub;
87+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
8788
import com.oracle.svm.core.meta.MethodPointer;
8889
import com.oracle.svm.core.reflect.serialize.SerializationSupport;
8990
import com.oracle.svm.core.util.VMError;
@@ -157,6 +158,7 @@ public class SVMImageLayerLoader extends ImageLayerLoader {
157158
private final SharedLayerSnapshot.Reader snapshot;
158159
private final FileChannel graphsChannel;
159160
private final ClassInitializationSupport classInitializationSupport;
161+
private final boolean buildingApplicationLayer;
160162

161163
private HostedUniverse hostedUniverse;
162164

@@ -203,6 +205,7 @@ public SVMImageLayerLoader(SVMImageLayerSnapshotUtil imageLayerSnapshotUtil, Hos
203205
this.graphsChannel = graphChannel;
204206
this.useSharedLayerGraphs = useSharedLayerGraphs;
205207
classInitializationSupport = ClassInitializationSupport.singleton();
208+
buildingApplicationLayer = ImageLayerBuildingSupport.buildingApplicationLayer();
206209
}
207210

208211
public AnalysisUniverse getUniverse() {
@@ -1012,8 +1015,11 @@ private EncodedGraph getEncodedGraph(AnalysisMethod analysisMethod, Text.Reader
10121015
SVMImageLayerSnapshotUtil.AbstractSVMGraphDecoder decoder = imageLayerSnapshotUtil.getGraphDecoder(this, analysisMethod, universe.getSnippetReflection(), nodeClassMap);
10131016
EncodedGraph encodedGraph = (EncodedGraph) ObjectCopier.decode(decoder, encodedAnalyzedGraph);
10141017
for (int i = 0; i < encodedGraph.getNumObjects(); ++i) {
1015-
if (encodedGraph.getObject(i) instanceof CGlobalDataInfo cGlobalDataInfo) {
1018+
Object obj = encodedGraph.getObject(i);
1019+
if (obj instanceof CGlobalDataInfo cGlobalDataInfo) {
10161020
encodedGraph.setObject(i, CGlobalDataFeature.singleton().registerAsAccessedOrGet(cGlobalDataInfo.getData()));
1021+
} else if (buildingApplicationLayer && obj instanceof LoadImageSingletonDataImpl data) {
1022+
data.setApplicationLayerConstant();
10171023
}
10181024
}
10191025
return encodedGraph;

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/snippets/SubstrateGraphBuilderPlugins.java

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,6 @@
3838
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
3939
import java.util.stream.Stream;
4040

41-
import com.oracle.svm.core.encoder.SymbolEncoder;
42-
import jdk.graal.compiler.core.common.LibGraalSupport;
43-
import jdk.graal.compiler.core.common.NativeImageSupport;
4441
import org.graalvm.nativeimage.AnnotationAccess;
4542
import org.graalvm.nativeimage.ImageInfo;
4643
import org.graalvm.nativeimage.ImageSingletons;
@@ -68,7 +65,7 @@
6865
import com.oracle.svm.core.SubstrateOptions;
6966
import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode;
7067
import com.oracle.svm.core.config.ConfigurationValues;
71-
import com.oracle.svm.core.graal.RuntimeCompilation;
68+
import com.oracle.svm.core.encoder.SymbolEncoder;
7269
import com.oracle.svm.core.graal.jdk.SubstrateObjectCloneWithExceptionNode;
7370
import com.oracle.svm.core.graal.nodes.DeoptEntryNode;
7471
import com.oracle.svm.core.graal.nodes.FarReturnNode;
@@ -92,7 +89,6 @@
9289
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
9390
import com.oracle.svm.core.layeredimagesingleton.ApplicationLayerOnlyImageSingleton;
9491
import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton;
95-
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
9692
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
9793
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
9894
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
@@ -111,6 +107,8 @@
111107
import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor;
112108

113109
import jdk.graal.compiler.core.common.CompressEncoding;
110+
import jdk.graal.compiler.core.common.LibGraalSupport;
111+
import jdk.graal.compiler.core.common.NativeImageSupport;
114112
import jdk.graal.compiler.core.common.type.AbstractObjectStamp;
115113
import jdk.graal.compiler.core.common.type.IntegerStamp;
116114
import jdk.graal.compiler.core.common.type.StampFactory;
@@ -1227,21 +1225,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
12271225
}
12281226

12291227
Object singleton = LayeredImageSingletonSupport.singleton().lookup(key, true, true);
1230-
if (singleton instanceof LayeredImageSingleton layeredSingleton) {
1231-
if (!layeredSingleton.getImageBuilderFlags().contains(LayeredImageSingletonBuilderFlags.RUNTIME_ACCESS)) {
1232-
/*
1233-
* Runtime compilation installs many singletons into the image which are
1234-
* otherwise hosted only. Note the platform checks still apply and can be
1235-
* used to ensure certain singleton are not installed into the image.
1236-
*/
1237-
if (!RuntimeCompilation.isEnabled()) {
1238-
throw VMError.shouldNotReachHere("Layered image singleton without runtime access is in runtime graph: " + singleton);
1239-
}
1240-
}
1241-
if (layeredSingleton instanceof MultiLayeredImageSingleton) {
1242-
throw VMError.shouldNotReachHere("Forbidden lookup of MultiLayeredImageSingleton in runtime graph: " + singleton);
1243-
}
1244-
}
1228+
LayeredImageSingletonBuilderFlags.validateRuntimeLookup(singleton);
12451229
b.addPush(JavaKind.Object, ConstantNode.forConstant(b.getSnippetReflection().forObject(singleton), b.getMetaAccess()));
12461230
return true;
12471231
}

0 commit comments

Comments
 (0)