Skip to content

Commit 47d997e

Browse files
committed
[GR-59783] Change MultiLayerImageSingleton array properties.
PullRequest: graal/19815
2 parents 0f61c40 + 2988f54 commit 47d997e

File tree

4 files changed

+115
-13
lines changed

4 files changed

+115
-13
lines changed

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
import org.graalvm.nativeimage.Platform;
3131
import org.graalvm.nativeimage.Platforms;
3232

33+
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
34+
3335
/**
3436
* In additional to the traditional singleton model, i.e. a key-value map whose lookups are constant
3537
* folded within generated code, we provide two additional options:
@@ -42,12 +44,13 @@
4244
* <li>{@link MultiLayeredImageSingleton}: {@link ImageSingletons#lookup} calls continue to refer to
4345
* the appropriate per layer image singleton, but there is also an additional method
4446
* {@link MultiLayeredImageSingleton#getAllLayers} which returns an array with the image singletons
45-
* corresponding to this key in all layers they were created. The length of this array will vary
46-
* from [0, total #layers], based on the number of layers singletons were installed in (i.e., it is
47-
* not required for the singleton to be installed in all layers). Within the array, the singletons
48-
* will be arranged so that index [0] corresponds to the singleton originating from the oldest layer
49-
* in which the singleton was installed and index [length - 1] holds the singleton from the newest
50-
* layer.</li>
47+
* corresponding to this key for all layers. The length of this array will always be the total
48+
* number of layers. If a singleton corresponding to this key was not installed in a given layer
49+
* (and this is allowed), then the array will contain null for the given index. See
50+
* {@link MultiLayeredAllowNullEntries} for more details. Within the array, the singletons will be
51+
* arranged so that index [0] corresponds to the singleton originating from the initial layer and
52+
* index [length - 1] holds the singleton from the application layer. See
53+
* {@link ImageLayerBuildingSupport} for a description of the different layer names.</li>
5154
* </ul>
5255
*
5356
* Note the unique behavior of {@link ApplicationLayerOnlyImageSingleton} applies only when building
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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.core.layeredimagesingleton;
26+
27+
import java.lang.annotation.ElementType;
28+
import java.lang.annotation.Retention;
29+
import java.lang.annotation.RetentionPolicy;
30+
import java.lang.annotation.Target;
31+
32+
/**
33+
* By default, it is expected a given {@link MultiLayeredImageSingleton} has a singleton installed
34+
* in each layer. If instead you want to make installation optional, you must add this annotation to
35+
* your class.
36+
*
37+
* Note this annotation is only meaningful when applied to {@link MultiLayeredImageSingleton}. In
38+
* all other scenarios it is ignored.
39+
*/
40+
@Retention(RetentionPolicy.RUNTIME)
41+
@Target(ElementType.TYPE)
42+
public @interface MultiLayeredAllowNullEntries {
43+
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ static <T extends MultiLayeredImageSingleton> T[] getAllLayers(Class<T> key) {
4141
}
4242

4343
/**
44-
* Retrieve a specific layer from a MultiLayeredImageSingleton. Note if a
45-
* MultiLayeredImageSingleton is not installed in all layers, then the singletons index will not
46-
* match the layer number it was installed in.
44+
* Retrieve a specific layer's singleton from a MultiLayeredImageSingleton. The index represents
45+
* which layer number's singleton to retrieve. If a singleton was not installed in that layer
46+
* (and this is allowed), then null is returned.
4747
*/
4848
@SuppressWarnings("unused")
4949
static <T extends MultiLayeredImageSingleton> T getForLayer(Class<T> key, int index) {

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

Lines changed: 60 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@
3535
import java.util.Map;
3636
import java.util.Objects;
3737
import java.util.concurrent.ConcurrentHashMap;
38+
import java.util.function.BiConsumer;
3839
import java.util.function.BiFunction;
3940
import java.util.function.Consumer;
40-
import java.util.stream.Stream;
4141

42+
import org.graalvm.nativeimage.AnnotationAccess;
4243
import org.graalvm.nativeimage.ImageSingletons;
4344
import org.graalvm.word.Pointer;
4445

@@ -54,6 +55,7 @@
5455
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
5556
import com.oracle.svm.core.feature.InternalFeature;
5657
import com.oracle.svm.core.graal.code.CGlobalDataInfo;
58+
import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo;
5759
import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
5860
import com.oracle.svm.core.imagelayer.LoadImageSingletonFactory;
5961
import com.oracle.svm.core.layeredimagesingleton.ApplicationLayerOnlyImageSingleton;
@@ -63,8 +65,10 @@
6365
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
6466
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
6567
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
68+
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredAllowNullEntries;
6669
import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton;
6770
import com.oracle.svm.core.layeredimagesingleton.UnsavedSingleton;
71+
import com.oracle.svm.core.util.UserError;
6872
import com.oracle.svm.core.util.VMError;
6973
import com.oracle.svm.hosted.FeatureImpl;
7074
import com.oracle.svm.hosted.c.CGlobalDataFeature;
@@ -74,6 +78,7 @@
7478
import com.oracle.svm.util.ReflectionUtil;
7579

7680
import jdk.graal.compiler.api.replacements.SnippetReflectionProvider;
81+
import jdk.graal.compiler.debug.GraalError;
7782
import jdk.graal.compiler.nodes.ConstantNode;
7883
import jdk.graal.compiler.nodes.ValueNode;
7984
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
@@ -166,6 +171,12 @@ public void duringSetup(DuringSetupAccess access) {
166171
LayeredImageHeapObjectAdder.singleton().registerObjectAdder(this::addInitialObjects);
167172
}
168173

174+
static void checkAllowNullEntries(Class<?> key) {
175+
boolean nullEntriesAllowed = AnnotationAccess.isAnnotationPresent(key, MultiLayeredAllowNullEntries.class);
176+
UserError.guarantee(nullEntriesAllowed,
177+
"This MultiLayeredSingleton requires an entry to be installed in every layer. Please see the javadoc within MultiLayeredAllowNullEntries for more details.");
178+
}
179+
169180
/**
170181
* This method needs to be called after all image singletons are registered. Currently, some
171182
* singletons are registered in
@@ -229,6 +240,8 @@ public void processRegisteredSingletons(AnalysisUniverse universe) {
229240
Class<?> key = slotInfo.keyClass();
230241
if (ImageSingletons.contains(key)) {
231242
multiLayerEmbeddedRoots.add(layeredImageSingletonSupport.runtimeLookup(key));
243+
} else {
244+
checkAllowNullEntries(key);
232245
}
233246
/*
234247
* Within the application layer there will be an array created to hold all
@@ -262,6 +275,9 @@ public void processRegisteredSingletons(AnalysisUniverse universe) {
262275
if (!multiLayerEmbeddedRoots.isEmpty()) {
263276
multiLayerEmbeddedRootsRegistration.accept(multiLayerEmbeddedRoots.toArray());
264277
}
278+
} else {
279+
// GR-58631
280+
throw GraalError.unimplemented("More work needed for 3+ layer support");
265281
}
266282
}
267283

@@ -273,15 +289,46 @@ public void beforeCompilation(BeforeCompilationAccess access) {
273289

274290
ImageHeapObjectArray createMultiLayerArray(Class<?> key, AnalysisType arrayType, SnippetReflectionProvider snippetReflectionProvider) {
275291
List<Integer> priorIds = getCrossLayerSingletonMappingInfo().getPriorLayerObjectIDs(key);
276-
Stream<JavaConstant> values = priorIds.stream().map(priorId -> loader.getOrCreateConstant(priorId));
292+
var layerInfo = DynamicImageLayerInfo.singleton();
293+
294+
final JavaConstant[] elements = new JavaConstant[layerInfo.numLayers];
295+
BiConsumer<JavaConstant, Integer> installElement = (priorConstant, index) -> {
296+
assert elements[index] == null : elements[index];
297+
elements[index] = priorConstant;
298+
};
277299

300+
// first install prior singletons
301+
priorIds.forEach(priorId -> {
302+
var priorConstant = loader.getOrCreateConstant(priorId);
303+
int index = 0;
304+
assert layerInfo.numLayers == 2 : "incompatible with 3+ layers";
305+
installElement.accept(priorConstant, index);
306+
});
307+
308+
// next install current singleton
278309
if (ImageSingletons.contains(key)) {
279310
var singleton = LayeredImageSingletonSupport.singleton().runtimeLookup(key);
280311
JavaConstant singletonConstant = snippetReflectionProvider.forObject(singleton);
281-
values = Stream.concat(values, Stream.of(singletonConstant));
312+
installElement.accept(singletonConstant, layerInfo.layerNumber);
313+
}
314+
315+
// finally fill any missing holes
316+
boolean holesPresent = false;
317+
for (int i = 0; i < layerInfo.numLayers; i++) {
318+
if (elements[i] == null) {
319+
holesPresent = true;
320+
installElement.accept(JavaConstant.NULL_POINTER, i);
321+
}
322+
}
323+
324+
if (holesPresent) {
325+
/*
326+
* Once more validate that null entries are allowed. However, any issues are expected to
327+
* be caught before this point.
328+
*/
329+
checkAllowNullEntries(key);
282330
}
283331

284-
Object[] elements = values.toArray();
285332
return ImageHeapObjectArray.createUnbackedImageHeapArray(arrayType, elements);
286333
}
287334

@@ -477,6 +524,15 @@ private LoadImageSingletonData getImageSingletonInfo(Class<?> keyClass, SlotReco
477524
return result;
478525
}
479526

527+
if (result.kind == SlotRecordKind.MULTI_LAYERED_SINGLETON) {
528+
if (!ImageSingletons.contains(keyClass)) {
529+
/*
530+
* If the singleton doesn't exist, ensure this is allowed.
531+
*/
532+
LoadImageSingletonFeature.checkAllowNullEntries(keyClass);
533+
}
534+
}
535+
480536
SlotInfo priorSlotInfo = priorKeyToSlotInfoMap.get(keyClass);
481537
if (priorSlotInfo != null && priorSlotInfo.recordKind() != kind) {
482538
VMError.shouldNotReachHere("A singleton cannot implement both %s and %s", priorSlotInfo.recordKind(), kind);

0 commit comments

Comments
 (0)