Skip to content

[GR-61421] Allow InitialLayerOnlyImageSingletons to be folded within future layers. #10642

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,11 @@ static ConcurrentHashMap<String, String> getInternedStrings() {
return ImageSingletons.lookup(RuntimeInternedStrings.class).internedStrings;
}

@Override
public boolean accessibleInFutureLayers() {
return true;
}

@Override
public EnumSet<LayeredImageSingletonBuilderFlags> getImageBuilderFlags() {
return LayeredImageSingletonBuilderFlags.RUNTIME_ACCESS_ONLY;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
*/
package com.oracle.svm.core.layeredimagesingleton;

import org.graalvm.nativeimage.ImageSingletons;

import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.util.VMError;

Expand All @@ -34,4 +36,14 @@ default PersistFlags preparePersist(ImageSingletonWriter writer) {
VMError.guarantee(ImageLayerBuildingSupport.buildingInitialLayer(), "This singleton should only be installed in the initial layer");
return PersistFlags.FORBIDDEN;
}

/**
* When true is returned, runtime code within future layers can call
* {@link ImageSingletons#lookup} and the singleton will be constant folded into the code. In
* the future layers it will still not be possible to call {@link ImageSingletons#lookup} at
* build time.
*/
default boolean accessibleInFutureLayers() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import jdk.vm.ci.meta.JavaConstant;

@Platforms(Platform.HOSTED_ONLY.class)
public interface LayeredImageSingletonSupport {

Expand All @@ -47,5 +49,9 @@ static LayeredImageSingletonSupport singleton() {

Collection<Class<?>> getMultiLayeredImageSingletonKeys();

void freezeMultiLayeredImageSingletons();
Collection<Class<?>> getFutureLayerAccessibleImageSingletonKeys();

void freezeLayeredImageSingletonMetadata();

JavaConstant getInitialLayerOnlyImageSingleton(Class<?> key);
}
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ struct ImageSingletonKey {
keyClassName @0 :Text;
persistFlag @1 :Int32;
objectId @2 :SingletonObjId;
constantId @3 :ConstantId;
}

struct ImageSingletonObject {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@

import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport;
import com.oracle.svm.core.layeredimagesingleton.ApplicationLayerOnlyImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton.PersistFlags;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
Expand All @@ -47,6 +48,8 @@
import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport;
import com.oracle.svm.hosted.imagelayer.SVMImageLayerSingletonLoader;

import jdk.vm.ci.meta.JavaConstant;

public final class ImageSingletonsSupportImpl extends ImageSingletonsSupport implements LayeredImageSingletonSupport {

@Override
Expand All @@ -70,8 +73,19 @@ public Collection<Class<?>> getMultiLayeredImageSingletonKeys() {
}

@Override
public void freezeMultiLayeredImageSingletons() {
HostedManagement.getAndAssertExists().freezeMultiLayeredImageSingletons();
public Collection<Class<?>> getFutureLayerAccessibleImageSingletonKeys() {
return HostedManagement.getAndAssertExists().getFutureLayerAccessibleImageSingletonKeys();
}

@Override
public void freezeLayeredImageSingletonMetadata() {
HostedManagement.getAndAssertExists().freezeLayeredImageSingletonMetadata();
}

@Override
public JavaConstant getInitialLayerOnlyImageSingleton(Class<?> key) {
var loader = HostedImageLayerBuildingSupport.singleton().getSingletonLoader();
return loader.loadInitialLayerOnlyImageSingleton(key);
}

@Override
Expand Down Expand Up @@ -181,6 +195,7 @@ public static void persist() {
private final Map<Class<?>, Object> configObjects;
private final boolean checkUnsupported;
private Set<Class<?>> multiLayeredImageSingletonKeys;
private Set<Class<?>> futureLayerAccessibleImageSingletonKeys;

public HostedManagement() {
this(false);
Expand All @@ -189,6 +204,7 @@ public HostedManagement() {
public HostedManagement(boolean checkUnsupported) {
this.configObjects = new ConcurrentHashMap<>();
this.multiLayeredImageSingletonKeys = ConcurrentHashMap.newKeySet();
this.futureLayerAccessibleImageSingletonKeys = ConcurrentHashMap.newKeySet();
this.checkUnsupported = checkUnsupported;
}

Expand Down Expand Up @@ -229,6 +245,10 @@ private void doAddInternal(Class<?> key, Object value) {
}
}

if (singleton instanceof InitialLayerOnlyImageSingleton initial && initial.accessibleInFutureLayers()) {
futureLayerAccessibleImageSingletonKeys.add(key);
}

if (!singleton.getImageBuilderFlags().contains(LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS)) {
storedValue = new RuntimeOnlyWrapper(singleton);
}
Expand All @@ -245,8 +265,13 @@ Collection<Class<?>> getMultiLayeredImageSingletonKeys() {
return multiLayeredImageSingletonKeys;
}

void freezeMultiLayeredImageSingletons() {
Collection<Class<?>> getFutureLayerAccessibleImageSingletonKeys() {
return futureLayerAccessibleImageSingletonKeys;
}

void freezeLayeredImageSingletonMetadata() {
multiLayeredImageSingletonKeys = Set.copyOf(multiLayeredImageSingletonKeys);
futureLayerAccessibleImageSingletonKeys = Set.copyOf(futureLayerAccessibleImageSingletonKeys);
}

<T> T doLookup(Class<T> key, boolean stripRuntimeOnly, boolean allowMultiLayered) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ public void processRegisteredSingletons(AnalysisUniverse universe) {
loader = (SVMImageLayerLoader) universe.getImageLayerLoader();

LayeredImageSingletonSupport layeredImageSingletonSupport = LayeredImageSingletonSupport.singleton();
layeredImageSingletonSupport.freezeMultiLayeredImageSingletons();
layeredImageSingletonSupport.freezeLayeredImageSingletonMetadata();

Consumer<Object[]> multiLayerEmbeddedRootsRegistration = (objArray) -> {
var method = metaAccess.lookupJavaMethod(ReflectionUtil.lookupMethod(MultiLayeredImageSingleton.class, "getAllLayers", Class.class));
Expand All @@ -211,6 +211,15 @@ public void processRegisteredSingletons(AnalysisUniverse universe) {
if (multiLayeredSingletons.length != 0) {
multiLayerEmbeddedRootsRegistration.accept(multiLayeredSingletons);
}

/*
* Make sure all image singletons accessible in future layers are persisted.
*/
for (Class<?> key : layeredImageSingletonSupport.getFutureLayerAccessibleImageSingletonKeys()) {
var singleton = layeredImageSingletonSupport.lookup(key, true, false);
ImageHeapConstant constant = (ImageHeapConstant) universe.getSnippetReflection().forObject(singleton);
SVMImageLayerSnapshotUtil.forcePersistConstant(constant);
}
}

if (ImageLayerBuildingSupport.buildingInitialLayer()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,28 +41,30 @@
import org.graalvm.collections.UnmodifiableEconomicMap;

import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader;
import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton.PersistFlags;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.ImageSingletonKey;
import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.ImageSingletonObject;
import com.oracle.svm.hosted.imagelayer.SharedLayerSnapshotCapnProtoSchemaHolder.KeyStoreEntry;
import com.oracle.svm.util.ReflectionUtil;

import jdk.vm.ci.meta.JavaConstant;

public class SVMImageLayerSingletonLoader {
private final HostedImageLayerBuildingSupport imageLayerBuildingSupport;
private final SharedLayerSnapshotCapnProtoSchemaHolder.SharedLayerSnapshot.Reader snapshot;
private Map<Class<?>, Integer> initialLayerOnlySingletonConstantIds;

public SVMImageLayerSingletonLoader(HostedImageLayerBuildingSupport imageLayerBuildingSupport, SharedLayerSnapshotCapnProtoSchemaHolder.SharedLayerSnapshot.Reader snapshot) {
this.imageLayerBuildingSupport = imageLayerBuildingSupport;
this.snapshot = snapshot;
}

public Map<Object, Set<Class<?>>> loadImageSingletons(Object forbiddenObject) {
return loadImageSingletons0(forbiddenObject);
}

private Map<Object, Set<Class<?>>> loadImageSingletons0(Object forbiddenObject) {
Map<Integer, Object> idToObjectMap = new HashMap<>();
Map<Class<?>, Integer> initialLayerKeyToIdMap = new HashMap<>();
for (ImageSingletonObject.Reader obj : snapshot.getSingletonObjects()) {
String className = obj.getClassName().toString();

Expand Down Expand Up @@ -110,15 +112,31 @@ private Map<Object, Set<Class<?>>> loadImageSingletons0(Object forbiddenObject)
Class<?> clazz = imageLayerBuildingSupport.lookupClass(false, className);
singletonInitializationMap.computeIfAbsent(forbiddenObject, (k) -> new HashSet<>());
singletonInitializationMap.get(forbiddenObject).add(clazz);
if (InitialLayerOnlyImageSingleton.class.isAssignableFrom(clazz)) {
int constantId = entry.getConstantId();
if (constantId != -1) {
initialLayerKeyToIdMap.put(clazz, constantId);
}
}
} else {
assert persistInfo == PersistFlags.NOTHING : "Unexpected PersistFlags value: " + persistInfo;
assert id == -1 : "Unrestored image singleton should not be linked to an object";
}
}

initialLayerOnlySingletonConstantIds = Map.copyOf(initialLayerKeyToIdMap);

return singletonInitializationMap;
}

public JavaConstant loadInitialLayerOnlyImageSingleton(Class<?> key) {
int constantId = initialLayerOnlySingletonConstantIds.getOrDefault(key, -1);
if (constantId != -1) {
return imageLayerBuildingSupport.getLoader().getOrCreateConstant(constantId);
}
throw UserError.abort("Unable to load InitialLayerOnlyImageSingleton: %s. Please override accessibleInFutureLayers if you want this singleton to be reachable in future layers.", key);
}

public Class<?> lookupClass(boolean optional, String className) {
return imageLayerBuildingSupport.lookupClass(optional, className);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,19 @@ private static String getQualifiedName(AnalysisMethod method) {
return method.getSignature().getReturnType().toJavaName(true) + " " + method.getQualifiedName();
}

public static void forcePersistConstant(ImageHeapConstant imageHeapConstant) {
AnalysisUniverse universe = imageHeapConstant.getType().getUniverse();
universe.getHeapScanner().markReachable(imageHeapConstant, ObjectScanner.OtherReason.PERSISTED);

imageHeapConstant.getType().registerAsTrackedAcrossLayers(imageHeapConstant);
/* If this is a Class constant persist the corresponding type. */
ConstantReflectionProvider constantReflection = universe.getBigbang().getConstantReflectionProvider();
AnalysisType typeFromClassConstant = (AnalysisType) constantReflection.asJavaType(imageHeapConstant);
if (typeFromClassConstant != null) {
typeFromClassConstant.registerAsTrackedAcrossLayers(imageHeapConstant);
}
}

public static class SVMGraphEncoder extends ObjectCopier.Encoder {
@SuppressWarnings("this-escape")
public SVMGraphEncoder(Map<Object, Field> externalValues) {
Expand Down Expand Up @@ -407,18 +420,7 @@ protected ImageHeapConstantBuiltIn(SVMImageLayerLoader imageLayerLoader) {
@Override
public void encode(ObjectCopier.Encoder encoder, ObjectCopierOutputStream stream, Object obj) throws IOException {
ImageHeapConstant imageHeapConstant = (ImageHeapConstant) obj;

AnalysisUniverse universe = imageHeapConstant.getType().getUniverse();
universe.getHeapScanner().markReachable(imageHeapConstant, ObjectScanner.OtherReason.PERSISTED);

imageHeapConstant.getType().registerAsTrackedAcrossLayers(imageHeapConstant);
/* If this is a Class constant persist the corresponding type. */
ConstantReflectionProvider constantReflection = universe.getBigbang().getConstantReflectionProvider();
AnalysisType typeFromClassConstant = (AnalysisType) constantReflection.asJavaType(imageHeapConstant);
if (typeFromClassConstant != null) {
typeFromClassConstant.registerAsTrackedAcrossLayers(imageHeapConstant);
}

forcePersistConstant(imageHeapConstant);
stream.writePackedUnsignedInt(ImageHeapConstant.getConstantID(imageHeapConstant));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@
import com.oracle.svm.core.classinitialization.ClassInitializationInfo;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter;
import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.RuntimeOnlyWrapper;
import com.oracle.svm.core.meta.MethodPointer;
Expand Down Expand Up @@ -179,6 +180,7 @@ public class SVMImageLayerWriter extends ImageLayerWriter {
private final SharedLayerSnapshot.Builder snapshotBuilder = this.snapshotFileBuilder.initRoot(SharedLayerSnapshot.factory);
private Map<ImageHeapConstant, ConstantParent> constantsMap;
private final Map<String, MethodGraphsInfo> methodsMap = new ConcurrentHashMap<>();
private final Map<InitialLayerOnlyImageSingleton, Integer> initialLayerOnlySingletonMap = new ConcurrentHashMap<>();
private FileInfo fileInfo;
private GraphsOutput graphsOutput;
private final boolean useSharedLayerGraphs;
Expand Down Expand Up @@ -740,6 +742,13 @@ private void persistConstant(ImageHeapConstant imageHeapConstant, ConstantParent
int identityHashCode = identityHashCodeProvider.identityHashCode(imageHeapConstant);
builder.setIdentityHashCode(identityHashCode);

if (imageHeapConstant.isBackedByHostedObject() && InitialLayerOnlyImageSingleton.class.isAssignableFrom(type.getJavaClass())) {
InitialLayerOnlyImageSingleton singleton = aUniverse.getBigbang().getSnippetReflectionProvider().asObject(InitialLayerOnlyImageSingleton.class, imageHeapConstant.getHostedObject());
if (singleton.accessibleInFutureLayers()) {
initialLayerOnlySingletonMap.put(singleton, id);
}
}

switch (imageHeapConstant) {
case ImageHeapInstance imageHeapInstance -> {
builder.initObject().setInstance(Void.VOID);
Expand Down Expand Up @@ -994,6 +1003,11 @@ public void writeImageSingletonInfo(List<Map.Entry<Class<?>, Object>> layeredIma
sb.setKeyClassName(key);
sb.setObjectId(info.id);
sb.setPersistFlag(info.flags.ordinal());
int constantId = -1;
if (singleton instanceof InitialLayerOnlyImageSingleton initialLayerOnlyImageSingleton && initialLayerOnlyImageSingleton.accessibleInFutureLayers()) {
constantId = initialLayerOnlySingletonMap.getOrDefault(initialLayerOnlyImageSingleton, -1);
}
sb.setConstantId(constantId);
}

var sortedByIDs = singletonInfoMap.entrySet().stream()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3622,7 +3622,7 @@ public enum Which {


public static class ImageSingletonKey {
public static final org.capnproto.StructSize STRUCT_SIZE = new org.capnproto.StructSize((short)1,(short)1);
public static final org.capnproto.StructSize STRUCT_SIZE = new org.capnproto.StructSize((short)2,(short)1);
public static final class Factory extends org.capnproto.StructFactory<Builder, Reader> {
public Factory() {
}
Expand Down Expand Up @@ -3678,6 +3678,13 @@ public final void setObjectId(int value) {
_setIntField(1, value);
}

public final int getConstantId() {
return _getIntField(2);
}
public final void setConstantId(int value) {
_setIntField(2, value);
}

}

public static final class Reader extends org.capnproto.StructReader {
Expand All @@ -3700,6 +3707,10 @@ public final int getObjectId() {
return _getIntField(1);
}

public final int getConstantId() {
return _getIntField(2);
}

}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
import com.oracle.svm.core.imagelayer.LoadImageSingletonFactory;
import com.oracle.svm.core.jdk.proxy.DynamicProxyRegistry;
import com.oracle.svm.core.layeredimagesingleton.ApplicationLayerOnlyImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags;
import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonSupport;
Expand Down Expand Up @@ -1161,6 +1162,16 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
return true;
}

if (InitialLayerOnlyImageSingleton.class.isAssignableFrom(key) && ImageLayerBuildingSupport.buildingExtensionLayer()) {
/*
* This singleton is only installed in the initial layer heap. When allowed, all
* other layers lookups refer to this singleton.
*/
JavaConstant initialSingleton = LayeredImageSingletonSupport.singleton().getInitialLayerOnlyImageSingleton(key);
b.addPush(JavaKind.Object, ConstantNode.forConstant(initialSingleton, b.getMetaAccess(), b.getGraph()));
return true;
}

Object singleton = LayeredImageSingletonSupport.singleton().lookup(key, true, true);
if (singleton instanceof LayeredImageSingleton layeredSingleton) {
if (!layeredSingleton.getImageBuilderFlags().contains(LayeredImageSingletonBuilderFlags.RUNTIME_ACCESS)) {
Expand Down