From 3dad2f25504ebfa5a6534047bcd10482742c8660 Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Wed, 2 Oct 2024 15:11:01 +0200 Subject: [PATCH] Collect all external values for Layered Image --- .../compiler/util/test/ObjectCopierTest.java | 8 +- .../hotspot/libgraal/CompilerConfig.java | 95 +--------- .../graal/compiler/nodes/EncodedGraph.java | 5 +- .../jdk/graal/compiler/util/ObjectCopier.java | 108 +++++++++++- .../graal/pointsto/heap/ImageLayerLoader.java | 32 ++-- .../pointsto/heap/ImageLayerSnapshotUtil.java | 98 +++-------- .../graal/pointsto/heap/ImageLayerWriter.java | 41 ++--- .../imagelayer/ImageLayerBuildingSupport.java | 18 +- .../svm/hosted/HostedConfiguration.java | 5 + .../svm/hosted/NativeImageGenerator.java | 28 ++- .../svm/hosted/heap/SVMImageLayerLoader.java | 29 ++- .../heap/SVMImageLayerSnapshotUtil.java | 166 ++++++++++-------- .../svm/hosted/heap/SVMImageLayerWriter.java | 2 +- .../HostedImageLayerBuildingSupport.java | 23 ++- .../oracle/svm/hosted/meta/HostedMethod.java | 9 - .../thread/LayeredVMThreadLocalCollector.java | 21 +-- .../hosted/thread/VMThreadLocalCollector.java | 30 +++- 17 files changed, 345 insertions(+), 373 deletions(-) diff --git a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java index 80566ffe769e..98b6513eafbb 100644 --- a/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java +++ b/compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/util/test/ObjectCopierTest.java @@ -35,11 +35,11 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import jdk.graal.compiler.core.test.SubprocessTest; import org.graalvm.collections.EconomicMap; import org.junit.Assert; import org.junit.Test; +import jdk.graal.compiler.core.test.SubprocessTest; import jdk.graal.compiler.util.ObjectCopier; /** @@ -172,10 +172,10 @@ public void testIt() { root.put("singleton2", TestObject.TEST_OBJECT_SINGLETON); root.put("singleton2_2", TestObject.TEST_OBJECT_SINGLETON); - List externalValues = List.of(ObjectCopier.getField(BaseClass.class, "BASE_SINGLETON"), + List externalValueFields = List.of(ObjectCopier.getField(BaseClass.class, "BASE_SINGLETON"), ObjectCopier.getField(TestObject.class, "TEST_OBJECT_SINGLETON")); - String encoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValues), root); + String encoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields), root); if (DEBUG) { System.out.printf("encoded:%n%s%n", encoded); } @@ -184,7 +184,7 @@ public void testIt() { System.out.printf("root:%n%s%n", root); System.out.printf("decoded:%n%s%n", decoded); } - String reencoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValues), decoded); + String reencoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValueFields), decoded); if (DEBUG) { System.out.printf("reencoded:%n%s%n", reencoded); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/CompilerConfig.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/CompilerConfig.java index 3f5178cc4f5a..28e61f20f7ea 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/CompilerConfig.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/libgraal/CompilerConfig.java @@ -24,37 +24,29 @@ */ package jdk.graal.compiler.hotspot.libgraal; -import java.io.IOException; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.net.URI; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.stream.Stream; -import jdk.graal.compiler.core.common.spi.ForeignCallSignature; -import jdk.graal.compiler.core.target.Backend; -import jdk.graal.compiler.hotspot.HotSpotForeignCallLinkage; -import jdk.graal.compiler.hotspot.meta.HotSpotHostForeignCallsProvider; -import jdk.graal.compiler.truffle.hotspot.HotSpotTruffleCompilerImpl; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; -import org.graalvm.word.LocationIdentity; +import jdk.graal.compiler.core.common.spi.ForeignCallSignature; +import jdk.graal.compiler.core.target.Backend; import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.hotspot.EncodedSnippets; +import jdk.graal.compiler.hotspot.HotSpotForeignCallLinkage; import jdk.graal.compiler.hotspot.HotSpotGraalCompiler; import jdk.graal.compiler.hotspot.HotSpotGraalRuntimeProvider; import jdk.graal.compiler.hotspot.HotSpotReplacementsImpl; import jdk.graal.compiler.hotspot.SymbolicSnippetEncoder; +import jdk.graal.compiler.hotspot.meta.HotSpotHostForeignCallsProvider; import jdk.graal.compiler.hotspot.meta.HotSpotProviders; import jdk.graal.compiler.options.OptionValues; +import jdk.graal.compiler.truffle.hotspot.HotSpotTruffleCompilerImpl; import jdk.graal.compiler.util.ObjectCopier; import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; @@ -84,13 +76,13 @@ public static void main(String[] args) throws Exception { List foreignCallSignatures = getForeignCallSignatures(replacements, options, graalRuntime); EncodedSnippets encodedSnippets = getEncodedSnippets(replacements, options); - List externalValues = getExternalValues(); + List externalValueFields = ObjectCopier.getExternalValueFields(); EconomicMap encodedObjects = EconomicMap.create(); encodedObjects.put("encodedSnippets", encodedSnippets); encodedObjects.put("foreignCallSignatures", foreignCallSignatures); - ObjectCopier.Encoder encoder = new ObjectCopier.Encoder(externalValues) { + ObjectCopier.Encoder encoder = new ObjectCopier.Encoder(externalValueFields) { @Override protected ClassInfo makeClassInfo(Class declaringClass) { ClassInfo ci = ClassInfo.of(declaringClass); @@ -152,77 +144,4 @@ private static void collectForeignCalls(HotSpotHostForeignCallsProvider foreignC } }); } - - private static List getExternalValues() throws IOException { - List externalValues = new ArrayList<>(); - addImmutableCollectionsFields(externalValues); - addStaticFinalObjectFields(LocationIdentity.class, externalValues); - - try (FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap())) { - for (String module : List.of("jdk.internal.vm.ci", "jdk.graal.compiler", "com.oracle.graal.graal_enterprise")) { - Path top = fs.getPath("/modules/" + module); - try (Stream files = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile())) { - files.forEach(p -> { - String fileName = p.getFileName().toString(); - if (fileName.endsWith(".class") && !fileName.equals("module-info.class")) { - // Strip module prefix and convert to dotted form - int nameCount = p.getNameCount(); - String className = p.subpath(2, nameCount).toString().replace('/', '.'); - // Strip ".class" suffix - className = className.replace('/', '.').substring(0, className.length() - ".class".length()); - try { - Class graalClass = Class.forName(className); - addStaticFinalObjectFields(graalClass, externalValues); - } catch (ClassNotFoundException e) { - throw new GraalError(e); - } - } - }); - } - } - } - return externalValues; - } - - /** - * Adds the static, final, non-primitive fields of non-enum {@code declaringClass} to - * {@code fields}. In the process, the fields are made {@linkplain Field#setAccessible - * accessible}. - */ - private static void addStaticFinalObjectFields(Class declaringClass, List fields) { - if (Enum.class.isAssignableFrom(declaringClass)) { - return; - } - for (Field field : declaringClass.getDeclaredFields()) { - int fieldModifiers = field.getModifiers(); - int fieldMask = Modifier.STATIC | Modifier.FINAL; - if ((fieldModifiers & fieldMask) != fieldMask) { - continue; - } - if (field.getType().isPrimitive()) { - continue; - } - field.setAccessible(true); - fields.add(field); - } - } - - /** - * Adds the EMPTY* fields from {@code java.util.ImmutableCollections} to {@code fields}, making - * them {@linkplain Field#setAccessible accessible} in the process. - */ - private static void addImmutableCollectionsFields(List fields) { - Class c = List.of().getClass().getDeclaringClass(); - GraalError.guarantee(c.getName().equals("java.util.ImmutableCollections"), "Incompatible ImmutableCollections class"); - for (Field f : c.getDeclaredFields()) { - if (f.getName().startsWith("EMPTY")) { - int modifiers = f.getModifiers(); - GraalError.guarantee(Modifier.isStatic(modifiers), "Expect %s to be static", f); - GraalError.guarantee(Modifier.isFinal(modifiers), "Expect %s to be final", f); - GraalError.guarantee(!f.getType().isPrimitive(), "Expect %s to be non-primitive", f); - f.setAccessible(true); - fields.add(f); - } - } - } } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/EncodedGraph.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/EncodedGraph.java index aedcafb6f507..37e4bf6baa70 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/EncodedGraph.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/EncodedGraph.java @@ -30,7 +30,6 @@ import jdk.graal.compiler.debug.GraalError; import jdk.graal.compiler.graph.Node; import jdk.graal.compiler.graph.NodeClass; - import jdk.vm.ci.meta.Assumptions; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -139,6 +138,10 @@ public Object getObject(int i) { return objects[i]; } + public void setObject(int i, Object object) { + objects[i] = object; + } + public NodeClass[] getNodeClasses() { return types; } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java index 9af28e1cde78..4be038275550 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/util/ObjectCopier.java @@ -25,11 +25,18 @@ package jdk.graal.compiler.util; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.io.PrintStream; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; @@ -785,16 +792,30 @@ public static class Encoder extends ObjectCopier { /** * Map from values to static final fields. In a serialized object graph, references to such - * values are encoded with a reference to the field. + * values are encoded using the static final field they come from. This field is then looked + * up via reflection when the value needs to be decoded. */ - final Map externalValues = new IdentityHashMap<>(); + final Map externalValues; - public Encoder(List externalValues) { + public Encoder(List externalValueFields) { + this(gatherExternalValues(externalValueFields)); + } + + /** + * Use precomputed {@code externalValues} to avoid recomputing them. + */ + public Encoder(Map externalValues) { objects.add(null); objectToId.put(null, new ObjectID(0, null)); - for (Field f : externalValues) { - addExternalValue(f); + this.externalValues = externalValues; + } + + public static Map gatherExternalValues(List externalValueFields) { + Map result = new IdentityHashMap<>(); + for (Field f : externalValueFields) { + addExternalValue(result, f); } + return result; } /** @@ -808,7 +829,7 @@ protected ClassInfo makeClassInfo(Class declaringClass) { return ClassInfo.of(declaringClass); } - private void addExternalValue(Field field) { + private static void addExternalValue(Map externalValues, Field field) { GraalError.guarantee(Modifier.isStatic(field.getModifiers()), "Field '%s' is not static. Only a static field can be used as known location for an instance.", field); Object value = readField(field, null); if (value == null) { @@ -825,7 +846,7 @@ private void addExternalValue(Field field) { } public Map getExternalValues() { - return externalValues; + return Collections.unmodifiableMap(externalValues); } private String encodeMap(UnmodifiableEconomicMap map) { @@ -1000,6 +1021,79 @@ public static Field getField(Class declaredClass, String fieldName) { } } + public static List getExternalValueFields() throws IOException { + List externalValues = new ArrayList<>(); + addImmutableCollectionsFields(externalValues); + addStaticFinalObjectFields(LocationIdentity.class, externalValues); + + try (FileSystem fs = FileSystems.newFileSystem(URI.create("jrt:/"), Collections.emptyMap())) { + for (String module : List.of("jdk.internal.vm.ci", "jdk.graal.compiler", "com.oracle.graal.graal_enterprise")) { + Path top = fs.getPath("/modules/" + module); + try (Stream files = Files.find(top, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile())) { + files.forEach(p -> { + String fileName = p.getFileName().toString(); + if (fileName.endsWith(".class") && !fileName.equals("module-info.class")) { + // Strip module prefix and convert to dotted form + int nameCount = p.getNameCount(); + String className = p.subpath(2, nameCount).toString().replace('/', '.'); + // Strip ".class" suffix + className = className.replace('/', '.').substring(0, className.length() - ".class".length()); + try { + Class graalClass = Class.forName(className); + addStaticFinalObjectFields(graalClass, externalValues); + } catch (ClassNotFoundException e) { + throw new GraalError(e); + } + } + }); + } + } + } + return externalValues; + } + + /** + * Adds the static, final, non-primitive fields of non-enum {@code declaringClass} to + * {@code fields}. In the process, the fields are made {@linkplain Field#setAccessible + * accessible}. + */ + public static void addStaticFinalObjectFields(Class declaringClass, List fields) { + if (Enum.class.isAssignableFrom(declaringClass)) { + return; + } + for (Field field : declaringClass.getDeclaredFields()) { + int fieldModifiers = field.getModifiers(); + int fieldMask = Modifier.STATIC | Modifier.FINAL; + if ((fieldModifiers & fieldMask) != fieldMask) { + continue; + } + if (field.getType().isPrimitive()) { + continue; + } + field.setAccessible(true); + fields.add(field); + } + } + + /** + * Adds the EMPTY* fields from {@code java.util.ImmutableCollections} to {@code fields}, making + * them {@linkplain Field#setAccessible accessible} in the process. + */ + private static void addImmutableCollectionsFields(List fields) { + Class c = List.of().getClass().getDeclaringClass(); + GraalError.guarantee(c.getName().equals("java.util.ImmutableCollections"), "Incompatible ImmutableCollections class"); + for (Field f : c.getDeclaredFields()) { + if (f.getName().startsWith("EMPTY")) { + int modifiers = f.getModifiers(); + GraalError.guarantee(Modifier.isStatic(modifiers), "Expect %s to be static", f); + GraalError.guarantee(Modifier.isFinal(modifiers), "Expect %s to be final", f); + GraalError.guarantee(!f.getType().isPrimitive(), "Expect %s to be non-primitive", f); + f.setAccessible(true); + fields.add(f); + } + } + } + /** * Describes the path from a root object to a target object. That is, the sequence of field and * array reads performed on the root object to access the target object. diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java index 9d841571cdab..ddf7b0bcb561 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoader.java @@ -323,7 +323,7 @@ record FieldIdentifier(String tid, String name) { } protected final Set> heapScannerTasks = ConcurrentHashMap.newKeySet(); - private final ImageLayerSnapshotUtil imageLayerSnapshotUtil; + private ImageLayerSnapshotUtil imageLayerSnapshotUtil; private ImageLayerLoaderHelper imageLayerLoaderHelper; protected final Map typeToConstant = new ConcurrentHashMap<>(); protected final Map stringToConstant = new ConcurrentHashMap<>(); @@ -343,14 +343,17 @@ public record FilePaths(Path snapshot, Path snapshotGraphs) { } public ImageLayerLoader() { - this(new ImageLayerSnapshotUtil(), List.of()); + this(List.of()); } - public ImageLayerLoader(ImageLayerSnapshotUtil imageLayerSnapshotUtil, List loadPaths) { - this.imageLayerSnapshotUtil = imageLayerSnapshotUtil; + public ImageLayerLoader(List loadPaths) { this.loadPaths = loadPaths; } + public void setImageLayerSnapshotUtil(ImageLayerSnapshotUtil imageLayerSnapshotUtil) { + this.imageLayerSnapshotUtil = imageLayerSnapshotUtil; + } + public AnalysisUniverse getUniverse() { return universe; } @@ -809,19 +812,12 @@ public AnalysisParsedGraph getAnalysisParsedGraph(AnalysisMethod analysisMethod) EconomicMap methodData = getMethodData(analysisMethod); String encodedAnalyzedGraph = readEncodedGraph(methodData, ANALYSIS_PARSED_GRAPH_TAG); Boolean intrinsic = get(methodData, INTRINSIC_TAG); - /* - * Methods without a persisted graph are folded and static methods. - * - * GR-55278: graphs that contain a reference to a $$Lambda cannot be persisted as well. - */ - if (encodedAnalyzedGraph != null) { - EncodedGraph analyzedGraph = (EncodedGraph) ObjectCopier.decode(imageLayerSnapshotUtil.getGraphDecoder(this, analysisMethod, universe.getSnippetReflection()), encodedAnalyzedGraph); - if (hasStrengthenedGraph(analysisMethod)) { - loadAllAnalysisElements(readEncodedGraph(methodData, STRENGTHENED_GRAPH_TAG)); - } - return new AnalysisParsedGraph(analyzedGraph, intrinsic); + EncodedGraph analyzedGraph = (EncodedGraph) ObjectCopier.decode(imageLayerSnapshotUtil.getGraphDecoder(this, analysisMethod, universe.getSnippetReflection()), encodedAnalyzedGraph); + if (hasStrengthenedGraph(analysisMethod)) { + loadAllAnalysisElements(readEncodedGraph(methodData, STRENGTHENED_GRAPH_TAG)); } - throw AnalysisError.shouldNotReachHere("The method " + analysisMethod + " does not have a graph from the base layer"); + afterGraphDecodeHook(analyzedGraph); + return new AnalysisParsedGraph(analyzedGraph, intrinsic); } private String readEncodedGraph(EconomicMap methodData, String elementIdentifier) { @@ -856,12 +852,12 @@ public void setStrengthenedGraph(AnalysisMethod analysisMethod) { EconomicMap methodData = getMethodData(analysisMethod); String encodedAnalyzedGraph = readEncodedGraph(methodData, STRENGTHENED_GRAPH_TAG); EncodedGraph analyzedGraph = (EncodedGraph) ObjectCopier.decode(imageLayerSnapshotUtil.getGraphDecoder(this, analysisMethod, universe.getSnippetReflection()), encodedAnalyzedGraph); - processGraph(analyzedGraph); + afterGraphDecodeHook(analyzedGraph); analysisMethod.setAnalyzedGraph(analyzedGraph); } @SuppressWarnings("unused") - protected void processGraph(EncodedGraph encodedGraph) { + protected void afterGraphDecodeHook(EncodedGraph encodedGraph) { } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java index 3d30d926ac03..91eb1ca10add 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerSnapshotUtil.java @@ -24,17 +24,13 @@ */ package com.oracle.graal.pointsto.heap; +import java.io.IOException; import java.lang.reflect.Executable; import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; -import java.util.Locale; +import java.util.Map; import java.util.Set; -import org.graalvm.word.LocationIdentity; - import com.oracle.graal.pointsto.infrastructure.OriginalMethodProvider; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; @@ -45,16 +41,11 @@ import com.oracle.graal.pointsto.meta.PointsToAnalysisType; import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.graal.pointsto.util.AnalysisFuture; -import com.oracle.svm.util.ModuleSupport; -import com.oracle.svm.util.ReflectionUtil; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; -import jdk.graal.compiler.graph.NodeClass; import jdk.graal.compiler.nodes.EncodedGraph; import jdk.graal.compiler.nodes.FieldLocationIdentity; -import jdk.graal.compiler.nodes.NamedLocationIdentity; import jdk.graal.compiler.util.ObjectCopier; -import jdk.vm.ci.meta.JavaKind; public class ImageLayerSnapshotUtil { public static final String FILE_NAME_PREFIX = "layer-snapshot-"; @@ -164,33 +155,28 @@ public class ImageLayerSnapshotUtil { public static final String IMAGE_SINGLETON_KEYS = "image singleton keys"; public static final String IMAGE_SINGLETON_OBJECTS = "image singleton objects"; - protected final List externalValues; + protected final List externalValueFields; + /** This needs to be initialized after analysis, as some fields are not available before. */ + protected Map externalValues; - @SuppressWarnings("this-escape") public ImageLayerSnapshotUtil() { - externalValues = new ArrayList<>(); - - addExternalValues(LocationIdentity.class); - addExternalValues(NamedLocationIdentity.class); - } - - protected void addExternalValues(Class clazz) { - Arrays.stream(clazz.getDeclaredFields()).filter(this::shouldAddExternalValue).forEach(this::addExternalValue); - } - - private void addExternalValue(Field f) { - ModuleSupport.accessModuleByClass(ModuleSupport.Access.OPEN, ImageLayerSnapshotUtil.class, f.getDeclaringClass()); - f.setAccessible(true); - externalValues.add(f); - } - - private boolean shouldAddExternalValue(Field f) { - Class type = f.getType(); - return Modifier.isStatic(f.getModifiers()) && shouldAddExternalValue(type); + try { + this.externalValueFields = ObjectCopier.getExternalValueFields(); + } catch (IOException e) { + throw AnalysisError.shouldNotReachHere("Unexpected exception when creating external value fields list", e); + } } - protected boolean shouldAddExternalValue(Class type) { - return LocationIdentity.class.isAssignableFrom(type); + /** + * Compute and cache the final {@code externalValues} map in + * {@link ImageLayerSnapshotUtil#externalValues} to avoid computing it for each graph. + *

+ * A single {@code ObjectCopier.Encoder} instance could alternatively be used for all graphs, + * but it would then be impossible to process multiple graphs concurrently. + */ + public void initializeExternalValues() { + assert externalValues == null : "The external values should be computed only once."; + externalValues = ObjectCopier.Encoder.gatherExternalValues(externalValueFields); } public static String snapshotFileName(String imageName) { @@ -240,15 +226,13 @@ public GraphDecoder getGraphDecoder(ImageLayerLoader imageLayerLoader, AnalysisM public static class GraphEncoder extends ObjectCopier.Encoder { @SuppressWarnings("this-escape") - public GraphEncoder(List externalValues, ImageLayerWriter imageLayerWriter) { + public GraphEncoder(Map externalValues, ImageLayerWriter imageLayerWriter) { super(externalValues); - addBuiltin(new NodeClassBuiltIn()); addBuiltin(new ImageHeapConstantBuiltIn(imageLayerWriter, null)); addBuiltin(new AnalysisTypeBuiltIn(imageLayerWriter, null)); addBuiltin(new AnalysisMethodBuiltIn(imageLayerWriter, null, null)); addBuiltin(new AnalysisFieldBuiltIn(imageLayerWriter, null)); addBuiltin(new FieldLocationIdentityBuiltIn(imageLayerWriter, null)); - addBuiltin(new NamedLocationIdentityArrayBuiltIn()); } } @@ -259,13 +243,11 @@ public static class GraphDecoder extends ObjectCopier.Decoder { public GraphDecoder(ClassLoader classLoader, ImageLayerLoader imageLayerLoader, AnalysisMethod analysisMethod) { super(classLoader); this.imageLayerLoader = imageLayerLoader; - addBuiltin(new NodeClassBuiltIn()); addBuiltin(new ImageHeapConstantBuiltIn(null, imageLayerLoader)); addBuiltin(new AnalysisTypeBuiltIn(null, imageLayerLoader)); addBuiltin(new AnalysisMethodBuiltIn(null, imageLayerLoader, analysisMethod)); addBuiltin(new AnalysisFieldBuiltIn(null, imageLayerLoader)); addBuiltin(new FieldLocationIdentityBuiltIn(null, imageLayerLoader)); - addBuiltin(new NamedLocationIdentityArrayBuiltIn()); } @Override @@ -274,23 +256,6 @@ public Class loadClass(String className) { } } - public static class NodeClassBuiltIn extends ObjectCopier.Builtin { - protected NodeClassBuiltIn() { - super(NodeClass.class); - } - - @Override - public String encode(ObjectCopier.Encoder encoder, Object obj) { - return ((NodeClass) obj).getClazz().getName(); - } - - @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - Class holder = ReflectionUtil.lookupClass(false, encoded); - return ReflectionUtil.readField(holder, "TYPE", null); - } - } - public static class ImageHeapConstantBuiltIn extends ObjectCopier.Builtin { private final ImageLayerWriter imageLayerWriter; private final ImageLayerLoader imageLayerLoader; @@ -430,25 +395,4 @@ private static String encodeField(AnalysisField field, ImageLayerWriter imageLay private static AnalysisField decodeField(ImageLayerLoader imageLayerLoader, String encoded) { return imageLayerLoader.getAnalysisField(Integer.parseInt(encoded)); } - - public static class NamedLocationIdentityArrayBuiltIn extends ObjectCopier.Builtin { - protected NamedLocationIdentityArrayBuiltIn() { - super(NamedLocationIdentity.class); - } - - @Override - public String encode(ObjectCopier.Encoder encoder, Object obj) { - NamedLocationIdentity namedLocationIdentity = (NamedLocationIdentity) obj; - AnalysisError.guarantee(NamedLocationIdentity.isArrayLocation(namedLocationIdentity), - "The named location identity %s should be encoded using an external value.", namedLocationIdentity); - String name = namedLocationIdentity.toString().split("Array: ")[1]; - /* Capitalizing the first letter gets the name of the Enum value */ - return name.substring(0, 1).toUpperCase(Locale.ROOT) + name.substring(1); - } - - @Override - protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - return NamedLocationIdentity.getArrayLocation(JavaKind.valueOf(encoded)); - } - } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java index a7915e360a78..483756d78f6c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriter.java @@ -90,7 +90,6 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TYPES_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.VALUE_TAG; -import static jdk.graal.compiler.java.LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING; import java.io.IOException; import java.io.PrintWriter; @@ -127,6 +126,7 @@ import com.oracle.svm.util.FileDumpingUtil; import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.java.LambdaUtils; import jdk.graal.compiler.nodes.EncodedGraph; import jdk.graal.compiler.nodes.spi.IdentityHashCodeProvider; import jdk.graal.compiler.util.ObjectCopier; @@ -140,7 +140,7 @@ public class ImageLayerWriter { static final Charset GRAPHS_CHARSET = Charset.defaultCharset(); - protected final ImageLayerSnapshotUtil imageLayerSnapshotUtil; + protected ImageLayerSnapshotUtil imageLayerSnapshotUtil; private ImageLayerWriterHelper imageLayerWriterHelper; private ImageHeap imageHeap; protected AnalysisUniverse aUniverse; @@ -203,13 +203,12 @@ void finish() { } public ImageLayerWriter() { - this(true, new ImageLayerSnapshotUtil()); + this(true); } @SuppressWarnings({"this-escape", "unused"}) - public ImageLayerWriter(boolean useSharedLayerGraphs, ImageLayerSnapshotUtil imageLayerSnapshotUtil) { + public ImageLayerWriter(boolean useSharedLayerGraphs) { this.useSharedLayerGraphs = useSharedLayerGraphs; - this.imageLayerSnapshotUtil = imageLayerSnapshotUtil; this.jsonMap = EconomicMap.create(); this.constantsToRelink = new ArrayList<>(); this.persistedTypeIds = ConcurrentHashMap.newKeySet(); @@ -220,6 +219,10 @@ public ImageLayerWriter(boolean useSharedLayerGraphs, ImageLayerSnapshotUtil ima this.constantsMap = new ConcurrentHashMap<>(); } + public void setImageLayerSnapshotUtil(ImageLayerSnapshotUtil imageLayerSnapshotUtil) { + this.imageLayerSnapshotUtil = imageLayerSnapshotUtil; + } + public void setInternedStringsIdentityMap(IdentityHashMap map) { this.internedStringsIdentityMap = map; } @@ -257,6 +260,10 @@ public void dumpFiles() { }); } + public void initializeExternalValues() { + imageLayerSnapshotUtil.initializeExternalValues(); + } + public void persistImageHeapSize(long imageHeapSize) { jsonMap.put(IMAGE_HEAP_SIZE_TAG, String.valueOf(imageHeapSize)); } @@ -449,7 +456,7 @@ public void persistAnalysisParsedGraph(AnalysisMethod method) { Object analyzedGraph = method.getGraph(); if (analyzedGraph instanceof AnalysisParsedGraph analysisParsedGraph) { if (!methodMap.containsKey(INTRINSIC_TAG)) { - if (!persistGraph(analysisParsedGraph.getEncodedGraph(), methodMap, ANALYSIS_PARSED_GRAPH_TAG)) { + if (!persistGraph(method, analysisParsedGraph.getEncodedGraph(), methodMap, ANALYSIS_PARSED_GRAPH_TAG)) { return; } methodMap.put(INTRINSIC_TAG, analysisParsedGraph.isIntrinsic()); @@ -462,22 +469,17 @@ public void persistMethodStrengthenedGraph(AnalysisMethod method) { if (!methodMap.containsKey(STRENGTHENED_GRAPH_TAG)) { EncodedGraph analyzedGraph = method.getAnalyzedGraph(); - persistGraph(analyzedGraph, methodMap, STRENGTHENED_GRAPH_TAG); + persistGraph(method, analyzedGraph, methodMap, STRENGTHENED_GRAPH_TAG); } } - private boolean persistGraph(EncodedGraph analyzedGraph, EconomicMap methodMap, String graphTag) { + private boolean persistGraph(AnalysisMethod method, EncodedGraph analyzedGraph, EconomicMap methodMap, String graphTag) { if (!useSharedLayerGraphs) { return false; } String encodedGraph = ObjectCopier.encode(imageLayerSnapshotUtil.getGraphEncoder(this), analyzedGraph); - /* - * The ObjectCopier cannot look up Lambda types by reflection, so it cannot decode a graph - * that contains a reference to a Lambda. Since the original Class is needed, the analysis - * id cannot be used either. - */ - if (encodedGraph.contains(LAMBDA_CLASS_NAME_SUBSTRING)) { - return false; + if (encodedGraph.contains(LambdaUtils.LAMBDA_CLASS_NAME_SUBSTRING)) { + throw AnalysisError.shouldNotReachHere("The graph for the method %s contains a reference to a lambda type, which cannot be decoded: %s".formatted(method, encodedGraph)); } String location = graphsOutput.add(encodedGraph); methodMap.put(graphTag, location); @@ -648,13 +650,4 @@ private static Object getPrimitiveConstantValue(PrimitiveConstant primitiveConst protected boolean delegateProcessing(List> data, Object constant) { return false; } - - public boolean persistedMethodGraph(AnalysisMethod method) { - String name = imageLayerSnapshotUtil.getMethodIdentifier(method); - if (methodsMap.containsKey(name)) { - EconomicMap methodMap = methodsMap.get(name); - return methodMap.get(ANALYSIS_PARSED_GRAPH_TAG) != null || methodMap.get(STRENGTHENED_GRAPH_TAG) != null; - } - return false; - } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/ImageLayerBuildingSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/ImageLayerBuildingSupport.java index d0b1001dddf6..d9de1f1dbbaa 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/ImageLayerBuildingSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/ImageLayerBuildingSupport.java @@ -62,7 +62,7 @@ * Note this is intentionally not a LayeredImageSingleton itself to prevent circular dependencies. */ public abstract class ImageLayerBuildingSupport { - protected final boolean buildingImageLayer; + public final boolean buildingImageLayer; private final boolean buildingInitialLayer; private final boolean buildingApplicationLayer; @@ -70,30 +70,16 @@ protected ImageLayerBuildingSupport(boolean buildingImageLayer, boolean building this.buildingImageLayer = buildingImageLayer; this.buildingInitialLayer = buildingInitialLayer; this.buildingApplicationLayer = buildingApplicationLayer; - - if (buildingImageLayer) { - openModules(); - } - } /** * To allow the {@link ObjectCopier} to access private fields by reflection, some modules needs * to be opened when a layer is built. */ - private static void openModules() { + public static void openModules() { ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "java.base", "java.lang"); ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "java.base", "java.util"); ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "java.base", "java.util.concurrent"); - ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "org.graalvm.nativeimage.builder", "com.oracle.svm.core.c"); - ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "org.graalvm.nativeimage.builder", "com.oracle.svm.core.c.function"); - ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "org.graalvm.nativeimage.builder", "com.oracle.svm.core.c.struct"); - ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "org.graalvm.nativeimage.builder", "com.oracle.svm.core.graal.code"); - ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "org.graalvm.nativeimage.builder", "com.oracle.svm.core.graal.stackvalue"); - ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "org.graalvm.nativeimage.builder", "com.oracle.svm.core.snippets"); - ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "org.graalvm.nativeimage.builder", "com.oracle.svm.core.threadlocal"); - ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "org.graalvm.nativeimage.builder", "com.oracle.svm.hosted.imagelayer"); - ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, "org.graalvm.nativeimage.builder", "com.oracle.svm.hosted.meta"); } private static ImageLayerBuildingSupport singleton() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java index a7a60d0bd807..7528d8f1e8e5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedConfiguration.java @@ -65,6 +65,7 @@ import com.oracle.svm.hosted.config.HybridLayout; import com.oracle.svm.hosted.config.HybridLayoutSupport; import com.oracle.svm.hosted.heap.SVMImageLayerLoaderHelper; +import com.oracle.svm.hosted.heap.SVMImageLayerSnapshotUtil; import com.oracle.svm.hosted.heap.SVMImageLayerWriterHelper; import com.oracle.svm.hosted.image.LIRNativeImageCodeCache; import com.oracle.svm.hosted.image.NativeImageCodeCache; @@ -241,6 +242,10 @@ public SVMImageLayerLoaderHelper createSVMImageLayerLoaderHelper() { return new SVMImageLayerLoaderHelper(HostedImageLayerBuildingSupport.singleton().getLoader()); } + public SVMImageLayerSnapshotUtil createSVMImageLayerSnapshotUtil(ImageClassLoader imageClassLoader) { + return new SVMImageLayerSnapshotUtil(imageClassLoader); + } + public CompileQueue createCompileQueue(DebugContext debug, FeatureHandler featureHandler, HostedUniverse hostedUniverse, RuntimeConfiguration runtimeConfiguration, boolean deoptimizeAll) { return new CompileQueue(debug, featureHandler, hostedUniverse, runtimeConfiguration, deoptimizeAll, Collections.emptyList()); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java index 2e0d358f289d..2b6428215d6c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java @@ -98,6 +98,7 @@ import com.oracle.graal.pointsto.heap.HostedValuesProvider; import com.oracle.graal.pointsto.heap.ImageHeap; import com.oracle.graal.pointsto.heap.ImageHeapScanner; +import com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil; import com.oracle.graal.pointsto.heap.ImageLayerWriter; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; import com.oracle.graal.pointsto.infrastructure.WrappedJavaMethod; @@ -233,6 +234,7 @@ import com.oracle.svm.hosted.heap.SVMImageHeapScanner; import com.oracle.svm.hosted.heap.SVMImageHeapVerifier; import com.oracle.svm.hosted.heap.SVMImageLayerLoader; +import com.oracle.svm.hosted.heap.SVMImageLayerWriter; import com.oracle.svm.hosted.image.AbstractImage; import com.oracle.svm.hosted.image.AbstractImage.NativeImageKind; import com.oracle.svm.hosted.image.NativeImageCodeCache; @@ -530,12 +532,8 @@ public void run(Map entryPoints, setSystemPropertiesForImageLate(k); var hostedOptionValues = new HostedOptionValues(optionProvider.getHostedValues()); - HostedImageLayerBuildingSupport imageLayerSupport = HostedImageLayerBuildingSupport.initialize(hostedOptionValues); - SVMImageLayerLoader imageLayerLoader = imageLayerSupport.getLoader(); - if (imageLayerLoader != null) { - imageLayerLoader.setImageClassLoader(loader); - } - ImageSingletonsSupportImpl.HostedManagement.install(new ImageSingletonsSupportImpl.HostedManagement(imageLayerLoader != null || imageLayerSupport.getWriter() != null), imageLayerSupport); + HostedImageLayerBuildingSupport imageLayerSupport = HostedImageLayerBuildingSupport.initialize(hostedOptionValues, loader); + ImageSingletonsSupportImpl.HostedManagement.install(new ImageSingletonsSupportImpl.HostedManagement(imageLayerSupport.buildingImageLayer), imageLayerSupport); ImageSingletons.add(LayeredImageSingletonSupport.class, (LayeredImageSingletonSupport) ImageSingletonsSupportImpl.get()); ImageSingletons.add(ProgressReporter.class, reporter); @@ -613,6 +611,10 @@ protected void doRun(Map entryPoints, JavaMainSupport j BeforeUniverseBuildingAccessImpl beforeUniverseBuildingConfig = new BeforeUniverseBuildingAccessImpl(featureHandler, loader, debug, hMetaAccess); featureHandler.forEachFeature(feature -> feature.beforeUniverseBuilding(beforeUniverseBuildingConfig)); + if (ImageLayerBuildingSupport.buildingSharedLayer()) { + HostedImageLayerBuildingSupport.singleton().getWriter().initializeExternalValues(); + } + new UniverseBuilder(aUniverse, bb.getMetaAccess(), hUniverse, hMetaAccess, HostedConfiguration.instance().createStrengthenGraphs(bb, hUniverse), bb.getUnsupportedFeatures()).build(debug); @@ -963,13 +965,21 @@ protected void setupNativeImage(OptionValues options, Map loadPaths) { - super(new SVMImageLayerSnapshotUtil(), loadPaths); + public SVMImageLayerLoader(List loadPaths, ImageClassLoader imageClassLoader) { + super(loadPaths); dynamicHubArrayHubField = ReflectionUtil.lookupField(DynamicHub.class, "arrayHub"); + this.imageClassLoader = imageClassLoader; } public void setHostedUniverse(HostedUniverse hostedUniverse) { this.hostedUniverse = hostedUniverse; } - public void setImageClassLoader(ImageClassLoader imageClassLoader) { - this.imageClassLoader = imageClassLoader; - } - public HostedUniverse getHostedUniverse() { return hostedUniverse; } @@ -197,13 +192,11 @@ protected void initializeBaseLayerMethod(AnalysisMethod analysisMethod, Economic } @Override - protected void processGraph(EncodedGraph encodedGraph) { - super.processGraph(encodedGraph); - Object[] objects = encodedGraph.getObjects(); - for (Object object : objects) { - if (object instanceof VMThreadLocalInfo vmThreadLocalInfo) { - LayeredVMThreadLocalCollector layeredVMThreadLocalCollector = (LayeredVMThreadLocalCollector) ImageSingletons.lookup(VMThreadLocalCollector.class); - layeredVMThreadLocalCollector.registerPriorThreadLocalInfo(vmThreadLocalInfo); + protected void afterGraphDecodeHook(EncodedGraph encodedGraph) { + super.afterGraphDecodeHook(encodedGraph); + for (int i = 0; i < encodedGraph.getNumObjects(); ++i) { + if (encodedGraph.getObject(i) instanceof CGlobalDataInfo cGlobalDataInfo) { + encodedGraph.setObject(i, CGlobalDataFeature.singleton().registerAsAccessedOrGet(cGlobalDataInfo.getData())); } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java index a829d10a6a27..48d646d25d74 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerSnapshotUtil.java @@ -31,13 +31,15 @@ import java.lang.reflect.Executable; import java.lang.reflect.Field; +import java.net.URI; +import java.net.URISyntaxException; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import org.graalvm.nativeimage.ImageSingletons; + import com.oracle.graal.pointsto.heap.ImageLayerLoader; import com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil; import com.oracle.graal.pointsto.heap.ImageLayerWriter; @@ -45,35 +47,16 @@ import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.svm.core.SubstrateDiagnostics; +import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.svm.core.c.struct.CInterfaceLocationIdentity; -import com.oracle.svm.core.deopt.DeoptimizationCounters; -import com.oracle.svm.core.graal.snippets.DeoptTester; -import com.oracle.svm.core.graal.snippets.StackOverflowCheckImpl; -import com.oracle.svm.core.graal.snippets.SubstrateAllocationSnippets; -import com.oracle.svm.core.heap.NoAllocationVerifier; import com.oracle.svm.core.hub.DynamicHub; -import com.oracle.svm.core.identityhashcode.IdentityHashCodeSupport; -import com.oracle.svm.core.jfr.JfrThreadLocal; -import com.oracle.svm.core.jfr.events.JfrAllocationEvents; -import com.oracle.svm.core.jfr.events.ThreadCPULoadEvent; -import com.oracle.svm.core.jfr.sampler.AbstractJfrExecutionSampler; -import com.oracle.svm.core.jni.JNIObjectHandles; -import com.oracle.svm.core.jni.JNIThreadLocalEnvironment; -import com.oracle.svm.core.jni.JNIThreadLocalPendingException; -import com.oracle.svm.core.jni.JNIThreadLocalPrimitiveArrayViews; -import com.oracle.svm.core.jni.JNIThreadOwnedMonitors; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.reflect.serialize.SerializationSupport; -import com.oracle.svm.core.snippets.ExceptionUnwind; -import com.oracle.svm.core.snippets.ImplicitExceptions; -import com.oracle.svm.core.stack.JavaFrameAnchors; -import com.oracle.svm.core.thread.JavaThreads; -import com.oracle.svm.core.thread.PlatformThreads; -import com.oracle.svm.core.thread.Safepoint; -import com.oracle.svm.core.thread.ThreadingSupportImpl; -import com.oracle.svm.core.thread.VMThreads; import com.oracle.svm.core.threadlocal.FastThreadLocal; +import com.oracle.svm.core.threadlocal.VMThreadLocalInfo; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.ImageClassLoader; +import com.oracle.svm.hosted.VMFeature; import com.oracle.svm.hosted.code.FactoryMethod; import com.oracle.svm.hosted.code.IncompatibleClassChangeFallbackMethod; import com.oracle.svm.hosted.meta.HostedArrayClass; @@ -81,6 +64,8 @@ import com.oracle.svm.hosted.meta.HostedMethod; import com.oracle.svm.hosted.meta.HostedSnippetReflectionProvider; import com.oracle.svm.hosted.meta.HostedType; +import com.oracle.svm.hosted.thread.VMThreadLocalCollector; +import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; @@ -104,46 +89,59 @@ public class SVMImageLayerSnapshotUtil extends ImageLayerSnapshotUtil { protected static final Set dynamicHubRelinkedFields = Set.of(companion, classInitializationInfo, name, superHub, componentType, arrayHub); protected final Map> fieldsToRelink = new HashMap<>(); + private final ImageClassLoader imageClassLoader; @SuppressWarnings("this-escape") - public SVMImageLayerSnapshotUtil() { + public SVMImageLayerSnapshotUtil(ImageClassLoader imageClassLoader) { super(); - addExternalValues(DeoptTester.class); - addExternalValues(JfrThreadLocal.class); - addExternalValues(JfrAllocationEvents.class); - addExternalValues(AbstractJfrExecutionSampler.class); - addExternalValues(ThreadCPULoadEvent.class); - addExternalValues(JNIObjectHandles.class); - addExternalValues(JNIThreadLocalPendingException.class); - addExternalValues(JNIThreadLocalPrimitiveArrayViews.class); - addExternalValues(JNIThreadOwnedMonitors.class); - addExternalValues(JNIThreadLocalEnvironment.class); - addExternalValues(ExceptionUnwind.class); - addExternalValues(JavaThreads.class); - addExternalValues(ImplicitExceptions.class); - addExternalValues(IdentityHashCodeSupport.class); - addExternalValues(StackOverflowCheckImpl.class); - addExternalValues(CInterfaceLocationIdentity.class); - addExternalValues(JavaFrameAnchors.class); - addExternalValues(PlatformThreads.class); - addExternalValues(VMThreads.class); - addExternalValues(VMThreads.StatusSupport.class); - addExternalValues(VMThreads.SafepointBehavior.class); - addExternalValues(VMThreads.ActionOnTransitionToJavaSupport.class); - addExternalValues(Safepoint.class); - addExternalValues(NoAllocationVerifier.class); - addExternalValues(ThreadingSupportImpl.class); - addExternalValues(ThreadingSupportImpl.RecurringCallbackTimer.class); - addExternalValues(DeoptimizationCounters.class); - addExternalValues(Objects.requireNonNull(ReflectionUtil.lookupClass(false, "com.oracle.svm.core.genscavenge.ThreadLocalAllocation"))); - addExternalValues(Objects.requireNonNull(ReflectionUtil.lookupClass(false, "com.oracle.svm.core.genscavenge.graal.BarrierSnippets"))); - addExternalValues(SubstrateDiagnostics.class); - addExternalValues(SubstrateAllocationSnippets.class); + this.imageClassLoader = imageClassLoader; + addSVMExternalValueFields(); } - @Override - protected boolean shouldAddExternalValue(Class type) { - return FastThreadLocal.class.isAssignableFrom(type) || super.shouldAddExternalValue(type); + /** + * Gets the externalValues (like {@link ObjectCopier#getExternalValueFields()}) of classes from + * the SVM core classes. + */ + private void addSVMExternalValueFields() { + for (URI svmURI : getBuilderLocations()) { + for (String className : imageClassLoader.classLoaderSupport.classes(svmURI)) { + try { + Class clazz = imageClassLoader.forName(className); + + String packageName = clazz.getPackageName(); + if (!shouldScanPackage(packageName)) { + continue; + } + + /* The ObjectCopier needs to access the static fields by reflection */ + Module module = clazz.getModule(); + ModuleSupport.accessPackagesToClass(ModuleSupport.Access.OPEN, ObjectCopier.class, false, module.getName(), packageName); + + ObjectCopier.addStaticFinalObjectFields(clazz, externalValueFields); + } catch (ClassNotFoundException e) { + throw AnalysisError.shouldNotReachHere("The class %s from the modulePath %s was not found".formatted(className, svmURI.getPath()), e); + } + } + } + } + + protected Set getBuilderLocations() { + try { + Class vmFeatureClass = ImageSingletons.lookup(VMFeature.class).getClass(); + URI svmURI = VMFeature.class.getProtectionDomain().getCodeSource().getLocation().toURI(); + if (vmFeatureClass == VMFeature.class) { + return Set.of(svmURI); + } else { + return Set.of(svmURI, vmFeatureClass.getProtectionDomain().getCodeSource().getLocation().toURI()); + } + } catch (URISyntaxException e) { + throw VMError.shouldNotReachHere("Error when trying to get SVM URI", e); + } + } + + @SuppressWarnings("unused") + protected boolean shouldScanPackage(String packageName) { + return true; } @Override @@ -232,7 +230,7 @@ public GraphDecoder getGraphDecoder(ImageLayerLoader imageLayerLoader, AnalysisM public static class SVMGraphEncoder extends GraphEncoder { @SuppressWarnings("this-escape") - public SVMGraphEncoder(List externalValues, ImageLayerWriter imageLayerWriter) { + public SVMGraphEncoder(Map externalValues, ImageLayerWriter imageLayerWriter) { super(externalValues, imageLayerWriter); addBuiltin(new HostedTypeBuiltIn(null)); addBuiltin(new HostedMethodBuiltIn(null)); @@ -240,6 +238,7 @@ public SVMGraphEncoder(List externalValues, ImageLayerWriter imageLayerWr addBuiltin(new HostedSnippetReflectionProviderBuiltIn(null)); addBuiltin(new CInterfaceLocationIdentityBuiltIn()); addBuiltin(new FastThreadLocalLocationIdentityBuiltIn()); + addBuiltin(new VMThreadLocalInfoBuiltIn()); } } @@ -253,6 +252,7 @@ public SVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader svmImageLaye addBuiltin(new HostedSnippetReflectionProviderBuiltIn(snippetReflectionProvider)); addBuiltin(new CInterfaceLocationIdentityBuiltIn()); addBuiltin(new FastThreadLocalLocationIdentityBuiltIn()); + addBuiltin(new VMThreadLocalInfoBuiltIn()); } } @@ -357,18 +357,46 @@ protected FastThreadLocalLocationIdentityBuiltIn() { protected String encode(ObjectCopier.Encoder encoder, Object obj) { FastThreadLocal.FastThreadLocalLocationIdentity fastThreadLocalLocationIdentity = (FastThreadLocal.FastThreadLocalLocationIdentity) obj; FastThreadLocal fastThreadLocal = ReflectionUtil.readField(FastThreadLocal.FastThreadLocalLocationIdentity.class, "this$0", fastThreadLocalLocationIdentity); - Field staticField = encoder.getExternalValues().get(fastThreadLocal); - return staticField.getDeclaringClass().getName() + ":" + staticField.getName(); + return encodeStaticField(encoder, fastThreadLocal); } @Override protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { - String[] fieldParts = encoded.split(":"); - String className = fieldParts[0]; - String fieldName = fieldParts[1]; - Class declaringClass = ReflectionUtil.lookupClass(false, className); - FastThreadLocal fastThreadLocal = ReflectionUtil.readStaticField(declaringClass, fieldName); + FastThreadLocal fastThreadLocal = getObjectFromStaticField(encoded); return fastThreadLocal.getLocationIdentity(); } } + + public static class VMThreadLocalInfoBuiltIn extends ObjectCopier.Builtin { + protected VMThreadLocalInfoBuiltIn() { + super(VMThreadLocalInfo.class); + } + + @Override + protected String encode(ObjectCopier.Encoder encoder, Object obj) { + VMThreadLocalInfo vmThreadLocalInfo = (VMThreadLocalInfo) obj; + VMThreadLocalCollector vmThreadLocalCollector = ImageSingletons.lookup(VMThreadLocalCollector.class); + FastThreadLocal fastThreadLocal = vmThreadLocalCollector.getThreadLocal(vmThreadLocalInfo); + return encodeStaticField(encoder, fastThreadLocal); + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + FastThreadLocal fastThreadLocal = getObjectFromStaticField(encoded); + return ImageSingletons.lookup(VMThreadLocalCollector.class).forFastThreadLocal(fastThreadLocal); + } + } + + private static String encodeStaticField(ObjectCopier.Encoder encoder, Object object) { + Field staticField = encoder.getExternalValues().get(object); + return staticField.getDeclaringClass().getName() + ":" + staticField.getName(); + } + + private static T getObjectFromStaticField(String staticField) { + String[] fieldParts = staticField.split(":"); + String className = fieldParts[0]; + String fieldName = fieldParts[1]; + Class declaringClass = ReflectionUtil.lookupClass(false, className); + return ReflectionUtil.readStaticField(declaringClass, fieldName); + } } 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 a2094f61ec04..e49d2a6bbeed 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 @@ -101,7 +101,7 @@ public class SVMImageLayerWriter extends ImageLayerWriter { private HostedUniverse hUniverse; public SVMImageLayerWriter(boolean useSharedLayerGraphs) { - super(useSharedLayerGraphs, new SVMImageLayerSnapshotUtil()); + super(useSharedLayerGraphs); } public void setNativeImageHeap(NativeImageHeap nativeImageHeap) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java index b773606d3811..5b3a405c4a5c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedImageLayerBuildingSupport.java @@ -53,6 +53,7 @@ import com.oracle.svm.core.util.ArchiveSupport; import com.oracle.svm.core.util.UserError; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.NativeImageGenerator; import com.oracle.svm.hosted.c.NativeLibraries; import com.oracle.svm.hosted.heap.SVMImageLayerLoader; @@ -164,27 +165,35 @@ private static boolean isLayerOptionEnabled(HostedOptionKey threadLocalAssignmentMap; - - /** - * With the inlining of prior layer methods, it is possible for VMThreadLocalsInfos from prior - * layers to exist in graphs. We must record these instances so that they can have offsets - * assigned. In addition, now it is possible for MultipleThreadLocals to exist which are - * assigned to the same offset; however, this does not affect correctness, as all offsets are - * assigned in the initial layer and during lowering this discrepancy disappears. - */ - final Set priorLayerThreadLocals; private final boolean initialLayer; private int nextOffset; @@ -83,14 +71,9 @@ private LayeredVMThreadLocalCollector(Map threadLocalAssignm this.threadLocalAssignmentMap = threadLocalAssignmentMap; initialLayer = ImageLayerBuildingSupport.buildingInitialLayer(); - this.priorLayerThreadLocals = initialLayer ? null : ConcurrentHashMap.newKeySet(); this.nextOffset = nextOffset; } - public void registerPriorThreadLocalInfo(VMThreadLocalInfo info) { - priorLayerThreadLocals.add(info); - } - @Override public Object apply(Object source) { /* @@ -114,12 +97,12 @@ public int sortAndAssignOffsets() { } else { assert nextOffset != -1; - Stream.concat(priorLayerThreadLocals.stream(), threadLocals.values().stream()).forEach(info -> { + for (VMThreadLocalInfo info : threadLocals.values()) { var assignment = threadLocalAssignmentMap.get(info.name); info.offset = assignment.offset(); assert assignment.size() == calculateSize(info) : Assertions.errorMessage("Mismatch in computed size: ", assignment.size(), calculateSize(info), info.name); info.sizeInBytes = assignment.size(); - }); + } } return nextOffset; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadLocalCollector.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadLocalCollector.java index 28d26cc2229d..ea19d54a6206 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadLocalCollector.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/VMThreadLocalCollector.java @@ -62,6 +62,7 @@ public static class Options { } Map threadLocals; + Map infoToThreadLocals; private boolean sealed; final boolean validateUniqueNames; final Set seenNames; @@ -78,16 +79,21 @@ protected VMThreadLocalCollector(boolean validateUniqueNames) { public void installThreadLocalMap() { assert threadLocals == null : threadLocals; threadLocals = ObservableImageHeapMapProvider.create(); + infoToThreadLocals = new ConcurrentHashMap<>(); } - @Override - public Object apply(Object source) { - if (source instanceof FastThreadLocal threadLocal) { + public VMThreadLocalInfo forFastThreadLocal(FastThreadLocal threadLocal) { + VMThreadLocalInfo localInfo = threadLocals.get(threadLocal); + if (localInfo == null) { if (sealed) { - assert threadLocals.containsKey(threadLocal) : "VMThreadLocal must have been discovered during static analysis"; + throw VMError.shouldNotReachHere("VMThreadLocal must have been discovered during static analysis"); } else { - var previous = threadLocals.putIfAbsent(threadLocal, new VMThreadLocalInfo(threadLocal)); - if (previous == null && validateUniqueNames) { + VMThreadLocalInfo newInfo = new VMThreadLocalInfo(threadLocal); + localInfo = threadLocals.computeIfAbsent(threadLocal, tl -> { + infoToThreadLocals.putIfAbsent(newInfo, threadLocal); + return newInfo; + }); + if (localInfo == newInfo && validateUniqueNames) { /* * Ensure this name is unique. */ @@ -95,6 +101,14 @@ public Object apply(Object source) { } } } + return localInfo; + } + + @Override + public Object apply(Object source) { + if (source instanceof FastThreadLocal fastThreadLocal) { + forFastThreadLocal(fastThreadLocal); + } /* * We want to collect all instances without actually replacing them, so we always return the * source object. @@ -118,6 +132,10 @@ public VMThreadLocalInfo findInfo(GraphBuilderContext b, ValueNode threadLocalNo return result; } + public FastThreadLocal getThreadLocal(VMThreadLocalInfo vmThreadLocalInfo) { + return infoToThreadLocals.get(vmThreadLocalInfo); + } + protected static int calculateSize(VMThreadLocalInfo info) { if (info.sizeSupplier != null) { int unalignedSize = info.sizeSupplier.getAsInt();