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 2e21e3fd5430..2ea055c6f21f 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 @@ -42,6 +42,10 @@ public final class DynamicHubSupport { @UnknownPrimitiveField(availability = AfterHostedUniverse.class) private int maxTypeId; @UnknownObjectField(availability = AfterHostedUniverse.class) private byte[] referenceMapEncoding; + public static DynamicHubSupport singleton() { + return ImageSingletons.lookup(DynamicHubSupport.class); + } + @Platforms(Platform.HOSTED_ONLY.class) public DynamicHubSupport() { } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/ImageSingletonLoader.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/ImageSingletonLoader.java index 1871acb5d89a..aeeafd0db123 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/ImageSingletonLoader.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/ImageSingletonLoader.java @@ -24,6 +24,10 @@ */ package com.oracle.svm.core.layeredimagesingleton; +import java.util.List; + public interface ImageSingletonLoader { int readInt(String keyName); + + List readIntList(String keyName); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/ImageSingletonWriter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/ImageSingletonWriter.java index 72406c993f86..8b19b6042798 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/ImageSingletonWriter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/ImageSingletonWriter.java @@ -24,6 +24,10 @@ */ package com.oracle.svm.core.layeredimagesingleton; +import java.util.List; + public interface ImageSingletonWriter { void writeInt(String keyName, int value); + + void writeIntList(String keyName, List value); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/LoadedLayeredImageSingletonInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/LoadedLayeredImageSingletonInfo.java index be3458377b99..e352a954db11 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/LoadedLayeredImageSingletonInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/layeredimagesingleton/LoadedLayeredImageSingletonInfo.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.core.layeredimagesingleton; +import org.graalvm.nativeimage.ImageSingletons; + import java.util.Set; public class LoadedLayeredImageSingletonInfo { @@ -37,4 +39,8 @@ public LoadedLayeredImageSingletonInfo(Set> loadedKeys) { public boolean handledDuringLoading(Class key) { return loadedKeys.contains(key); } + + public static LoadedLayeredImageSingletonInfo singleton() { + return ImageSingletons.lookup(LoadedLayeredImageSingletonInfo.class); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java index 4c49898d044e..16967c3a7671 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/OpenTypeWorldFeature.java @@ -24,21 +24,44 @@ */ package com.oracle.svm.hosted; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.hosted.Feature; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.hub.DynamicHub; +import com.oracle.svm.core.hub.DynamicHubSupport; +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader; +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; +import com.oracle.svm.core.layeredimagesingleton.LoadedLayeredImageSingletonInfo; import com.oracle.svm.hosted.meta.HostedType; +import jdk.graal.compiler.debug.Assertions; + @AutomaticallyRegisteredFeature public class OpenTypeWorldFeature implements InternalFeature { @Override public boolean isInConfiguration(Feature.IsInConfigurationAccess access) { return !SubstrateOptions.closedTypeWorld(); + } + @Override + public void beforeUniverseBuilding(BeforeUniverseBuildingAccess access) { + if (SVMImageLayerSupport.singleton().persistImageSingletons() && !LoadedLayeredImageSingletonInfo.singleton().handledDuringLoading(LayerTypeInfo.class)) { + ImageSingletons.add(LayerTypeInfo.class, new LayerTypeInfo()); + } } @Override @@ -49,4 +72,136 @@ public void beforeCompilation(BeforeCompilationAccess access) { impl.registerAsImmutable(hub.getOpenTypeWorldTypeCheckSlots()); } } + + public static int loadTypeInfo(Collection types) { + if (ImageSingletons.contains(LayerTypeInfo.class) && SVMImageLayerSupport.singleton().loadAnalysis()) { + /* + * Load analysis must be enabled or otherwise the same Analysis Type id will not be + * reassigned across layers. + */ + return ImageSingletons.lookup(LayerTypeInfo.class).loadTypeID(types); + } + + return 0; + } + + public static void persistTypeInfo(Collection types) { + if (ImageSingletons.contains(LayerTypeInfo.class)) { + ImageSingletons.lookup(LayerTypeInfo.class).persistTypeInfo(types); + } + } + + record TypeInfo(int typeID, int numClassTypes, int numInterfaceTypes, int[] typecheckSlots) { + private List toIntList() { + ArrayList list = new ArrayList<>(); + list.add(typeID); + list.add(numClassTypes); + list.add(numInterfaceTypes); + Arrays.stream(typecheckSlots).forEach(list::add); + + return list; + } + + private static TypeInfo fromIntList(List list) { + int typeID = list.get(0); + int numClassTypes = list.get(1); + int numInterfaceTypes = list.get(2); + int[] typecheckSlots = list.subList(3, list.size()).stream().mapToInt(i -> i).toArray(); + return new TypeInfo(typeID, numClassTypes, numInterfaceTypes, typecheckSlots); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TypeInfo typeInfo = (TypeInfo) o; + return typeID == typeInfo.typeID && numClassTypes == typeInfo.numClassTypes && numInterfaceTypes == typeInfo.numInterfaceTypes && Arrays.equals(typecheckSlots, typeInfo.typecheckSlots); + } + + @Override + public int hashCode() { + int result = Objects.hash(typeID, numClassTypes, numInterfaceTypes); + result = 31 * result + Arrays.hashCode(typecheckSlots); + return result; + } + } + + private static class LayerTypeInfo implements LayeredImageSingleton { + Map identifierToTypeInfo = new HashMap<>(); + int maxTypeID = 0; + + public int loadTypeID(Collection types) { + ArrayList usedIDs = new ArrayList<>(); + for (HostedType type : types) { + int identifierID = type.getWrapped().getId(); + TypeInfo info = identifierToTypeInfo.get(identifierID); + if (info != null) { + usedIDs.add(info.typeID); + type.loadTypeID(info.typeID); + } + } + + return maxTypeID; + } + + public void persistTypeInfo(Collection types) { + for (HostedType type : types) { + if (type.getTypeID() != -1) { + int identifierID = type.getWrapped().getId(); + int typeID = type.getTypeID(); + int numClassTypes = type.getNumClassTypes(); + int numInterfaceTypes = type.getNumInterfaceTypes(); + int[] typecheckSlots = type.getOpenTypeWorldTypeCheckSlots(); + var priorInfo = identifierToTypeInfo.get(identifierID); + var newTypeInfo = new TypeInfo(typeID, numClassTypes, numInterfaceTypes, typecheckSlots); + if (priorInfo == null) { + identifierToTypeInfo.put(identifierID, newTypeInfo); + } else { + assert newTypeInfo.equals(priorInfo) : Assertions.errorMessage("Mismatch for ", type, priorInfo, newTypeInfo, Arrays.toString(priorInfo.typecheckSlots), + Arrays.toString(newTypeInfo.typecheckSlots)); + } + } + } + } + + @Override + public EnumSet getImageBuilderFlags() { + return EnumSet.of(ImageBuilderFlags.BUILDTIME_ACCESS); + } + + @Override + public PersistFlags preparePersist(ImageSingletonWriter writer) { + /* + * Note all that is strictly needed to restore the typecheck information is the + * (identifierID -> typeID) mappings. In the future we can compact the amount of + * information we store. + */ + var identifierIDs = identifierToTypeInfo.keySet().stream().sorted().toList(); + writer.writeIntList("identifierIDs", identifierIDs); + writer.writeInt("maxTypeID", DynamicHubSupport.singleton().getMaxTypeId()); + + for (int identifierID : identifierIDs) { + var typeInfo = identifierToTypeInfo.get(identifierID); + writer.writeIntList(Integer.toString(identifierID), typeInfo.toIntList()); + } + + return PersistFlags.CREATE; + } + + @SuppressWarnings("unused") + public static Object createFromLoader(ImageSingletonLoader loader) { + var info = new LayerTypeInfo(); + info.maxTypeID = loader.readInt("maxTypeID"); + List identifierIDs = loader.readIntList("identifierIDs"); + for (var identifierID : identifierIDs) { + var previous = info.identifierToTypeInfo.put(identifierID, TypeInfo.fromIntList(loader.readIntList(Integer.toString(identifierID)))); + assert previous == null : previous; + } + return info; + } + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java index 4daba6c174b5..7dcfd68257ff 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoader.java @@ -260,4 +260,12 @@ public int readInt(String keyName) { assert type.equals("I") : type; return cast(value.get(1)); } + + @Override + public List readIntList(String keyName) { + List value = cast(keyStore.get(keyName)); + String type = cast(value.get(0)); + assert type.equals("I[]") : type; + return cast(value.get(1)); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java index 0a04be4d83a9..45fd833124cb 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriter.java @@ -250,4 +250,9 @@ EconomicMap getKeyValueStore() { public void writeInt(String keyName, int value) { keyValueStore.put(keyName, List.of("I", value)); } + + @Override + public void writeIntList(String keyName, List value) { + keyValueStore.put(keyName, List.of("I[]", value)); + } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrEventFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrEventFeature.java index 0d3c2273a6fc..0202d1a210f5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrEventFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jfr/JfrEventFeature.java @@ -80,7 +80,7 @@ public void duringSetup(DuringSetupAccess c) { @Override public void beforeCompilation(BeforeCompilationAccess a) { // Reserve slot 0 for error-catcher. - int mapSize = ImageSingletons.lookup(DynamicHubSupport.class).getMaxTypeId() + 1; + int mapSize = DynamicHubSupport.singleton().getMaxTypeId() + 1; // Create trace-ID map with fixed size. ImageSingletons.lookup(JfrTraceIdMap.class).initialize(mapSize); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java index eaa383586b1e..30effeff03d4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedType.java @@ -59,6 +59,7 @@ public abstract class HostedType extends HostedElement implements SharedType, Wr protected HostedType[] subTypes; protected HostedField[] staticFields; + boolean loadedFromPriorLayer; protected int typeID; protected HostedType uniqueConcreteImplementation; protected HostedMethod[] allDeclaredMethods; @@ -263,6 +264,11 @@ public HostedType getUniqueConcreteImplementation() { return uniqueConcreteImplementation; } + public void loadTypeID(int newTypeID) { + this.typeID = newTypeID; + this.loadedFromPriorLayer = true; + } + @Override public DynamicHub getHub() { return universe.hostVM().dynamicHub(wrapped); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java index 558328314ea7..969065b49435 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/TypeCheckBuilder.java @@ -39,12 +39,11 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiFunction; -import org.graalvm.nativeimage.ImageSingletons; - import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.graal.snippets.OpenTypeWorldSnippets; import com.oracle.svm.core.hub.DynamicHubSupport; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.OpenTypeWorldFeature; import jdk.graal.compiler.core.common.calc.UnsignedMath; @@ -157,12 +156,15 @@ public final class TypeCheckBuilder { public static int buildTypeMetadata(HostedUniverse hUniverse, Collection types, HostedType objectType, HostedType cloneableType, HostedType serializableType) { var builder = new TypeCheckBuilder(types, objectType, cloneableType, serializableType); - builder.buildTypeInformation(hUniverse); if (SubstrateOptions.closedTypeWorld()) { + builder.buildTypeInformation(hUniverse, 0); builder.calculateClosedTypeWorldTypeMetadata(); return builder.getNumTypeCheckSlots(); } else { + int startingTypeID = OpenTypeWorldFeature.loadTypeInfo(builder.heightOrderedTypes); + builder.buildTypeInformation(hUniverse, startingTypeID); builder.calculateOpenTypeWorldTypeMetadata(); + OpenTypeWorldFeature.persistTypeInfo(builder.heightOrderedTypes); return UNINITIALIZED_TYPECHECK_SLOTS; } } @@ -447,18 +449,22 @@ private HostedType getHighestDimArrayType(HostedType type, int dimMax) { * * The stamps are calculated by performing a dataflow analysis of the {@link #subtypeMap}. */ - public void buildTypeInformation(HostedUniverse hUniverse) { + public void buildTypeInformation(HostedUniverse hUniverse, int startingTypeID) { hUniverse.orderedTypes = heightOrderedTypes; - ImageSingletons.lookup(DynamicHubSupport.class).setMaxTypeId(heightOrderedTypes.size()); - for (int i = 0; i < heightOrderedTypes.size(); i++) { - HostedType type = heightOrderedTypes.get(i); - boolean uninitialized = type.typeID == -1 && type.subTypes == null; - VMError.guarantee(uninitialized, "Type initialized multiple times: %s", type); - type.typeID = i; + int nextTypeID = startingTypeID; + for (HostedType type : heightOrderedTypes) { + if (type.typeID != -1) { + assert type.loadedFromPriorLayer && type.typeID < startingTypeID : "Type initialized multiple times: " + type; + } else { + type.typeID = nextTypeID++; + } + VMError.guarantee(type.subTypes == null, "Type initialized multiple times: %s", type); type.subTypes = subtypeMap.get(type).toArray(HostedType.EMPTY_ARRAY); } + DynamicHubSupport.singleton().setMaxTypeId(nextTypeID); + /* * Search through list in reverse order so that all of a type's subtypes are traversed * before itself. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index b727c3492de8..a81bb94ab0e5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -44,7 +44,6 @@ import java.util.function.Function; import java.util.stream.Collectors; -import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.c.function.CEntryPointLiteral; import org.graalvm.nativeimage.c.function.CFunction; @@ -94,6 +93,7 @@ import com.oracle.svm.hosted.NativeImageOptions; import com.oracle.svm.hosted.ameta.FieldValueInterceptionSupport; import com.oracle.svm.hosted.annotation.CustomSubstitutionMethod; +import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import com.oracle.svm.hosted.config.DynamicHubLayout; import com.oracle.svm.hosted.config.HybridLayout; import com.oracle.svm.hosted.heap.PodSupport; @@ -856,7 +856,7 @@ private void buildHubs() { referenceMaps.put(type, referenceMap); referenceMapEncoder.add(referenceMap); } - ImageSingletons.lookup(DynamicHubSupport.class).setReferenceMapEncoding(referenceMapEncoder.encodeAll()); + DynamicHubSupport.singleton().setReferenceMapEncoding(referenceMapEncoder.encodeAll()); ObjectLayout ol = ConfigurationValues.getObjectLayout(); DynamicHubLayout dynamicHubLayout = DynamicHubLayout.singleton();