From 9cf446f5e3d01a4d33df856e7fac39cdc4993551 Mon Sep 17 00:00:00 2001 From: Sacha Coppey Date: Fri, 15 Mar 2024 18:47:01 +0100 Subject: [PATCH] Persist and reload graphs after analysis and after strengthen graphs Co-authored-by: Doug Simon --- .../compiler/util/test/ObjectCopierTest.java | 4 +- .../hotspot/SymbolicSnippetEncoder.java | 3 +- .../hotspot/guestgraal/CompilerConfig.java | 14 +- .../replacements/SnippetTemplate.java | 8 +- .../jdk/graal/compiler/util/ObjectCopier.java | 86 ++-- .../StandalonePointsToAnalysis.java | 4 +- .../pointsto/flow/AnalysisParsedGraph.java | 2 +- .../graal/pointsto/heap/ImageLayerLoader.java | 414 +++++++++++++++--- .../pointsto/heap/ImageLayerLoaderHelper.java | 40 ++ .../pointsto/heap/ImageLayerSnapshotUtil.java | 296 +++++++++++++ .../graal/pointsto/heap/ImageLayerWriter.java | 310 +++++++++---- .../pointsto/heap/ImageLayerWriterHelper.java | 42 ++ .../graal/pointsto/meta/AnalysisMethod.java | 40 +- .../graal/pointsto/meta/AnalysisType.java | 12 + .../graal/pointsto/meta/AnalysisUniverse.java | 14 +- .../graal/pointsto/meta/BaseLayerElement.java | 42 ++ .../graal/pointsto/meta/BaseLayerField.java | 111 +++++ .../graal/pointsto/meta/BaseLayerMethod.java | 63 ++- .../graal/pointsto/meta/BaseLayerType.java | 5 +- .../pointsto/results/StrengthenGraphs.java | 11 + .../com/oracle/svm/core/SubstrateOptions.java | 9 + .../imagelayer/ImageLayerBuildingSupport.java | 31 +- .../svm/core/threadlocal/FastThreadLocal.java | 2 +- .../RuntimeCompiledMethodSupport.java | 2 +- .../svm/hosted/HostedConfiguration.java | 12 + .../svm/hosted/NativeImageGenerator.java | 28 +- .../svm/hosted/OpenTypeWorldFeature.java | 4 +- .../svm/hosted/SubstrateStrengthenGraphs.java | 22 + .../ameta/FieldValueInterceptionSupport.java | 3 +- .../SubstrateAnnotationExtractor.java | 12 +- .../AnalysisToHostedGraphTransplanter.java | 2 +- .../oracle/svm/hosted/code/CompileQueue.java | 89 ++-- .../oracle/svm/hosted/code/FactoryMethod.java | 4 + .../svm/hosted/heap/SVMImageLayerLoader.java | 125 ++++-- .../heap/SVMImageLayerLoaderHelper.java | 58 +++ .../heap/SVMImageLayerSnapshotUtil.java | 232 ++++++++++ .../svm/hosted/heap/SVMImageLayerWriter.java | 68 +-- .../heap/SVMImageLayerWriterHelper.java | 58 +++ .../imagelayer/HostedDynamicLayerInfo.java | 4 + .../HostedImageLayerBuildingSupport.java | 11 +- .../imagelayer/LoadImageSingletonFeature.java | 7 + .../oracle/svm/hosted/meta/HostedMethod.java | 20 +- .../svm/hosted/meta/UniverseBuilder.java | 11 +- .../thread/LayeredVMThreadLocalCollector.java | 21 +- 44 files changed, 2024 insertions(+), 332 deletions(-) create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoaderHelper.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriterHelper.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerElement.java create mode 100644 substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerField.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoaderHelper.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriterHelper.java 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 9c1565788301..80566ffe769e 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 @@ -175,7 +175,7 @@ public void testIt() { List externalValues = List.of(ObjectCopier.getField(BaseClass.class, "BASE_SINGLETON"), ObjectCopier.getField(TestObject.class, "TEST_OBJECT_SINGLETON")); - String encoded = ObjectCopier.encode(root, externalValues); + String encoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValues), 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(decoded, externalValues); + String reencoded = ObjectCopier.encode(new ObjectCopier.Encoder(externalValues), decoded); if (DEBUG) { System.out.printf("reencoded:%n%s%n", reencoded); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java index 6d987a4c91aa..7daddd599a23 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/SymbolicSnippetEncoder.java @@ -1073,7 +1073,8 @@ protected boolean tryInvocationPlugin(CallTargetNode.InvokeKind invokeKind, Valu /** * To prevent this field being considered as an externalValue by - * {@link ObjectCopier#encode(Object, List)}, it must not be {@code final}. + * {@link ObjectCopier#encode(ObjectCopier.Encoder, Object)}, it must not be + * {@code final}. */ private static Map, SnippetResolvedJavaType> snippetTypes = new HashMap<>(); diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/guestgraal/CompilerConfig.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/guestgraal/CompilerConfig.java index 0884a947c99c..097b704e8e8f 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/guestgraal/CompilerConfig.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/guestgraal/CompilerConfig.java @@ -35,6 +35,7 @@ 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; @@ -89,7 +90,18 @@ public static void main(String[] args) throws Exception { encodedObjects.put("encodedSnippets", encodedSnippets); encodedObjects.put("foreignCallSignatures", foreignCallSignatures); - String encoded = ObjectCopier.encode(encodedObjects, externalValues); + ObjectCopier.Encoder encoder = new ObjectCopier.Encoder(externalValues) { + @Override + protected ClassInfo makeClassInfo(Class declaringClass) { + ClassInfo ci = ClassInfo.of(declaringClass); + for (var f : ci.fields().values()) { + // Avoid problems with identity hash codes + GraalError.guarantee(!f.getName().toLowerCase(Locale.ROOT).contains("hash"), "Cannot serialize hash field: %s", f); + } + return ci; + } + }; + String encoded = ObjectCopier.encode(encoder, encodedObjects); Files.writeString(Path.of(args[0]), encoded); } diff --git a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/SnippetTemplate.java b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/SnippetTemplate.java index 1a89962ee5dc..4850d800f650 100644 --- a/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/SnippetTemplate.java +++ b/compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/SnippetTemplate.java @@ -844,12 +844,12 @@ public int hashCode() { private static final TimerKey SnippetTemplateCreationTime = DebugContext.timer("SnippetTemplateCreationTime"); private static final CounterKey SnippetTemplates = DebugContext.counter("SnippetTemplateCount"); - static class Options { + public static class Options { @Option(help = "Use a LRU cache for snippet templates.")// public static final OptionKey UseSnippetTemplateCache = new OptionKey<>(true); @Option(help = "")// - static final OptionKey MaxTemplatesPerSnippet = new OptionKey<>(50); + public static final OptionKey MaxTemplatesPerSnippet = new OptionKey<>(50); } /** @@ -997,11 +997,11 @@ protected PhaseSuite createMidTierPostLoweringPhases() { } } - private static final class LRUCache extends LinkedHashMap { + public static final class LRUCache extends LinkedHashMap { private static final long serialVersionUID = 1L; private final int maxCacheSize; - LRUCache(int initialCapacity, int maxCacheSize) { + public LRUCache(int initialCapacity, int maxCacheSize) { super(initialCapacity, 0.75F, true); this.maxCacheSize = maxCacheSize; } 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 738b1f49bf0e..6e12e8208a38 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 @@ -36,7 +36,6 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.function.BiConsumer; @@ -56,6 +55,7 @@ import jdk.graal.compiler.core.common.FieldIntrospection; import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.replacements.SnippetTemplate; import jdk.internal.misc.Unsafe; /** @@ -92,7 +92,7 @@ public class ObjectCopier { /** * A builtin is specialized support for encoded and decoding values of specific types. */ - abstract static class Builtin { + public abstract static class Builtin { /** * The primary type for this builtin. */ @@ -148,7 +148,7 @@ void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { * Encodes the value of {@code obj} to a String that does not contain {@code '\n'} or * {@code '\r'}. */ - abstract String encode(Encoder encoder, Object obj); + protected abstract String encode(Encoder encoder, Object obj); /** * Decodes {@code encoded} to an object of a type handled by this builtin. @@ -156,7 +156,7 @@ void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { * @param encoding the non-default encoded used when encoded the object or null if the * default encoded was used */ - abstract Object decode(Decoder decoder, Class concreteType, String encoding, String encoded); + protected abstract Object decode(Decoder decoder, Class concreteType, String encoding, String encoded); @Override public String toString() { @@ -182,12 +182,12 @@ static final class ClassBuiltin extends Builtin { } @Override - String encode(Encoder encoder, Object obj) { + protected String encode(Encoder encoder, Object obj) { return ((Class) obj).getName(); } @Override - Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { return switch (encoded) { case "boolean" -> boolean.class; case "byte" -> byte.class; @@ -230,7 +230,7 @@ String encodingName(Object obj) { } @Override - String encode(Encoder encoder, Object obj) { + protected String encode(Encoder encoder, Object obj) { String s = obj instanceof String ? (String) obj : new String((char[]) obj); if ("escaped".equals(encodingName(s))) { return s.replace("\\", "\\\\").replace("\n", "\\n").replace("\r", "\\r"); @@ -239,7 +239,7 @@ String encode(Encoder encoder, Object obj) { } @Override - Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { String s = encoded; if (encoding != null) { GraalError.guarantee(encoding.equals("escaped"), "Unknown encoded: %s", encoding); @@ -270,13 +270,13 @@ static final class EnumBuiltin extends Builtin { } @Override - String encode(Encoder encoder, Object obj) { + protected String encode(Encoder encoder, Object obj) { return ((Enum) obj).name(); } @SuppressWarnings({"unchecked", "rawtypes"}) @Override - Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { return Enum.valueOf((Class) concreteType, encoded); } } @@ -297,11 +297,13 @@ static final class HashMapBuiltin extends Builtin { final Map, Supplier> factories; HashMapBuiltin() { - super(HashMap.class, IdentityHashMap.class, LinkedHashMap.class); + super(HashMap.class, IdentityHashMap.class, LinkedHashMap.class, SnippetTemplate.LRUCache.class); + int size = SnippetTemplate.Options.MaxTemplatesPerSnippet.getDefaultValue(); factories = Map.of( HashMap.class, HashMap::new, IdentityHashMap.class, IdentityHashMap::new, - LinkedHashMap.class, LinkedHashMap::new); + LinkedHashMap.class, LinkedHashMap::new, + SnippetTemplate.LRUCache.class, () -> new SnippetTemplate.LRUCache<>(size, size)); } @Override @@ -311,14 +313,14 @@ void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { } @Override - String encode(Encoder encoder, Object obj) { + protected String encode(Encoder encoder, Object obj) { Map map = (Map) obj; return encoder.encodeMap(new EconomicMapWrap<>(map)); } @SuppressWarnings("unchecked") @Override - Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { Map map = (Map) factories.get(concreteType).get(); decoder.decodeMap(encoded, map::put); return map; @@ -350,12 +352,12 @@ void makeChildIds(Encoder encoder, Object obj, ObjectPath objectPath) { } @Override - String encode(Encoder encoder, Object obj) { + protected String encode(Encoder encoder, Object obj) { return encoder.encodeMap((UnmodifiableEconomicMap) obj); } @Override - Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { + protected Object decode(Decoder decoder, Class concreteType, String encoding, String encoded) { if (EconomicMap.class.isAssignableFrom(concreteType)) { EconomicMap map = EconomicMap.create(); decoder.decodeMap(encoded, map::put); @@ -374,8 +376,8 @@ Object decode(Decoder decoder, Class concreteType, String encoding, String en * have the same name in which case the descriptor includes the qualified name of the * class declaring the field as a prefix. */ - record ClassInfo(Class clazz, Map fields) { - static ClassInfo of(Class declaringClass) { + public record ClassInfo(Class clazz, Map fields) { + public static ClassInfo of(Class declaringClass) { Map fields = new HashMap<>(); for (Class c = declaringClass; !c.equals(Object.class); c = c.getSuperclass()) { for (Field f : c.getDeclaredFields()) { @@ -387,9 +389,6 @@ static ClassInfo of(Class declaringClass) { } Field conflict = fields.put(fieldDesc, f); GraalError.guarantee(conflict == null, "Cannot support 2 fields with same name and declaring class: %s and %s", conflict, f); - - // Try to avoid problems with identity hash codes - GraalError.guarantee(!f.getName().toLowerCase(Locale.ROOT).contains("hash"), "Cannot serialize hash field: %s", f); } } } @@ -403,7 +402,7 @@ static ClassInfo of(Class declaringClass) { final Map, Builtin> builtinClasses = new HashMap<>(); final Set> notBuiltins = new HashSet<>(); - final void addBuiltin(Builtin builtin) { + protected final void addBuiltin(Builtin builtin) { addBuiltin(builtin, builtin.clazz); } @@ -439,13 +438,9 @@ static String[] splitSpaceSeparatedElements(String elements) { } /** - * Encodes {@code root} to a String. - * - * @param externalValues static fields whose values should not be encoded but instead - * represented as a reference to the field + * Encodes {@code root} to a String using {@code encoder}. */ - public static String encode(Object root, List externalValues) { - Encoder encoder = new Encoder(externalValues); + public static String encode(Encoder encoder, Object root) { int rootId = encoder.makeId(root, ObjectPath.of("[root]")).id(); GraalError.guarantee(rootId == 1, "The root object should have id of 1, not %d", rootId); ByteArrayOutputStream baos = new ByteArrayOutputStream(); @@ -457,15 +452,19 @@ public static String encode(Object root, List externalValues) { public static Object decode(String encoded, ClassLoader loader) { Decoder decoder = new Decoder(loader); + return decode(decoder, encoded); + } + + public static Object decode(Decoder decoder, String encoded) { return decoder.decode(encoded); } - static class Decoder extends ObjectCopier { + public static class Decoder extends ObjectCopier { private final Map idToObject = new HashMap<>(); private final ClassLoader loader; - Decoder(ClassLoader loader) { + public Decoder(ClassLoader loader) { this.loader = loader; } @@ -779,7 +778,7 @@ final Builtin getBuiltin(Class clazz, boolean onlyCheck) { return b; } - static class Encoder extends ObjectCopier { + public static class Encoder extends ObjectCopier { final Map objectToId = new IdentityHashMap<>(); final List objects = new ArrayList<>(); @@ -790,7 +789,7 @@ static class Encoder extends ObjectCopier { */ final Map externalValues = new IdentityHashMap<>(); - Encoder(List externalValues) { + public Encoder(List externalValues) { objects.add(null); objectToId.put(null, new ObjectID(0, null)); for (Field f : externalValues) { @@ -798,6 +797,17 @@ static class Encoder extends ObjectCopier { } } + /** + * Gets a {@link ClassInfo} for encoding the fields of {@code declaringClass}. + *

+ * A subclass can override this to enforce encoding invariants on classes or fields. + * + * @throws GraalError if an invariant is violated + */ + protected ClassInfo makeClassInfo(Class declaringClass) { + return ClassInfo.of(declaringClass); + } + private void addExternalValue(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); @@ -814,6 +824,10 @@ private void addExternalValue(Field field) { } + public Map getExternalValues() { + return externalValues; + } + private String encodeMap(UnmodifiableEconomicMap map) { UnmodifiableMapCursor cursor = map.getEntries(); StringBuilder value = new StringBuilder(); @@ -858,8 +872,6 @@ ObjectID makeId(Object obj, ObjectPath objectPath) { objectToId.put(field, id); return id; } - checkIllegalValue(Field.class, obj, objectPath, "Field type is used in object copying implementation"); - checkIllegalValue(FieldIntrospection.class, obj, objectPath, "Graal metadata type cannot be copied"); objects.add(obj); objectToId.put(obj, id); @@ -871,6 +883,10 @@ ObjectID makeId(Object obj, ObjectPath objectPath) { builtin.makeChildIds(this, obj, objectPath); return id; } + + checkIllegalValue(Field.class, obj, objectPath, "Field type is used in object copying implementation"); + checkIllegalValue(FieldIntrospection.class, obj, objectPath, "Graal metadata type cannot be copied"); + if (clazz.isArray()) { Class componentType = clazz.getComponentType(); if (!componentType.isPrimitive()) { @@ -885,7 +901,7 @@ ObjectID makeId(Object obj, ObjectPath objectPath) { checkIllegalValue(LocationIdentity.class, obj, objectPath, "must come from a static field"); checkIllegalValue(HashSet.class, obj, objectPath, "hashes are typically not stable across VM executions"); - ClassInfo classInfo = classInfos.computeIfAbsent(clazz, ClassInfo::of); + ClassInfo classInfo = classInfos.computeIfAbsent(clazz, this::makeClassInfo); for (Field f : classInfo.fields().values()) { makeId(f.getType(), objectPath.add(f.getName() + ":type")); if (!f.getType().isPrimitive()) { diff --git a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandalonePointsToAnalysis.java b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandalonePointsToAnalysis.java index 1dc26544b7aa..1834b3d0cb87 100644 --- a/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandalonePointsToAnalysis.java +++ b/substratevm/src/com.oracle.graal.pointsto.standalone/src/com/oracle/graal/pointsto/standalone/StandalonePointsToAnalysis.java @@ -57,9 +57,7 @@ public StandalonePointsToAnalysis(OptionValues options, AnalysisUniverse univers public void cleanupAfterAnalysis() { super.cleanupAfterAnalysis(); // No need to keep method graphs for standalone analysis. - universe.getMethods().forEach(m -> { - m.setAnalyzedGraph(null); - }); + universe.getMethods().forEach(AnalysisMethod::clearAnalyzedGraph); universe.getMethods().clear(); universe.getFields().clear(); addedClinits.clear(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnalysisParsedGraph.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnalysisParsedGraph.java index 316e9a0c05f2..df9fa174ca4d 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnalysisParsedGraph.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnalysisParsedGraph.java @@ -70,7 +70,7 @@ public final class AnalysisParsedGraph { private final EncodedGraph encodedGraph; private final boolean isIntrinsic; - private AnalysisParsedGraph(EncodedGraph encodedGraph, boolean isIntrinsic) { + public AnalysisParsedGraph(EncodedGraph encodedGraph, boolean isIntrinsic) { this.isIntrinsic = isIntrinsic; this.encodedGraph = encodedGraph; } 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 5292f771ee97..d51dbc06dbe7 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 @@ -24,13 +24,19 @@ */ package com.oracle.graal.pointsto.heap; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ANALYSIS_PARSED_GRAPH_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ARGUMENTS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ARGUMENT_IDS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ARRAY_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CAN_BE_STATICALLY_BOUND_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_JAVA_NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_NAME_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CODE_SIZE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.COMPONENT_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANTS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANTS_TO_RELINK_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANT_TYPE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTRUCTOR_NAME; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.DATA_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ENCLOSING_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ENUM_CLASS_TAG; @@ -39,6 +45,7 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_ACCESSED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_FOLDED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_READ_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_WRITTEN_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.HUB_IDENTITY_HASH_CODE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IDENTITY_HASH_CODE_TAG; @@ -46,13 +53,19 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_HEAP_SIZE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INSTANCE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INTERFACES_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INTRINSIC_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_CONSTRUCTOR_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_ENUM_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INITIALIZED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INTERFACE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INTERNAL_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_LINKED_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_SYNTHETIC_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_VAR_ARGS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.LOCATION_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.METHODS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.MODIFIERS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NEXT_FIELD_ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NEXT_METHOD_ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NEXT_TYPE_ID_TAG; @@ -61,11 +74,14 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.OBJECT_OFFSET_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.OBJECT_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PERSISTED; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.POSITION_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PRIMITIVE_ARRAY_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RETURN_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.SIMULATED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.SOURCE_FILE_NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STATIC_OBJECT_FIELDS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STATIC_PRIMITIVE_FIELDS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STRENGTHENED_GRAPH_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.SUPER_CLASS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TYPES_TAG; @@ -75,6 +91,9 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStreamReader; +import java.lang.annotation.Annotation; +import java.lang.reflect.Executable; +import java.lang.reflect.Field; import java.nio.file.Path; import java.util.HashMap; import java.util.List; @@ -86,20 +105,28 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; +import com.oracle.graal.pointsto.flow.AnalysisParsedGraph; import com.oracle.graal.pointsto.heap.value.ValueSupplier; +import com.oracle.graal.pointsto.infrastructure.ResolvedSignature; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.graal.pointsto.meta.BaseLayerField; import com.oracle.graal.pointsto.meta.BaseLayerMethod; import com.oracle.graal.pointsto.meta.BaseLayerType; +import com.oracle.graal.pointsto.meta.PointsToAnalysisField; +import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; +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.ReflectionUtil; import jdk.graal.compiler.core.common.SuppressFBWarnings; import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.nodes.EncodedGraph; +import jdk.graal.compiler.util.ObjectCopier; import jdk.graal.compiler.util.json.JsonParser; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; @@ -128,6 +155,9 @@ * "class name": type.getName(), * "modifiers": modifiers, * "is interface": isInterface, + * "is enum": isEnum, + * "is initialized": isInitialized, + * "is linked": isLinked, * "source file name": sourceFileName, * "enclosing type": enclosingTid, * "component type": componentTid, @@ -135,13 +165,41 @@ * "interfaces": [ * interfaceTid, * ... + * ], + * "annotations": [ + * annotationName, + * ... * ] * }, * ... * }, * "methods": { * methodIdentifier: { - * "id": id + * "id": id, + * ("arguments": [ + * argumentName, + * ... + * ], + * "class name": className,) + * "tid": tid, + * "argument ids": [ + * argumentId, + * ... + * ], + * "id": id, + * "name": name, + * "return type": returnTypeId, + * "is varArg": isVarArg, + * "can be statically bound": canBeStaticallyBound, + * "modifiers": modifiers, + * "is constructor": isConstructor, + * "is synthetic": isSynthetic, + * "code size": codeSize, + * "compiled": compiled, + * "annotations": [ + * annotationName, + * ... + * ] * }, * ... * }, @@ -152,7 +210,16 @@ * "accessed": accessed, * "read": read, * "written": written, - * "folded": folded + * "folded": folded, + * "is internal": isInternal, + * "field type": typeId, + * "modifiers": modifiers, + * "position": position, + * "annotations": [ + * annotationName, + * ... + * ] + * (,"class name": className) * (,"location": location) * }, * ... @@ -207,23 +274,24 @@ * The "offset object" is the offset of the constant in the heap from the base layer. */ public class ImageLayerLoader { - private final Set processedFieldsIds = ConcurrentHashMap.newKeySet(); private final Map types = new ConcurrentHashMap<>(); protected final Map methods = new ConcurrentHashMap<>(); + protected final Map fields = new ConcurrentHashMap<>(); protected final Map constants = new ConcurrentHashMap<>(); private final List loadPaths; private final Map baseLayerTypes = new ConcurrentHashMap<>(); - /** - * Map from a missing method id to all the constants that depend on it. A method is missing when - * a constant contains a method pointer and the corresponding {@link AnalysisMethod} was not - * created yet. In this case, an {@link AnalysisFuture} that looks up the method and creates the - * missing constant is created and stored in this map. - */ - protected final ConcurrentHashMap>> missingMethodTasks = new ConcurrentHashMap<>(); - private final Map typeToIdentifier = new HashMap<>(); private final Map typeToHubIdentityHashCode = new HashMap<>(); + private final Map baseLayerMethods = new ConcurrentHashMap<>(); + private final Map typeIdToIdentifier = new HashMap<>(); + private final Map methodIdToIdentifier = new HashMap<>(); + private final Map fieldIdToIdentifier = new HashMap<>(); + + record FieldIdentifier(String tid, String name) { + } + protected final Set> heapScannerTasks = ConcurrentHashMap.newKeySet(); private final ImageLayerSnapshotUtil imageLayerSnapshotUtil; + private ImageLayerLoaderHelper imageLayerLoaderHelper; protected final Map typeToConstant = new ConcurrentHashMap<>(); protected final Map stringToConstant = new ConcurrentHashMap<>(); protected final Map, Integer> enumToConstant = new ConcurrentHashMap<>(); @@ -254,6 +322,10 @@ public void setUniverse(AnalysisUniverse newUniverse) { this.universe = newUniverse; } + public void setImageLayerLoaderHelper(ImageLayerLoaderHelper imageLayerLoaderHelper) { + this.imageLayerLoaderHelper = imageLayerLoaderHelper; + } + /** * Note this code is not thread safe. */ @@ -295,13 +367,20 @@ private void loadLayerAnalysis0() { imageHeapSize = Long.parseLong(get(jsonMap, IMAGE_HEAP_SIZE_TAG)); - /* This mapping allows one to get the base layer information from a type id */ - EconomicMap typesMap = get(jsonMap, TYPES_TAG); - MapCursor typesCursor = typesMap.getEntries(); - while (typesCursor.advance()) { - EconomicMap typeData = getValue(typesCursor); - int tid = get(typeData, ID_TAG); - typeToIdentifier.put(tid, typesCursor.getKey()); + /* Those mappings allow to get the base layer information from a type or method id */ + storeIdToIdentifier(TYPES_TAG, typeIdToIdentifier); + storeIdToIdentifier(METHODS_TAG, methodIdToIdentifier); + + EconomicMap fieldsMap = get(jsonMap, FIELDS_TAG); + MapCursor fieldsCursor = fieldsMap.getEntries(); + while (fieldsCursor.advance()) { + EconomicMap typeData = getValue(fieldsCursor); + MapCursor typeFieldsCursor = typeData.getEntries(); + while (typeFieldsCursor.advance()) { + EconomicMap fieldData = getValue(typeFieldsCursor); + int id = get(fieldData, ID_TAG); + fieldIdToIdentifier.put(id, new FieldIdentifier(fieldsCursor.getKey(), typeFieldsCursor.getKey())); + } } EconomicMap constantsMap = get(jsonMap, CONSTANTS_TAG); @@ -313,7 +392,16 @@ private void loadLayerAnalysis0() { } } - @SuppressWarnings("unchecked") + private void storeIdToIdentifier(String tag, Map idToIdentifier) { + EconomicMap elementsMap = get(jsonMap, tag); + MapCursor cursor = elementsMap.getEntries(); + while (cursor.advance()) { + EconomicMap data = getValue(cursor); + int id = get(data, ID_TAG); + idToIdentifier.put(id, cursor.getKey()); + } + } + protected void prepareConstantRelinking(EconomicMap constantData, int identityHashCode, int id) { String value = get(constantData, VALUE_TAG); if (value != null) { @@ -323,10 +411,7 @@ protected void prepareConstantRelinking(EconomicMap constantData String className = get(constantData, ENUM_CLASS_TAG); if (className != null) { - Class enumClass = ReflectionUtil.lookupClass(false, className); - String name = get(constantData, ENUM_NAME_TAG); - /* asSubclass produces an "unchecked" warning */ - Enum enumValue = Enum.valueOf(enumClass.asSubclass(Enum.class), name); + Enum enumValue = getEnumValue(constantData); injectIdentityHashCode(enumValue, identityHashCode); enumToConstant.put(enumValue, id); } @@ -377,7 +462,10 @@ private void loadType(EconomicMap typeData) { ResolvedJavaType objectType = universe.getOriginalMetaAccess().lookupJavaType(Object.class); - return new BaseLayerType(className, tid, modifiers, isInterface, isEnum, isInitialized, isLinked, sourceFileName, enclosingType, componentType, superClass, interfaces, objectType); + Annotation[] annotations = getAnnotations(typeData); + + return new BaseLayerType(className, tid, modifiers, isInterface, isEnum, isInitialized, isLinked, sourceFileName, enclosingType, componentType, superClass, interfaces, objectType, + annotations); }); BaseLayerType baseLayerType = baseLayerTypes.get(tid); AnalysisType type = universe.lookup(baseLayerType); @@ -385,14 +473,18 @@ private void loadType(EconomicMap typeData) { } } + protected Annotation[] getAnnotations(@SuppressWarnings("unused") EconomicMap elementData) { + return new Annotation[0]; + } + private ResolvedJavaType getResolvedJavaType(Integer tid) { return tid == null ? null : getAnalysisType(tid).getWrapped(); } - protected AnalysisType getAnalysisType(Integer tid) { + public AnalysisType getAnalysisType(Integer tid) { if (!types.containsKey(tid)) { EconomicMap typesMap = get(jsonMap, TYPES_TAG); - loadType(get(typesMap, typeToIdentifier.get(tid))); + loadType(get(typesMap, typeIdToIdentifier.get(tid))); } guarantee(types.containsKey(tid), "Type with id %d was not correctly loaded.", tid); /* @@ -436,7 +528,7 @@ private int getBaseLayerTypeId(AnalysisType type) { * Tries to look up the base layer type in the current VM. Some types cannot be looked up by * name (for example $$Lambda types), so this method can return null. */ - private static Class lookupBaseLayerTypeInHostVM(String type) { + public static Class lookupBaseLayerTypeInHostVM(String type) { int arrayType = 0; String componentType = type; /* @@ -477,48 +569,255 @@ private static Class lookupPrimitiveClass(String type) { }; } + private void loadMethod(EconomicMap methodData) { + int mid = get(methodData, ID_TAG); + + if (imageLayerLoaderHelper.loadMethod(methodData, mid)) { + return; + } + + String name = get(methodData, NAME_TAG); + String className = get(methodData, CLASS_NAME_TAG); + if (className != null) { + List arguments = get(methodData, ARGUMENTS_TAG); + + Executable method = null; + Class clazz = lookupBaseLayerTypeInHostVM(className); + if (clazz != null) { + Class[] argumentClasses = arguments.stream().map(ImageLayerLoader::lookupBaseLayerTypeInHostVM).toList().toArray(new Class[0]); + method = lookupMethodByReflection(name, clazz, argumentClasses); + } + + if (method != null) { + metaAccess.lookupJavaMethod(method); + if (methods.containsKey(mid)) { + return; + } + } + } + + int tid = get(methodData, TID_TAG); + List argumentIds = get(methodData, ARGUMENT_IDS_TAG); + int returnTypeId = get(methodData, RETURN_TYPE_TAG); + + AnalysisType type = getAnalysisType(tid); + List arguments = argumentIds.stream().map(this::getAnalysisType).toList(); + Class[] argumentClasses = arguments.stream().map(AnalysisType::getJavaClass).toList().toArray(new Class[0]); + Executable method = lookupMethodByReflection(name, type.getJavaClass(), argumentClasses); + + if (method != null) { + metaAccess.lookupJavaMethod(method); + if (methods.containsKey(mid)) { + return; + } + } + + AnalysisType returnType = getAnalysisType(returnTypeId); + ResolvedSignature signature = ResolvedSignature.fromList(arguments, returnType); + + if (name.equals(CONSTRUCTOR_NAME)) { + type.findConstructor(signature); + } else { + type.findMethod(name, signature); + } + + if (!methods.containsKey(mid)) { + createBaseLayerMethod(methodData, mid, name); + } + } + + private static Executable lookupMethodByReflection(String name, Class clazz, Class[] argumentClasses) { + Executable method; + if (name.equals(CONSTRUCTOR_NAME)) { + method = ReflectionUtil.lookupConstructor(true, clazz, argumentClasses); + } else { + method = ReflectionUtil.lookupMethod(true, clazz, name, argumentClasses); + } + return method; + } + + private void createBaseLayerMethod(EconomicMap methodData, int mid, String name) { + AnalysisType type = getAnalysisType(get(methodData, TID_TAG)); + List parameterTypeIds = get(methodData, ARGUMENT_IDS_TAG); + AnalysisType[] parameterTypes = parameterTypeIds.stream().map(this::getAnalysisType).toList().toArray(new AnalysisType[0]); + AnalysisType returnType = getAnalysisType(get(methodData, RETURN_TYPE_TAG)); + ResolvedSignature signature = ResolvedSignature.fromArray(parameterTypes, returnType); + boolean canBeStaticallyBound = get(methodData, CAN_BE_STATICALLY_BOUND_TAG); + boolean isConstructor = get(methodData, IS_CONSTRUCTOR_TAG); + int modifiers = get(methodData, MODIFIERS_TAG); + boolean isSynthetic = get(methodData, IS_SYNTHETIC_TAG); + boolean isVarArgs = get(methodData, IS_VAR_ARGS_TAG); + int codeSize = get(methodData, CODE_SIZE_TAG); + Annotation[] annotations = getAnnotations(methodData); + + baseLayerMethods.computeIfAbsent(mid, + methodId -> new BaseLayerMethod(mid, type, name, isVarArgs, signature, canBeStaticallyBound, isConstructor, modifiers, isSynthetic, codeSize, annotations)); + BaseLayerMethod baseLayerMethod = baseLayerMethods.get(mid); + + universe.lookup(baseLayerMethod); + } + + public AnalysisMethod getAnalysisMethod(int mid) { + if (!methods.containsKey(mid)) { + EconomicMap methodsMap = get(jsonMap, METHODS_TAG); + loadMethod(get(methodsMap, methodIdToIdentifier.get(mid))); + } + + AnalysisMethod analysisMethod = methods.get(mid); + AnalysisError.guarantee(analysisMethod != null, "Method with id %d was not correctly loaded.", mid); + return analysisMethod; + } + /** * Returns the method id of the given method in the base layer if it exists. This makes the link * between the base layer and the extension layer as the id is used to determine the method used * in RelocatableConstants. */ public int lookupHostedMethodInBaseLayer(AnalysisMethod analysisMethod) { + return getBaseLayerMethodId(analysisMethod); + } + + private int getBaseLayerMethodId(AnalysisMethod analysisMethod) { + if (analysisMethod.getWrapped() instanceof BaseLayerMethod baseLayerMethod) { + return baseLayerMethod.getBaseLayerId(); + } EconomicMap methodData = getMethodData(analysisMethod); - if (methodData == null) { + if (methodData == null || methods.containsKey(analysisMethod.getId())) { /* The method was not reachable in the base image */ return -1; } return get(methodData, ID_TAG); } - /** - * Executes the tasks waiting on a missing method. - *

- * Creates the RelocatableConstant waiting on the method or replaces the {@link BaseLayerMethod} - * by the complete {@link AnalysisMethod}. - */ - public void patchBaseLayerMethod(AnalysisMethod analysisMethod) { + public void initializeBaseLayerMethod(AnalysisMethod analysisMethod) { int id = analysisMethod.getId(); methods.putIfAbsent(id, analysisMethod); - /* Put the method reference in the RelocatableConstants that use a BaseLayerMethod */ - for (AnalysisFuture task : missingMethodTasks.getOrDefault(id, Set.of())) { - task.ensureDone(); + initializeBaseLayerMethod(analysisMethod, getMethodData(analysisMethod)); + } + + @SuppressWarnings("unused") + protected void initializeBaseLayerMethod(AnalysisMethod analysisMethod, EconomicMap methodData) { + /* No flags to load in the AnalysisMethod */ + } + + public boolean hasAnalysisParsedGraph(AnalysisMethod analysisMethod) { + EconomicMap methodData = getMethodData(analysisMethod); + return get(methodData, ANALYSIS_PARSED_GRAPH_TAG) != null; + } + + public AnalysisParsedGraph getAnalysisParsedGraph(AnalysisMethod analysisMethod) { + EconomicMap methodData = getMethodData(analysisMethod); + String encodedAnalyzedGraph = get(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, universe.getSnippetReflection()), encodedAnalyzedGraph); + if (hasStrengthenedGraph(analysisMethod)) { + loadAllAnalysisElements(get(methodData, STRENGTHENED_GRAPH_TAG)); + } + return new AnalysisParsedGraph(analyzedGraph, intrinsic); + } + throw AnalysisError.shouldNotReachHere("The method " + analysisMethod + " does not have a graph from the base layer"); + } + + public boolean hasStrengthenedGraph(AnalysisMethod analysisMethod) { + EconomicMap methodData = getMethodData(analysisMethod); + return get(methodData, STRENGTHENED_GRAPH_TAG) != null; + } + + public void setStrengthenedGraph(AnalysisMethod analysisMethod) { + EconomicMap methodData = getMethodData(analysisMethod); + String encodedAnalyzedGraph = get(methodData, STRENGTHENED_GRAPH_TAG); + EncodedGraph analyzedGraph = (EncodedGraph) ObjectCopier.decode(imageLayerSnapshotUtil.getGraphDecoder(this, universe.getSnippetReflection()), encodedAnalyzedGraph); + processGraph(analyzedGraph); + analysisMethod.setAnalyzedGraph(analyzedGraph); + } + + @SuppressWarnings("unused") + protected void processGraph(EncodedGraph encodedGraph) { + + } + + protected void loadAllAnalysisElements(String encoding) { + for (String line : encoding.lines().toList()) { + if (line.contains(PointsToAnalysisType.class.getName())) { + getAnalysisType(getId(line)); + } else if (line.contains(PointsToAnalysisMethod.class.getName())) { + getAnalysisMethod(getId(line)); + } else if (line.contains(PointsToAnalysisField.class.getName())) { + getAnalysisField(getId(line)); + } else if (line.contains(ImageHeapInstance.class.getName()) || line.contains(ImageHeapObjectArray.class.getName()) || line.contains(ImageHeapPrimitiveArray.class.getName())) { + getOrCreateConstant(getId(line)); + } } - missingMethodTasks.remove(id); + } + + protected static int getId(String line) { + return Integer.parseInt(line.split(" = ")[1]); } private EconomicMap getMethodData(AnalysisMethod analysisMethod) { - String name = imageLayerSnapshotUtil.getMethodIdentifier(analysisMethod); + String name; + int id = analysisMethod.getId(); + if (methodIdToIdentifier.containsKey(id)) { + name = methodIdToIdentifier.get(id); + } else { + name = imageLayerSnapshotUtil.getMethodIdentifier(analysisMethod); + } return getElementData(METHODS_TAG, name); } + private void loadField(FieldIdentifier fieldIdentifier, EconomicMap fieldData) { + AnalysisType declaringClass = getAnalysisType(Integer.parseInt(fieldIdentifier.tid)); + String className = get(fieldData, CLASS_NAME_TAG); + + Class clazz = className != null ? lookupBaseLayerTypeInHostVM(className) : declaringClass.getJavaClass(); + if (clazz == null) { + clazz = declaringClass.getJavaClass(); + } + + Field field = ReflectionUtil.lookupField(true, clazz, fieldIdentifier.name); + if (field == null) { + AnalysisType type = getAnalysisType(get(fieldData, FIELD_TYPE_TAG)); + BaseLayerField baseLayerField = new BaseLayerField(get(fieldData, ID_TAG), fieldIdentifier.name, declaringClass, type, get(fieldData, IS_INTERNAL_TAG), get(fieldData, MODIFIERS_TAG), + getAnnotations(fieldData)); + AnalysisField analysisField = universe.lookup(baseLayerField); + analysisField.setPosition(get(fieldData, POSITION_TAG)); + } else { + metaAccess.lookupJavaField(field); + } + } + + public AnalysisField getAnalysisField(int fid) { + if (!fields.containsKey(fid)) { + FieldIdentifier fieldIdentifier = fieldIdToIdentifier.get(fid); + EconomicMap fieldsMap = get(jsonMap, FIELDS_TAG); + loadField(fieldIdentifier, get(get(fieldsMap, fieldIdentifier.tid), fieldIdentifier.name)); + } + + AnalysisField analysisField = fields.get(fid); + AnalysisError.guarantee(analysisField != null, "Field with id %d was not correctly loaded.", fid); + return analysisField; + } + /** * Returns the field id of the given field in the base layer if it exists. This makes the link * between the base layer and the extension image as the id allows to set the flags of the * fields in the extension image. */ public int lookupHostedFieldInBaseLayer(AnalysisField analysisField) { + return getBaseLayerFieldId(analysisField); + } + + private int getBaseLayerFieldId(AnalysisField analysisField) { + if (analysisField.wrapped instanceof BaseLayerField baseLayerField) { + return baseLayerField.getBaseLayerId(); + } EconomicMap fieldData = getFieldData(analysisField); if (fieldData == null) { /* The field was not reachable in the base image */ @@ -527,8 +826,8 @@ public int lookupHostedFieldInBaseLayer(AnalysisField analysisField) { return get(fieldData, ID_TAG); } - public void loadFieldFlags(AnalysisField analysisField) { - if (processedFieldsIds.add(analysisField.getId())) { + public void initializeBaseLayerField(AnalysisField analysisField) { + if (fields.putIfAbsent(analysisField.getId(), analysisField) == null) { EconomicMap fieldData = getFieldData(analysisField); if (fieldData == null) { @@ -619,8 +918,10 @@ protected ImageHeapConstant getOrCreateConstant(EconomicMap cons AnalysisError.guarantee(object == relinkedObject, "Found discrepancy between hosted value %s and relinked value %s.", object, relinkedObject); } ImageHeapInstance imageHeapInstance = new ImageHeapInstance(type, hostedObject == null ? relinkedHostedObject : hostedObject, identityHashCode); - Object[] fieldValues = getReferencedValues(constantsMap, imageHeapInstance, instanceData, imageLayerSnapshotUtil.getRelinkedFields(type, metaAccess)); - imageHeapInstance.setFieldValues(fieldValues); + if (instanceData != null) { + Object[] fieldValues = getReferencedValues(constantsMap, imageHeapInstance, instanceData, imageLayerSnapshotUtil.getRelinkedFields(type, metaAccess)); + imageHeapInstance.setFieldValues(fieldValues); + } addBaseLayerObject(id, imageHeapInstance, objectOffset); } case ARRAY_TAG -> { @@ -659,11 +960,7 @@ protected JavaConstant getHostedObject(EconomicMap baseLayerCons return getHostedObject(value.intern()); } } else if (Enum.class.isAssignableFrom(clazz)) { - String className = get(baseLayerConstant, ENUM_CLASS_TAG); - Class enumClass = ReflectionUtil.lookupClass(false, className); - String name = get(baseLayerConstant, ENUM_NAME_TAG); - /* asSubclass produces an "unchecked" warning */ - Enum enumValue = Enum.valueOf(enumClass.asSubclass(Enum.class), name); + Enum enumValue = getEnumValue(baseLayerConstant); return getHostedObject(enumValue); } return null; @@ -731,7 +1028,7 @@ private Object[] getReferencedValues(EconomicMap constantsMap, I List constantData = data.get(position); String constantKind = (String) constantData.get(0); Object constantValue = constantData.get(1); - if (delegateProcessing(constantKind, constantValue, values, position)) { + if (delegateProcessing(constantKind, constantValue, constantData, values, position)) { continue; } if (constantKind.equals(OBJECT_TAG)) { @@ -778,7 +1075,7 @@ private Object[] getReferencedValues(EconomicMap constantsMap, I * Hook for subclasses to do their own processing. */ @SuppressWarnings("unused") - protected boolean delegateProcessing(String constantType, Object constantValue, Object[] values, int i) { + protected boolean delegateProcessing(String constantType, Object constantValue, List constantData, Object[] values, int i) { return false; } @@ -889,7 +1186,16 @@ private EconomicMap getElementData(String registry, String eleme return get(innerMap, elementIdentifier); } - protected static T get(EconomicMap innerMap, String elementIdentifier) { + @SuppressWarnings("unchecked") + protected static Enum getEnumValue(EconomicMap enumData) { + String className = get(enumData, ENUM_CLASS_TAG); + Class enumClass = ReflectionUtil.lookupClass(false, className); + String name = get(enumData, ENUM_NAME_TAG); + /* asSubclass produces an "unchecked" warning */ + return Enum.valueOf(enumClass.asSubclass(Enum.class), name); + } + + public static T get(EconomicMap innerMap, String elementIdentifier) { return cast(innerMap.get(elementIdentifier)); } @@ -937,6 +1243,10 @@ public ImageHeapConstant getOrCreateConstant(int id) { return getOrCreateConstant(get(jsonMap, CONSTANTS_TAG), id, null); } + public AnalysisMetaAccess getMetaAccess() { + return metaAccess; + } + public void setMetaAccess(AnalysisMetaAccess metaAccess) { this.metaAccess = metaAccess; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoaderHelper.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoaderHelper.java new file mode 100644 index 000000000000..f94f5a6c526a --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerLoaderHelper.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.heap; + +import org.graalvm.collections.EconomicMap; + +public class ImageLayerLoaderHelper { + protected ImageLayerLoader imageLayerLoader; + + public ImageLayerLoaderHelper(ImageLayerLoader imageLayerLoader) { + this.imageLayerLoader = imageLayerLoader; + } + + @SuppressWarnings("unused") + protected boolean loadMethod(EconomicMap methodData, int mid) { + return false; + } +} 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 e6d3606388c7..393f903f7add 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 @@ -25,29 +25,64 @@ package com.oracle.graal.pointsto.heap; 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.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; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.meta.PointsToAnalysisField; +import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; +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-"; public static final String FILE_EXTENSION = ".json"; + public static final String CONSTRUCTOR_NAME = ""; + public static final String PERSISTED = "persisted"; public static final int NULL_POINTER_CONSTANT = -1; public static final int NOT_MATERIALIZED_CONSTANT = -2; public static final String OBJECT_TAG = "A"; public static final String METHOD_POINTER_TAG = "M"; + public static final String C_ENTRY_POINT_LITERAL_CODE_POINTER = "CONSTANT"; public static final String TYPES_TAG = "types"; public static final String METHODS_TAG = "methods"; public static final String FIELDS_TAG = "fields"; + public static final String IS_INTERNAL_TAG = "is internal"; + public static final String FIELD_TYPE_TAG = "field type"; public static final String CLASS_JAVA_NAME_TAG = "class java name"; + public static final String CAN_BE_STATICALLY_BOUND_TAG = "can be statically bound"; + public static final String IS_CONSTRUCTOR_TAG = "is constructor"; + public static final String IS_SYNTHETIC_TAG = "is synthetic"; + public static final String CODE_SIZE_TAG = "code size"; + public static final String ANNOTATIONS_TAG = "annotations"; public static final String CLASS_NAME_TAG = "class name"; public static final String MODIFIERS_TAG = "modifiers"; + public static final String POSITION_TAG = "position"; public static final String IS_INTERFACE_TAG = "is interface"; public static final String IS_ENUM_TAG = "is enum"; public static final String IS_INITIALIZED_TAG = "is initialized"; @@ -60,9 +95,24 @@ public class ImageLayerSnapshotUtil { public static final String CONSTANTS_TAG = "constants"; public static final String CONSTANTS_TO_RELINK_TAG = "constants to relink"; public static final String TID_TAG = "tid"; + public static final String NAME_TAG = "name"; + public static final String ARGUMENTS_TAG = "arguments"; + public static final String ARGUMENT_IDS_TAG = "argument ids"; + public static final String RETURN_TYPE_TAG = "return type"; + public static final String IS_VAR_ARGS_TAG = "is varArg"; + public static final String METHOD_TYPE_TAG = "method type"; + public static final String METHOD_TYPE_PARAMETERS_TAG = "method type parameters"; + public static final String METHOD_TYPE_RETURN_TAG = "method type return"; + public static final String FACTORY_TAG = "factory"; + public static final String OUTLINED_SB_TAG = "outlinedSB"; + public static final String TARGET_CONSTRUCTOR_TAG = "target constructor"; + public static final String THROW_ALLOCATED_OBJECT_TAG = "throw allocated object"; public static final String IDENTITY_HASH_CODE_TAG = "identityHashCode"; public static final String HUB_IDENTITY_HASH_CODE_TAG = "hub identityHashCode"; public static final String ID_TAG = "id"; + public static final String ANALYSIS_PARSED_GRAPH_TAG = "analysis parsed graph"; + public static final String STRENGTHENED_GRAPH_TAG = "strengthened graph"; + public static final String INTRINSIC_TAG = "intrinsic"; public static final String CONSTANT_TYPE_TAG = "constant type"; public static final String DATA_TAG = "data"; public static final String INSTANCE_TAG = "instance"; @@ -88,6 +138,35 @@ 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; + + @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); + } + + protected boolean shouldAddExternalValue(Class type) { + return LocationIdentity.class.isAssignableFrom(type); + } + public static String snapshotFileName(String imageName) { return FILE_NAME_PREFIX + imageName + FILE_EXTENSION; } @@ -119,4 +198,221 @@ protected static String getQualifiedName(AnalysisMethod method) { public Set getRelinkedFields(AnalysisType type, AnalysisMetaAccess metaAccess) { return Set.of(); } + + public GraphEncoder getGraphEncoder(ImageLayerWriter imageLayerWriter) { + return new GraphEncoder(externalValues, imageLayerWriter); + } + + @SuppressWarnings("unused") + public GraphDecoder getGraphDecoder(ImageLayerLoader imageLayerLoader, SnippetReflectionProvider snippetReflectionProvider) { + return new GraphDecoder(EncodedGraph.class.getClassLoader(), imageLayerLoader); + } + + public static class GraphEncoder extends ObjectCopier.Encoder { + @SuppressWarnings("this-escape") + public GraphEncoder(List externalValues, ImageLayerWriter imageLayerWriter) { + super(externalValues); + addBuiltin(new NodeClassBuiltIn()); + addBuiltin(new ImageHeapConstantBuiltIn(imageLayerWriter, null)); + addBuiltin(new AnalysisTypeBuiltIn(imageLayerWriter, null)); + addBuiltin(new AnalysisMethodBuiltIn(imageLayerWriter, null)); + addBuiltin(new AnalysisFieldBuiltIn(imageLayerWriter, null)); + addBuiltin(new FieldLocationIdentityBuiltIn(imageLayerWriter, null)); + addBuiltin(new NamedLocationIdentityArrayBuiltIn()); + } + } + + public static class GraphDecoder extends ObjectCopier.Decoder { + @SuppressWarnings("this-escape") + public GraphDecoder(ClassLoader classLoader, ImageLayerLoader imageLayerLoader) { + super(classLoader); + addBuiltin(new NodeClassBuiltIn()); + addBuiltin(new ImageHeapConstantBuiltIn(null, imageLayerLoader)); + addBuiltin(new AnalysisTypeBuiltIn(null, imageLayerLoader)); + addBuiltin(new AnalysisMethodBuiltIn(null, imageLayerLoader)); + addBuiltin(new AnalysisFieldBuiltIn(null, imageLayerLoader)); + addBuiltin(new FieldLocationIdentityBuiltIn(null, imageLayerLoader)); + addBuiltin(new NamedLocationIdentityArrayBuiltIn()); + } + } + + 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; + + protected ImageHeapConstantBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayerLoader imageLayerLoader) { + super(ImageHeapConstant.class, ImageHeapInstance.class, ImageHeapObjectArray.class, ImageHeapPrimitiveArray.class); + this.imageLayerWriter = imageLayerWriter; + this.imageLayerLoader = imageLayerLoader; + } + + @Override + public String encode(ObjectCopier.Encoder encoder, Object obj) { + ImageHeapConstant imageHeapConstant = (ImageHeapConstant) obj; + imageLayerWriter.elementsToPersist.add(new AnalysisFuture<>(() -> imageLayerWriter.persistConstant(imageHeapConstant))); + return String.valueOf(imageHeapConstant.getConstantData().id); + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + return imageLayerLoader.getOrCreateConstant(Integer.parseInt(encoded)); + } + } + + public static class AnalysisTypeBuiltIn extends ObjectCopier.Builtin { + private final ImageLayerWriter imageLayerWriter; + private final ImageLayerLoader imageLayerLoader; + + protected AnalysisTypeBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayerLoader imageLayerLoader) { + super(AnalysisType.class, PointsToAnalysisType.class); + this.imageLayerWriter = imageLayerWriter; + this.imageLayerLoader = imageLayerLoader; + } + + @Override + public String encode(ObjectCopier.Encoder encoder, Object obj) { + AnalysisType type = (AnalysisType) obj; + if (!type.isReachable() && !imageLayerWriter.typesMap.containsKey(imageLayerWriter.imageLayerSnapshotUtil.getTypeIdentifier(type))) { + imageLayerWriter.persistType(type); + } + return String.valueOf(type.getId()); + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + return imageLayerLoader.getAnalysisType(Integer.parseInt(encoded)); + } + } + + public static class AnalysisMethodBuiltIn extends ObjectCopier.Builtin { + private final ImageLayerWriter imageLayerWriter; + private final ImageLayerLoader imageLayerLoader; + + protected AnalysisMethodBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayerLoader imageLayerLoader) { + super(AnalysisMethod.class, PointsToAnalysisMethod.class); + this.imageLayerWriter = imageLayerWriter; + this.imageLayerLoader = imageLayerLoader; + } + + @Override + public String encode(ObjectCopier.Encoder encoder, Object obj) { + AnalysisMethod method = (AnalysisMethod) obj; + AnalysisType declaringClass = method.getDeclaringClass(); + imageLayerWriter.elementsToPersist.add(new AnalysisFuture<>(() -> { + if (!method.isReachable() && !imageLayerWriter.methodsMap.containsKey(imageLayerWriter.imageLayerSnapshotUtil.getMethodIdentifier(method))) { + imageLayerWriter.persistAnalysisParsedGraph(method); + imageLayerWriter.persistMethod(method); + } + })); + for (AnalysisType parameter : method.toParameterList()) { + if (!parameter.isReachable() && !imageLayerWriter.typesMap.containsKey(imageLayerWriter.imageLayerSnapshotUtil.getTypeIdentifier(parameter))) { + imageLayerWriter.persistType(parameter); + } + } + if (!declaringClass.isReachable() && !imageLayerWriter.typesMap.containsKey(imageLayerWriter.imageLayerSnapshotUtil.getTypeIdentifier(declaringClass))) { + imageLayerWriter.persistType(declaringClass); + } + return String.valueOf(method.getId()); + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + return imageLayerLoader.getAnalysisMethod(Integer.parseInt(encoded)); + } + } + + public static class AnalysisFieldBuiltIn extends ObjectCopier.Builtin { + private final ImageLayerWriter imageLayerWriter; + private final ImageLayerLoader imageLayerLoader; + + protected AnalysisFieldBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayerLoader imageLayerLoader) { + super(AnalysisField.class, PointsToAnalysisField.class); + this.imageLayerWriter = imageLayerWriter; + this.imageLayerLoader = imageLayerLoader; + } + + @Override + public String encode(ObjectCopier.Encoder encoder, Object obj) { + AnalysisField field = (AnalysisField) obj; + return encodeField(field, imageLayerWriter); + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + return decodeField(imageLayerLoader, encoded); + } + } + + public static class FieldLocationIdentityBuiltIn extends ObjectCopier.Builtin { + private final ImageLayerWriter imageLayerWriter; + private final ImageLayerLoader imageLayerLoader; + + protected FieldLocationIdentityBuiltIn(ImageLayerWriter imageLayerWriter, ImageLayerLoader imageLayerLoader) { + super(FieldLocationIdentity.class); + this.imageLayerWriter = imageLayerWriter; + this.imageLayerLoader = imageLayerLoader; + } + + @Override + public String encode(ObjectCopier.Encoder encoder, Object obj) { + FieldLocationIdentity fieldLocationIdentity = (FieldLocationIdentity) obj; + AnalysisField field = (AnalysisField) fieldLocationIdentity.getField(); + return encodeField(field, imageLayerWriter); + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + return new FieldLocationIdentity(decodeField(imageLayerLoader, encoded)); + } + } + + private static String encodeField(AnalysisField field, ImageLayerWriter imageLayerWriter) { + String declaringClassId = String.valueOf(field.getDeclaringClass().getId()); + if (!imageLayerWriter.fieldsMap.containsKey(declaringClassId) || !imageLayerWriter.fieldsMap.get(declaringClassId).containsKey(field.getName())) { + imageLayerWriter.persistField(field); + } + return String.valueOf(field.getId()); + } + + 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 72e85ee33558..75bc0f68fefe 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 @@ -24,9 +24,15 @@ */ package com.oracle.graal.pointsto.heap; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ANALYSIS_PARSED_GRAPH_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ANNOTATIONS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ARGUMENTS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ARGUMENT_IDS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ARRAY_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CAN_BE_STATICALLY_BOUND_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_JAVA_NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_NAME_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CODE_SIZE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.COMPONENT_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANTS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CONSTANTS_TO_RELINK_TAG; @@ -39,46 +45,63 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_ACCESSED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_FOLDED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_READ_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FIELD_WRITTEN_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IDENTITY_HASH_CODE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_HEAP_SIZE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INSTANCE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INTERFACES_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.INTRINSIC_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_CONSTRUCTOR_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_ENUM_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INITIALIZED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INTERFACE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_INTERNAL_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_LINKED_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_SYNTHETIC_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IS_VAR_ARGS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.METHODS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.MODIFIERS_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NAME_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NEXT_FIELD_ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NEXT_METHOD_ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NEXT_TYPE_ID_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NOT_MATERIALIZED_CONSTANT; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.NULL_POINTER_CONSTANT; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.OBJECT_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.POSITION_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PRIMITIVE_ARRAY_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.RETURN_TYPE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.SIMULATED_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.SOURCE_FILE_NAME_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STRENGTHENED_GRAPH_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.SUPER_CLASS_TAG; 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.lang.reflect.Executable; +import java.lang.reflect.Field; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.IntStream; import org.graalvm.collections.EconomicMap; +import org.graalvm.nativeimage.AnnotationAccess; import com.oracle.graal.pointsto.BigBang; -import com.oracle.graal.pointsto.infrastructure.Universe; +import com.oracle.graal.pointsto.flow.AnalysisParsedGraph; +import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; @@ -88,7 +111,9 @@ import com.oracle.svm.util.FileDumpingUtil; import jdk.graal.compiler.debug.GraalError; +import jdk.graal.compiler.nodes.EncodedGraph; import jdk.graal.compiler.nodes.spi.IdentityHashCodeProvider; +import jdk.graal.compiler.util.ObjectCopier; import jdk.graal.compiler.util.json.JsonWriter; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; @@ -96,25 +121,42 @@ import jdk.vm.ci.meta.ResolvedJavaField; public class ImageLayerWriter { - public static final String TYPE_SWITCH_SUBSTRING = "$$TypeSwitch"; - private final ImageLayerSnapshotUtil imageLayerSnapshotUtil; + protected final ImageLayerSnapshotUtil imageLayerSnapshotUtil; + private ImageLayerWriterHelper imageLayerWriterHelper; private ImageHeap imageHeap; + protected AnalysisUniverse aUniverse; private IdentityHashMap internedStringsIdentityMap; - protected EconomicMap jsonMap = EconomicMap.create(); - protected List constantsToRelink; + protected final EconomicMap jsonMap; + protected final List constantsToRelink; + private final Set persistedTypeIds; + protected final Map> typesMap; + protected final Map> methodsMap; + protected final Map> fieldsMap; + private final Map> constantsMap; FileInfo fileInfo; + private final boolean useSharedLayerGraphs; + + protected final Set> elementsToPersist = ConcurrentHashMap.newKeySet(); private record FileInfo(Path layerSnapshotPath, String fileName, String suffix) { } public ImageLayerWriter() { - this.imageLayerSnapshotUtil = new ImageLayerSnapshotUtil(); + this(true, new ImageLayerSnapshotUtil()); } - public ImageLayerWriter(ImageLayerSnapshotUtil imageLayerSnapshotUtil) { + @SuppressWarnings({"this-escape", "unused"}) + public ImageLayerWriter(boolean useSharedLayerGraphs, ImageLayerSnapshotUtil imageLayerSnapshotUtil) { + this.useSharedLayerGraphs = useSharedLayerGraphs; this.imageLayerSnapshotUtil = imageLayerSnapshotUtil; + this.jsonMap = EconomicMap.create(); this.constantsToRelink = new ArrayList<>(); + this.persistedTypeIds = new HashSet<>(); + this.typesMap = new ConcurrentHashMap<>(); + this.methodsMap = new ConcurrentHashMap<>(); + this.fieldsMap = new ConcurrentHashMap<>(); + this.constantsMap = new ConcurrentHashMap<>(); } public void setInternedStringsIdentityMap(IdentityHashMap map) { @@ -125,25 +167,18 @@ public void setImageHeap(ImageHeap heap) { this.imageHeap = heap; } - /** - * $$TypeSwitch classes do not have a stable name between different JVM instances, which makes - * it hard to match them in two image build processes. - *

- * Those classes are only created to be a container for the static typeSwitch method used in the - * method handle for the corresponding switch. The only way to distinguish two $$TypeSwitch - * classes is to look at the code of the generated method and the class in which the hidden - * class is defined. Since the class is not cached, doing so might not be enough as if two - * identical switches are in the same class, they would create two $$TypeSwitch classes that - * would not be distinguishable. - */ - protected static boolean isTypeSwitch(AnalysisType type) { - return type.toJavaName().contains(TYPE_SWITCH_SUBSTRING); + public void setImageLayerWriterHelper(ImageLayerWriterHelper imageLayerWriterHelper) { + this.imageLayerWriterHelper = imageLayerWriterHelper; } public void setFileInfo(Path layerSnapshotPath, String fileName, String suffix) { fileInfo = new FileInfo(layerSnapshotPath, fileName, suffix); } + public void setAnalysisUniverse(AnalysisUniverse aUniverse) { + this.aUniverse = aUniverse; + } + public void dumpFile() { FileDumpingUtil.dumpFile(fileInfo.layerSnapshotPath, fileInfo.fileName, fileInfo.suffix, writer -> { try (JsonWriter jw = new JsonWriter(writer)) { @@ -158,12 +193,12 @@ public void persistImageHeapSize(long imageHeapSize) { jsonMap.put(IMAGE_HEAP_SIZE_TAG, String.valueOf(imageHeapSize)); } - public void persistAnalysisInfo(Universe hostedUniverse, AnalysisUniverse analysisUniverse) { - persistHook(hostedUniverse, analysisUniverse); + public void persistAnalysisInfo() { + persistHook(); - jsonMap.put(NEXT_TYPE_ID_TAG, analysisUniverse.getNextTypeId()); - jsonMap.put(NEXT_METHOD_ID_TAG, analysisUniverse.getNextMethodId()); - jsonMap.put(NEXT_FIELD_ID_TAG, analysisUniverse.getNextFieldId()); + jsonMap.put(NEXT_TYPE_ID_TAG, aUniverse.getNextTypeId()); + jsonMap.put(NEXT_METHOD_ID_TAG, aUniverse.getNextMethodId()); + jsonMap.put(NEXT_FIELD_ID_TAG, aUniverse.getNextFieldId()); /* * $$TypeSwitch classes should not be instantiated as they are only used as a container for @@ -171,31 +206,30 @@ public void persistAnalysisInfo(Universe hostedUniverse, AnalysisUniverse analys * removed after a mechanism for determining which types have to be persisted is added, or * if a stable name is implemented for them. */ - EconomicMap typesMap = EconomicMap.create(); - for (AnalysisType type : analysisUniverse.getTypes().stream().filter(t -> t.isReachable() && !isTypeSwitch(t)).toList()) { + for (AnalysisType type : aUniverse.getTypes().stream().filter(AnalysisType::isReachable).toList()) { checkTypeStability(type); - persistType(typesMap, type, analysisUniverse); + persistType(type); } jsonMap.put(TYPES_TAG, typesMap); - EconomicMap methodsMap = EconomicMap.create(); - for (AnalysisMethod method : analysisUniverse.getMethods().stream().filter(m -> m.isReachable() && !isTypeSwitch(m.getDeclaringClass())).toList()) { - persistMethod(methodsMap, method); + for (AnalysisMethod method : aUniverse.getMethods().stream().filter(AnalysisMethod::isReachable).toList()) { + persistMethod(method); } jsonMap.put(METHODS_TAG, methodsMap); - EconomicMap> fieldsMap = EconomicMap.create(); - for (AnalysisField field : analysisUniverse.getFields().stream().filter(AnalysisField::isReachable).toList()) { - persistField(fieldsMap, field, hostedUniverse); + for (AnalysisField field : aUniverse.getFields().stream().filter(AnalysisField::isReachable).toList()) { + persistField(field); } jsonMap.put(FIELDS_TAG, fieldsMap); - EconomicMap constantsMap = EconomicMap.create(); for (Map.Entry> entry : imageHeap.getReachableObjects().entrySet()) { for (ImageHeapConstant imageHeapConstant : entry.getValue()) { - persistConstant(analysisUniverse, imageHeapConstant, constantsMap); + persistConstant(imageHeapConstant); } } + for (AnalysisFuture task : elementsToPersist) { + task.ensureDone(); + } jsonMap.put(CONSTANTS_TAG, constantsMap); jsonMap.put(CONSTANTS_TO_RELINK_TAG, constantsToRelink); } @@ -205,15 +239,28 @@ public void persistAnalysisInfo(Universe hostedUniverse, AnalysisUniverse analys * pointsto. */ @SuppressWarnings("unused") - protected void persistHook(Universe hostedUniverse, AnalysisUniverse analysisUniverse) { + protected void persistHook() { } - private void persistType(EconomicMap typesMap, AnalysisType type, AnalysisUniverse analysisUniverse) { + protected void persistType(AnalysisType type) { + if (!persistedTypeIds.add(type.getId())) { + return; + } String typeIdentifier = imageLayerSnapshotUtil.getTypeIdentifier(type); + AnalysisType superclass = type.getSuperclass(); + if (superclass != null) { + /* + * Some persisted types are not reachable. In this case, the super class has to be + * persisted manually as well. + */ + if (!superclass.isReachable()) { + persistType(superclass); + } + } EconomicMap typeMap = EconomicMap.create(); - persistType(type, analysisUniverse, typeMap); + persistType(type, typeMap); if (typesMap.containsKey(typeIdentifier)) { throw GraalError.shouldNotReachHere("The type identifier should be unique, but " + typeIdentifier + " got added twice."); @@ -221,7 +268,7 @@ private void persistType(EconomicMap typesMap, AnalysisType type typesMap.put(typeIdentifier, typeMap); } - protected void persistType(AnalysisType type, @SuppressWarnings("unused") AnalysisUniverse analysisUniverse, EconomicMap typeMap) { + protected void persistType(AnalysisType type, EconomicMap typeMap) { typeMap.put(ID_TAG, type.getId()); List fields = new ArrayList<>(); @@ -247,6 +294,7 @@ protected void persistType(AnalysisType type, @SuppressWarnings("unused") Analys typeMap.put(SUPER_CLASS_TAG, type.getSuperclass().getId()); } typeMap.put(INTERFACES_TAG, Arrays.stream(type.getInterfaces()).map(AnalysisType::getId).toList()); + typeMap.put(ANNOTATIONS_TAG, Arrays.stream(AnnotationAccess.getAnnotationTypes(type)).map(Class::getName).toList()); } /** @@ -258,32 +306,112 @@ public void checkTypeStability(AnalysisType type) { /* Do not need to check anything here */ } - public void persistMethod(EconomicMap methodsMap, AnalysisMethod method) { - EconomicMap methodMap = EconomicMap.create(); + public void persistMethod(AnalysisMethod method) { + EconomicMap methodMap = getMethodMap(method); + Executable executable = method.getJavaMethod(); + + if (methodMap.containsKey(ID_TAG)) { + throw GraalError.shouldNotReachHere("The method identifier should be unique, but " + imageLayerSnapshotUtil.getMethodIdentifier(method) + " got added twice."); + } + if (executable != null) { + methodMap.put(ARGUMENTS_TAG, Arrays.stream(executable.getParameterTypes()).map(Class::getName).toList()); + methodMap.put(CLASS_NAME_TAG, executable.getDeclaringClass().getName()); + } + methodMap.put(TID_TAG, method.getDeclaringClass().getId()); + methodMap.put(ARGUMENT_IDS_TAG, method.getSignature().toParameterList(null).stream().map(AnalysisType::getId).toList()); methodMap.put(ID_TAG, method.getId()); + methodMap.put(NAME_TAG, method.getName()); + methodMap.put(RETURN_TYPE_TAG, method.getSignature().getReturnType().getId()); + methodMap.put(IS_VAR_ARGS_TAG, method.isVarArgs()); + methodMap.put(CAN_BE_STATICALLY_BOUND_TAG, method.canBeStaticallyBound()); + methodMap.put(MODIFIERS_TAG, method.getModifiers()); + methodMap.put(IS_CONSTRUCTOR_TAG, method.isConstructor()); + methodMap.put(IS_SYNTHETIC_TAG, method.isSynthetic()); + methodMap.put(CODE_SIZE_TAG, method.getCodeSize()); + methodMap.put(ANNOTATIONS_TAG, Arrays.stream(AnnotationAccess.getAnnotationTypes(method)).map(Class::getName).toList()); + + imageLayerWriterHelper.persistMethod(method, methodMap); + } + + public boolean isMethodPersisted(AnalysisMethod method) { String name = imageLayerSnapshotUtil.getMethodIdentifier(method); - if (methodsMap.containsKey(name)) { - throw GraalError.shouldNotReachHere("The method identifier should be unique, but " + name + " got added twice."); + return methodsMap.containsKey(name); + } + + public void persistMethodGraphs() { + for (AnalysisMethod method : aUniverse.getMethods()) { + if (method.isReachable()) { + persistAnalysisParsedGraph(method); + } + } + } + + public void persistAnalysisParsedGraph(AnalysisMethod method) { + EconomicMap methodMap = getMethodMap(method); + + Object analyzedGraph = method.getGraph(); + if (analyzedGraph instanceof AnalysisParsedGraph analysisParsedGraph) { + if (!persistGraph(analysisParsedGraph.getEncodedGraph(), methodMap, ANALYSIS_PARSED_GRAPH_TAG)) { + return; + } + methodMap.put(INTRINSIC_TAG, analysisParsedGraph.isIntrinsic()); } - methodsMap.put(name, methodMap); } - private void persistField(EconomicMap> fieldsMap, AnalysisField field, Universe hostedUniverse) { + public void persistMethodStrengthenedGraph(AnalysisMethod method) { + EconomicMap methodMap = getMethodMap(method); + + EncodedGraph analyzedGraph = method.getAnalyzedGraph(); + persistGraph(analyzedGraph, methodMap, STRENGTHENED_GRAPH_TAG); + } + + private boolean persistGraph(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; + } + methodMap.put(graphTag, encodedGraph); + return true; + } + + private EconomicMap getMethodMap(AnalysisMethod method) { + String name = imageLayerSnapshotUtil.getMethodIdentifier(method); + EconomicMap methodMap = methodsMap.get(name); + if (methodMap == null) { + methodMap = EconomicMap.create(); + methodsMap.put(name, methodMap); + } + return methodMap; + } + + protected void persistField(AnalysisField field) { EconomicMap fieldMap = EconomicMap.create(); - persistField(field, hostedUniverse, fieldMap); + persistField(field, fieldMap); - String tid = String.valueOf(field.getDeclaringClass().getId()); - if (fieldsMap.containsKey(tid)) { - fieldsMap.get(tid).put(field.getName(), fieldMap); - } else { - EconomicMap typeFieldsMap = EconomicMap.create(); - typeFieldsMap.put(field.getName(), fieldMap); - fieldsMap.put(tid, typeFieldsMap); + Field originalField = OriginalFieldProvider.getJavaField(field); + if (originalField != null && !originalField.getDeclaringClass().equals(field.getDeclaringClass().getJavaClass())) { + fieldMap.put(CLASS_NAME_TAG, originalField.getDeclaringClass().getName()); } + fieldMap.put(IS_INTERNAL_TAG, field.isInternal()); + fieldMap.put(FIELD_TYPE_TAG, field.getType().getId()); + fieldMap.put(MODIFIERS_TAG, field.getModifiers()); + fieldMap.put(POSITION_TAG, field.getPosition()); + fieldMap.put(ANNOTATIONS_TAG, Arrays.stream(AnnotationAccess.getAnnotationTypes(field)).map(Class::getName).toList()); + + String tid = String.valueOf(field.getDeclaringClass().getId()); + fieldsMap.computeIfAbsent(tid, key -> new ConcurrentHashMap<>()).put(field.getName(), fieldMap); } - protected void persistField(AnalysisField field, @SuppressWarnings("unused") Universe hostedUniverse, EconomicMap fieldMap) { + protected void persistField(AnalysisField field, EconomicMap fieldMap) { fieldMap.put(ID_TAG, field.getId()); fieldMap.put(FIELD_ACCESSED_TAG, field.getAccessedReason() != null); fieldMap.put(FIELD_READ_TAG, field.getReadReason() != null); @@ -291,28 +419,29 @@ protected void persistField(AnalysisField field, @SuppressWarnings("unused") Uni fieldMap.put(FIELD_FOLDED_TAG, field.getFoldedReason() != null); } - private void persistConstant(AnalysisUniverse analysisUniverse, ImageHeapConstant imageHeapConstant, EconomicMap constantsMap) { - if (imageHeapConstant.isReaderInstalled() && !constantsMap.containsKey(Integer.toString(getConstantId(imageHeapConstant)))) { + protected void persistConstant(ImageHeapConstant imageHeapConstant) { + if (!constantsMap.containsKey(Integer.toString(getConstantId(imageHeapConstant)))) { EconomicMap constantMap = EconomicMap.create(); - persistConstant(analysisUniverse, imageHeapConstant, constantMap, constantsMap); + persistConstant(imageHeapConstant, constantMap); } } - protected void persistConstant(AnalysisUniverse analysisUniverse, ImageHeapConstant imageHeapConstant, EconomicMap constantMap, EconomicMap constantsMap) { + protected void persistConstant(ImageHeapConstant imageHeapConstant, EconomicMap constantMap) { constantsMap.put(Integer.toString(getConstantId(imageHeapConstant)), constantMap); constantMap.put(TID_TAG, imageHeapConstant.getType().getId()); - IdentityHashCodeProvider identityHashCodeProvider = (IdentityHashCodeProvider) analysisUniverse.getBigbang().getConstantReflectionProvider(); + IdentityHashCodeProvider identityHashCodeProvider = (IdentityHashCodeProvider) aUniverse.getBigbang().getConstantReflectionProvider(); int identityHashCode = identityHashCodeProvider.identityHashCode(imageHeapConstant); constantMap.put(IDENTITY_HASH_CODE_TAG, identityHashCode); switch (imageHeapConstant) { case ImageHeapInstance imageHeapInstance -> { - persistConstant(analysisUniverse, constantsMap, constantMap, INSTANCE_TAG, imageHeapInstance.getFieldValues()); - persistConstantRelinkingInfo(constantMap, imageHeapConstant, analysisUniverse.getBigbang()); + Object[] fieldValues = imageHeapInstance.isReaderInstalled() ? imageHeapInstance.getFieldValues() : null; + persistConstant(constantMap, INSTANCE_TAG, fieldValues); + persistConstantRelinkingInfo(constantMap, imageHeapConstant, aUniverse.getBigbang()); } case ImageHeapObjectArray imageHeapObjectArray -> - persistConstant(analysisUniverse, constantsMap, constantMap, ARRAY_TAG, imageHeapObjectArray.getElementValues()); + persistConstant(constantMap, ARRAY_TAG, imageHeapObjectArray.getElementValues()); case ImageHeapPrimitiveArray imageHeapPrimitiveArray -> { constantMap.put(CONSTANT_TYPE_TAG, PRIMITIVE_ARRAY_TAG); constantMap.put(DATA_TAG, getString(imageHeapPrimitiveArray.getType().getComponentType().getJavaKind(), imageHeapPrimitiveArray.getArray())); @@ -369,31 +498,33 @@ private static List getString(JavaKind kind, Object arrayObject) { }; } - protected void persistConstant(AnalysisUniverse analysisUniverse, EconomicMap constantsMap, EconomicMap constantMap, String constantType, Object[] values) { + protected void persistConstant(EconomicMap constantMap, String constantType, Object[] values) { constantMap.put(CONSTANT_TYPE_TAG, constantType); - List> data = new ArrayList<>(); - for (Object object : values) { - if (delegateProcessing(data, object)) { - /* The object was already persisted */ - } else if (object instanceof ImageHeapConstant imageHeapConstant) { - data.add(List.of(OBJECT_TAG, getConstantId(imageHeapConstant))); - /* - * Some constants are not in imageHeap#reachableObjects, but are still created in - * reachable constants. They can be created in the extension image, but should not - * be used. - */ - persistConstant(analysisUniverse, imageHeapConstant, constantsMap); - } else if (object == JavaConstant.NULL_POINTER) { - data.add(List.of(OBJECT_TAG, NULL_POINTER_CONSTANT)); - } else if (object instanceof PrimitiveConstant primitiveConstant) { - JavaKind kind = primitiveConstant.getJavaKind(); - data.add(List.of(kind.getTypeChar(), getPrimitiveConstantValue(primitiveConstant, kind))); - } else { - AnalysisError.guarantee(object instanceof AnalysisFuture, "Unexpected constant %s", object); - data.add(List.of(OBJECT_TAG, NOT_MATERIALIZED_CONSTANT)); + if (values != null) { + List> data = new ArrayList<>(); + for (Object object : values) { + if (delegateProcessing(data, object)) { + /* The object was already persisted */ + } else if (object instanceof ImageHeapConstant imageHeapConstant) { + data.add(List.of(OBJECT_TAG, getConstantId(imageHeapConstant))); + /* + * Some constants are not in imageHeap#reachableObjects, but are still created + * in reachable constants. They can be created in the extension image, but + * should not be used. + */ + persistConstant(imageHeapConstant); + } else if (object == JavaConstant.NULL_POINTER) { + data.add(List.of(OBJECT_TAG, NULL_POINTER_CONSTANT)); + } else if (object instanceof PrimitiveConstant primitiveConstant) { + JavaKind kind = primitiveConstant.getJavaKind(); + data.add(List.of(kind.getTypeChar(), getPrimitiveConstantValue(primitiveConstant, kind))); + } else { + AnalysisError.guarantee(object instanceof AnalysisFuture, "Unexpected constant %s", object); + data.add(List.of(OBJECT_TAG, NOT_MATERIALIZED_CONSTANT)); + } } + constantMap.put(DATA_TAG, data); } - constantMap.put(DATA_TAG, data); } private static Object getPrimitiveConstantValue(PrimitiveConstant primitiveConstant, JavaKind kind) { @@ -415,4 +546,13 @@ 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.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriterHelper.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriterHelper.java new file mode 100644 index 000000000000..cbf6f837d280 --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageLayerWriterHelper.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.heap; + +import org.graalvm.collections.EconomicMap; + +import com.oracle.graal.pointsto.meta.AnalysisMethod; + +public class ImageLayerWriterHelper { + protected final ImageLayerWriter imageLayerWriter; + + public ImageLayerWriterHelper(ImageLayerWriter imageLayerWriter) { + this.imageLayerWriter = imageLayerWriter; + } + + @SuppressWarnings("unused") + protected void persistMethod(AnalysisMethod method, EconomicMap methodMap) { + /* No additional information to persist */ + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java index 43f0f61fc094..e6ef63d0ed48 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java @@ -44,6 +44,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.graalvm.nativeimage.hosted.Feature.DuringAnalysisAccess; @@ -186,12 +187,22 @@ public record Signature(String name, AnalysisType[] parameterTypes) { */ private boolean returnsAllInstantiatedTypes; - @SuppressWarnings("this-escape") + @SuppressWarnings({"this-escape", "unchecked"}) protected AnalysisMethod(AnalysisUniverse universe, ResolvedJavaMethod wrapped, MultiMethodKey multiMethodKey, Map multiMethodMap) { this.wrapped = wrapped; declaringClass = universe.lookup(wrapped.getDeclaringClass()); - signature = getUniverse().lookup(wrapped.getSignature(), wrapped.getDeclaringClass()); + var wrappedSignature = wrapped.getSignature(); + if (wrappedSignature instanceof ResolvedSignature resolvedSignature) { + /* BaseLayerMethods return fully resolved signatures */ + if (resolvedSignature.getReturnType() instanceof AnalysisType) { + signature = (ResolvedSignature) resolvedSignature; + } else { + signature = getUniverse().lookup(wrappedSignature, wrapped.getDeclaringClass()); + } + } else { + signature = getUniverse().lookup(wrappedSignature, wrapped.getDeclaringClass()); + } hasNeverInlineDirective = universe.hostVM().hasNeverInlineDirective(wrapped); name = createName(wrapped, multiMethodKey); @@ -827,6 +838,10 @@ public AnalysisParsedGraph reparseGraph(BigBang bb) { return ensureGraphParsedHelper(bb, true); } + public Object getGraph() { + return parsedGraphCacheState.get(); + } + /** * Ensures that the method has been parsed, i.e., that the {@link StructuredGraph Graal IR} for * the method is available. @@ -849,7 +864,12 @@ private AnalysisParsedGraph ensureGraphParsedHelper(BigBang bb, boolean forceRep */ if (curState == GRAPH_CACHE_UNPARSED || (forceReparse && curState instanceof AnalysisParsedGraph)) { - AnalysisParsedGraph graph = parseGraph(bb, curState); + AnalysisParsedGraph graph; + if (isInBaseLayer && getUniverse().getImageLayerLoader().hasAnalysisParsedGraph(this)) { + graph = getBaseLayerGraph(curState); + } else { + graph = parseGraph(bb, curState); + } if (graph != null) { return graph; } @@ -871,7 +891,15 @@ private AnalysisParsedGraph ensureGraphParsedHelper(BigBang bb, boolean forceRep } } + private AnalysisParsedGraph getBaseLayerGraph(Object expectedValue) { + return setGraph(expectedValue, () -> getUniverse().getImageLayerLoader().getAnalysisParsedGraph(this)); + } + private AnalysisParsedGraph parseGraph(BigBang bb, Object expectedValue) { + return setGraph(expectedValue, () -> AnalysisParsedGraph.parseBytecode(bb, this)); + } + + private AnalysisParsedGraph setGraph(Object expectedValue, Supplier graphSupplier) { ReentrantLock lock = new ReentrantLock(); lock.lock(); try { @@ -884,7 +912,7 @@ private AnalysisParsedGraph parseGraph(BigBang bb, Object expectedValue) { return null; } - AnalysisParsedGraph graph = AnalysisParsedGraph.parseBytecode(bb, this); + AnalysisParsedGraph graph = graphSupplier.get(); /* * Since we still hold the parsing lock, the transition form "parsing" to "parsed" @@ -964,6 +992,10 @@ public void setAnalyzedGraph(EncodedGraph analyzedGraph) { this.analyzedGraph = analyzedGraph; } + public void clearAnalyzedGraph() { + this.analyzedGraph = null; + } + public EncodedGraph getAnalyzedGraph() { return analyzedGraph; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java index b213b49501a9..80b63d735a8f 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisType.java @@ -1257,6 +1257,18 @@ public AnalysisMethod[] getDeclaredConstructors(boolean forceLink) { return universe.lookup(wrapped.getDeclaredConstructors(forceLink)); } + public AnalysisMethod findConstructor(Signature signature) { + if (wrapped instanceof BaseLayerType) { + return null; + } + for (AnalysisMethod ctor : getDeclaredConstructors(false)) { + if (ctor.getSignature().equals(signature)) { + return ctor; + } + } + return null; + } + @Override public AnalysisMethod findMethod(String name, Signature signature) { for (AnalysisMethod method : getDeclaredMethods(false)) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index e2e1e0615be5..94c7645260f2 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -50,6 +50,7 @@ import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.heap.ImageLayerLoader; +import com.oracle.graal.pointsto.heap.ImageLayerWriter; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.infrastructure.ResolvedSignature; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; @@ -120,6 +121,7 @@ public class AnalysisUniverse implements Universe { private final JavaKind wordKind; private AnalysisPolicy analysisPolicy; private ImageHeapScanner heapScanner; + private ImageLayerWriter imageLayerWriter; private ImageLayerLoader imageLayerLoader; private HeapSnapshotVerifier heapVerifier; private BigBang bb; @@ -372,7 +374,7 @@ private AnalysisField createField(ResolvedJavaField field) { AnalysisField newValue = analysisFactory.createField(this, field); AnalysisField oldValue = fields.putIfAbsent(field, newValue); if (oldValue == null && newValue.isInBaseLayer()) { - getImageLayerLoader().loadFieldFlags(newValue); + getImageLayerLoader().initializeBaseLayerField(newValue); } return oldValue != null ? oldValue : newValue; } @@ -420,7 +422,7 @@ private AnalysisMethod createMethod(ResolvedJavaMethod method) { if (oldValue == null) { if (newValue.isInBaseLayer()) { - getImageLayerLoader().patchBaseLayerMethod(newValue); + getImageLayerLoader().initializeBaseLayerMethod(newValue); } prepareMethodImplementations(newValue); } @@ -747,6 +749,14 @@ public ImageHeapScanner getHeapScanner() { return heapScanner; } + public void setImageLayerWriter(ImageLayerWriter imageLayerWriter) { + this.imageLayerWriter = imageLayerWriter; + } + + public ImageLayerWriter getImageLayerWriter() { + return imageLayerWriter; + } + public void setImageLayerLoader(ImageLayerLoader imageLayerLoader) { this.imageLayerLoader = imageLayerLoader; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerElement.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerElement.java new file mode 100644 index 000000000000..25257ed47860 --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerElement.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.meta; + +import java.lang.annotation.Annotation; + +/** + * Base class for types representing persisted information from the base layer. + */ +public class BaseLayerElement { + private final Annotation[] annotations; + + public BaseLayerElement(Annotation[] annotations) { + this.annotations = annotations; + } + + public Annotation[] getBaseLayerAnnotations() { + return annotations; + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerField.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerField.java new file mode 100644 index 000000000000..f7cbc7d3d1b2 --- /dev/null +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerField.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.pointsto.meta; + +import java.lang.annotation.Annotation; + +import jdk.graal.compiler.debug.GraalError; +import jdk.vm.ci.meta.JavaType; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaType; + +/** + * This type is used in the context of Layered Image, when loading a base layer in another layer. + *

+ * If a field cannot be looked up by name, a {@link BaseLayerField} is created and put in an + * {@link AnalysisField} to represent this missing field, using the information from the base layer. + */ +public class BaseLayerField extends BaseLayerElement implements ResolvedJavaField { + private final int id; + private final String name; + private final AnalysisType declaringClass; + private final AnalysisType type; + private final boolean isInternal; + private final int modifiers; + + public BaseLayerField(int id, String name, AnalysisType declaringClass, AnalysisType type, boolean isInternal, int modifiers, Annotation[] annotations) { + super(annotations); + this.id = id; + this.name = name; + this.declaringClass = declaringClass; + this.type = type; + this.isInternal = isInternal; + this.modifiers = modifiers; + } + + public int getBaseLayerId() { + return id; + } + + @Override + public int getModifiers() { + return modifiers; + } + + @Override + public int getOffset() { + throw GraalError.unimplemented("This field is incomplete and should not be used."); + } + + @Override + public boolean isInternal() { + return isInternal; + } + + @Override + public boolean isSynthetic() { + throw GraalError.unimplemented("This field is incomplete and should not be used."); + } + + @Override + public String getName() { + return name; + } + + @Override + public JavaType getType() { + return type.getWrapped(); + } + + @Override + public ResolvedJavaType getDeclaringClass() { + return declaringClass.getWrapped(); + } + + @Override + public T getAnnotation(Class annotationClass) { + throw GraalError.unimplemented("This field is incomplete and should not be used."); + } + + @Override + public Annotation[] getAnnotations() { + throw GraalError.unimplemented("This field is incomplete and should not be used."); + } + + @Override + public Annotation[] getDeclaredAnnotations() { + throw GraalError.unimplemented("This field is incomplete and should not be used."); + } +} diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerMethod.java index 2b0015eb6a4c..5418cba73cc4 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerMethod.java @@ -27,6 +27,8 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import com.oracle.graal.pointsto.infrastructure.ResolvedSignature; + import jdk.graal.compiler.debug.GraalError; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.ConstantPool; @@ -46,13 +48,38 @@ * created yet in the new layer. If this method cannot be looked up by name, a * {@link BaseLayerMethod} is created and put in an {@link AnalysisMethod} to represent this missing * method, using the information from the base layer. - *

- * At the moment, very little information about the method from the base layer is needed, but in the - * future, if those methods are used in other places, more information could be persisted from the - * base layer in the {@link com.oracle.graal.pointsto.heap.ImageLayerWriter} and the - * {@link com.oracle.graal.pointsto.heap.ImageLayerLoader} */ -public class BaseLayerMethod implements ResolvedJavaMethod { +public class BaseLayerMethod extends BaseLayerElement implements ResolvedJavaMethod { + private final int id; + private final ResolvedJavaType declaringClass; + private final String name; + private final boolean isVarArgs; + private final ResolvedSignature signature; + private final boolean canBeStaticallyBound; + private final boolean isConstructor; + private final int modifiers; + private final boolean isSynthetic; + private final int codeSize; + + public BaseLayerMethod(int id, AnalysisType declaringClass, String name, boolean isVarArgs, ResolvedSignature signature, boolean canBeStaticallyBound, boolean isConstructor, + int modifiers, boolean isSynthetic, int codeSize, Annotation[] annotations) { + super(annotations); + this.id = id; + this.declaringClass = declaringClass.getWrapped(); + this.name = name; + this.isVarArgs = isVarArgs; + this.signature = signature; + this.canBeStaticallyBound = canBeStaticallyBound; + this.isConstructor = isConstructor; + this.modifiers = modifiers; + this.isSynthetic = isSynthetic; + this.codeSize = codeSize; + } + + public int getBaseLayerId() { + return id; + } + @Override public byte[] getCode() { throw GraalError.unimplemented("This method is incomplete and should not be used."); @@ -60,22 +87,22 @@ public byte[] getCode() { @Override public int getCodeSize() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return codeSize; } @Override public String getName() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return name; } @Override public ResolvedJavaType getDeclaringClass() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return declaringClass; } @Override public Signature getSignature() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return signature; } @Override @@ -90,12 +117,12 @@ public int getMaxStackSize() { @Override public boolean isSynthetic() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return isSynthetic; } @Override public boolean isVarArgs() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return isVarArgs; } @Override @@ -115,17 +142,17 @@ public boolean isClassInitializer() { @Override public boolean isConstructor() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return isConstructor; } @Override public boolean canBeStaticallyBound() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return canBeStaticallyBound; } @Override public ExceptionHandler[] getExceptionHandlers() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return new ExceptionHandler[0]; } @Override @@ -175,12 +202,12 @@ public boolean shouldBeInlined() { @Override public LineNumberTable getLineNumberTable() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return null; } @Override public LocalVariableTable getLocalVariableTable() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return null; } @Override @@ -215,6 +242,6 @@ public Annotation[] getDeclaredAnnotations() { @Override public int getModifiers() { - throw GraalError.unimplemented("This method is incomplete and should not be used."); + return modifiers; } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerType.java index ecc7251f98c7..7be1054a7115 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/BaseLayerType.java @@ -43,7 +43,7 @@ * this case, a {@link BaseLayerType} is created using information from the base layer and wrapped * in an {@link AnalysisType} to replace this missing type is the new layer. */ -public class BaseLayerType implements ResolvedJavaType, OriginalClassProvider { +public class BaseLayerType extends BaseLayerElement implements ResolvedJavaType, OriginalClassProvider { /** * The type corresponding to this {@link BaseLayerType} can be created later while building the * new layer. To avoid both types having the same name, the name of the {@link BaseLayerType} is @@ -65,7 +65,8 @@ public class BaseLayerType implements ResolvedJavaType, OriginalClassProvider { private final ResolvedJavaType objectType; public BaseLayerType(String name, int baseLayerId, int modifiers, boolean isInterface, boolean isEnum, boolean isInitialized, boolean isLinked, String sourceFileName, - ResolvedJavaType enclosingType, ResolvedJavaType componentType, ResolvedJavaType superClass, ResolvedJavaType[] interfaces, ResolvedJavaType objectType) { + ResolvedJavaType enclosingType, ResolvedJavaType componentType, ResolvedJavaType superClass, ResolvedJavaType[] interfaces, ResolvedJavaType objectType, Annotation[] annotations) { + super(annotations); this.name = name.substring(0, name.length() - 1) + BASE_LAYER_SUFFIX; this.baseLayerId = baseLayerId; this.modifiers = modifiers; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java index b76b6a752b90..f253b52645f7 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java @@ -227,6 +227,11 @@ public final void applyResults(AnalysisMethod method) { return; } + if (method.isInBaseLayer()) { + useSharedLayerGraph(method); + return; + } + graph.resetDebug(debug); if (beforeCounters != null) { beforeCounters.collect(graph); @@ -241,6 +246,8 @@ public final void applyResults(AnalysisMethod method) { } method.setAnalyzedGraph(GraphEncoder.encodeSingleGraph(graph, AnalysisParsedGraph.HOST_ARCHITECTURE)); + persistStrengthenGraph(method); + if (nodeReferences != null) { /* Ensure the temporarily decoded graph is not kept alive via the node references. */ for (var nodeReference : nodeReferences) { @@ -249,6 +256,10 @@ public final void applyResults(AnalysisMethod method) { } } + protected abstract void useSharedLayerGraph(AnalysisMethod method); + + protected abstract void persistStrengthenGraph(AnalysisMethod method); + /* * Returns a type that can replace the original type in stamps as an exact type. When the * returned type is the original type itself, the original type has no subtype and can be used diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java index 7298f90070aa..6f1fb90054ba 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java @@ -107,6 +107,15 @@ public class SubstrateOptions { @Option(help = "Build shared library")// public static final HostedOptionKey SharedLibrary = new HostedOptionKey<>(false); + @Option(help = "Persist and reload graphs across layers. If false, graphs defined in the base layer can be reparsed by the current layer and inlined before analysis, " + + "but will not be inlined after analysis has completed via our other inlining infrastructure")// + public static final HostedOptionKey UseSharedLayerGraphs = new HostedOptionKey<>(true) { + @Override + protected void onValueUpdate(EconomicMap, Object> values, Boolean oldValue, Boolean newValue) { + NeverInline.update(values, "SubstrateStringConcatHelper.simpleConcat"); + } + }; + @APIOption(name = "static")// @Option(help = "Build statically linked executable (requires static libc and zlib)")// public static final HostedOptionKey StaticExecutable = new HostedOptionKey<>(false, key -> { 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 31cd3e41c2b8..d0b1001dddf6 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 @@ -25,11 +25,14 @@ package com.oracle.svm.core.imagelayer; import org.graalvm.nativeimage.ImageSingletons; - -import jdk.graal.compiler.api.replacements.Fold; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.svm.util.ModuleSupport; + +import jdk.graal.compiler.api.replacements.Fold; +import jdk.graal.compiler.util.ObjectCopier; + /** * Support for tracking the image layer stage of this native-image build. When image layers are * used, executing a native-image will require multiple object files produced by different native @@ -67,6 +70,30 @@ 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() { + 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.core/src/com/oracle/svm/core/threadlocal/FastThreadLocal.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/threadlocal/FastThreadLocal.java index f25c092ac4de..5bb62afdf119 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/threadlocal/FastThreadLocal.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/threadlocal/FastThreadLocal.java @@ -33,7 +33,7 @@ * restrictions of VM thread local variables. */ public abstract class FastThreadLocal { - class FastThreadLocalLocationIdentity extends LocationIdentity { + public class FastThreadLocalLocationIdentity extends LocationIdentity { @Override public boolean isImmutable() { return false; diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java index 9b5d9f1eaef4..17d122eea046 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompiledMethodSupport.java @@ -229,7 +229,7 @@ private void compileRuntimeCompiledMethod(DebugContext debug) { * The graph in the analysis universe is no longer necessary once it is transplanted * into the hosted universe. */ - aMethod.setAnalyzedGraph(null); + aMethod.clearAnalyzedGraph(); if (!trackNodeSourcePosition) { /* 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 f4e10792a958..95dc1af08714 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 @@ -39,6 +39,7 @@ import com.oracle.graal.pointsto.PointsToAnalysis; import com.oracle.graal.pointsto.flow.MethodFlowsGraph; import com.oracle.graal.pointsto.flow.MethodTypeFlowBuilder; +import com.oracle.graal.pointsto.heap.ImageLayerWriter; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccessExtensionProvider; import com.oracle.graal.pointsto.meta.AnalysisType; @@ -62,11 +63,14 @@ import com.oracle.svm.hosted.config.DynamicHubLayout; 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.SVMImageLayerWriterHelper; import com.oracle.svm.hosted.image.LIRNativeImageCodeCache; import com.oracle.svm.hosted.image.NativeImageCodeCache; import com.oracle.svm.hosted.image.NativeImageCodeCacheFactory; import com.oracle.svm.hosted.image.NativeImageHeap; import com.oracle.svm.hosted.image.ObjectFileFactory; +import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedInstanceClass; import com.oracle.svm.hosted.meta.HostedMetaAccess; @@ -228,6 +232,14 @@ public SVMHost createHostVM(OptionValues options, ImageClassLoader loader, Class return new SVMHost(options, loader, classInitializationSupport, annotationSubstitutions, missingRegistrationSupport); } + public SVMImageLayerWriterHelper createSVMImageLayerWriterHelper(ImageLayerWriter imageLayerWriter) { + return new SVMImageLayerWriterHelper(imageLayerWriter); + } + + public SVMImageLayerLoaderHelper createSVMImageLayerLoaderHelper() { + return new SVMImageLayerLoaderHelper(HostedImageLayerBuildingSupport.singleton().getLoader()); + } + 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 0f54dfa7c25e..39c01fec4d48 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 @@ -99,6 +99,7 @@ import com.oracle.graal.pointsto.heap.ImageHeap; import com.oracle.graal.pointsto.heap.ImageHeapScanner; import com.oracle.graal.pointsto.heap.ImageLayerLoader; +import com.oracle.graal.pointsto.heap.ImageLayerWriter; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; import com.oracle.graal.pointsto.infrastructure.WrappedJavaMethod; import com.oracle.graal.pointsto.meta.AnalysisFactory; @@ -596,6 +597,12 @@ protected void doRun(Map entryPoints, JavaMainSupport j loader.watchdog.recordActivity(); hUniverse = new HostedUniverse(bb); + if (ImageLayerBuildingSupport.buildingSharedLayer()) { + HostedImageLayerBuildingSupport.singleton().getWriter().setHostedUniverse(hUniverse); + } + if (ImageLayerBuildingSupport.buildingExtensionLayer()) { + HostedImageLayerBuildingSupport.singleton().getLoader().setHostedUniverse(hUniverse); + } hMetaAccess = new HostedMetaAccess(hUniverse, bb.getMetaAccess()); BeforeUniverseBuildingAccessImpl beforeUniverseBuildingConfig = new BeforeUniverseBuildingAccessImpl(featureHandler, loader, debug, hMetaAccess); @@ -636,6 +643,10 @@ protected void doRun(Map entryPoints, JavaMainSupport j recordRestrictHeapAccessCallees(aUniverse.getMethods()); + if (ImageLayerBuildingSupport.buildingSharedLayer()) { + HostedImageLayerBuildingSupport.singleton().getWriter().persistMethodGraphs(); + } + /* * After this point, all TypeFlow (and therefore also TypeState) objects are * unreachable and can be garbage collected. This is important to keep the overall @@ -717,7 +728,7 @@ protected void doRun(Map entryPoints, JavaMainSupport j image.build(imageName, debug); if (ImageLayerBuildingSupport.buildingSharedLayer()) { - HostedImageLayerBuildingSupport.singleton().getWriter().persistAnalysisInfo(hUniverse, bb.getUniverse()); + HostedImageLayerBuildingSupport.singleton().getWriter().persistAnalysisInfo(); } if (NativeImageOptions.PrintUniverse.getValue()) { @@ -947,11 +958,26 @@ protected void setupNativeImage(OptionValues options, Map types) { public void persistTypeInfo(Collection types) { for (HostedType type : types) { - if (type.getTypeID() != -1) { + AnalysisType analysisType = type.getWrapped(); + if (type.getTypeID() != -1 && analysisType.isReachable()) { int identifierID = type.getWrapped().getId(); int typeID = type.getTypeID(); int numClassTypes = type.getNumClassTypes(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateStrengthenGraphs.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateStrengthenGraphs.java index 2698655b66e2..de6cd4a3bbf5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateStrengthenGraphs.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SubstrateStrengthenGraphs.java @@ -26,7 +26,9 @@ import java.util.function.Supplier; +import com.oracle.graal.pointsto.heap.ImageLayerLoader; import com.oracle.graal.pointsto.infrastructure.Universe; +import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.results.StrengthenGraphs; import com.oracle.svm.common.meta.MultiMethod; @@ -40,6 +42,7 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.analysis.Inflation; import com.oracle.svm.hosted.code.SubstrateCompilationDirectives; +import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; import com.oracle.svm.hosted.meta.HostedType; import jdk.graal.compiler.graph.Node; @@ -62,6 +65,25 @@ public SubstrateStrengthenGraphs(Inflation bb, Universe converter) { super(bb, converter); } + @Override + protected void useSharedLayerGraph(AnalysisMethod method) { + ImageLayerLoader imageLayerLoader = HostedImageLayerBuildingSupport.singleton().getLoader(); + /* + * GR-55294: When the analysis elements from the base layer will be able to be materialized + * after the analysis, fewer graphs will have to be loaded here as well. + */ + if (imageLayerLoader.hasStrengthenedGraph(method)) { + imageLayerLoader.setStrengthenedGraph(method); + } + } + + @Override + protected void persistStrengthenGraph(AnalysisMethod method) { + if (HostedImageLayerBuildingSupport.buildingSharedLayer() && method.isReachable()) { + HostedImageLayerBuildingSupport.singleton().getWriter().persistMethodStrengthenedGraph(method); + } + } + @Override protected AnalysisType getSingleImplementorType(AnalysisType originalType) { HostedType singleImplementorType = ((HostedType) converter.lookup(originalType)).getSingleImplementor(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/FieldValueInterceptionSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/FieldValueInterceptionSupport.java index 898ca5477382..78f2b7afa787 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/FieldValueInterceptionSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/FieldValueInterceptionSupport.java @@ -41,6 +41,7 @@ import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.meta.BaseLayerField; import com.oracle.graal.pointsto.util.GraalAccess; import com.oracle.svm.core.BuildPhaseProvider; import com.oracle.svm.core.RuntimeAssertionsSupport; @@ -154,7 +155,7 @@ private Object computeAndCacheFieldValueInterceptor(AnalysisField field) { field.beforeFieldValueAccess(); ResolvedJavaField oField = OriginalFieldProvider.getOriginalField(field); - assert oField == null || oField instanceof HotSpotResolvedJavaField : oField; + assert oField == null || oField instanceof HotSpotResolvedJavaField || oField instanceof BaseLayerField : oField; FieldValueComputer computer = createFieldValueComputer(field); Object result; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/SubstrateAnnotationExtractor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/SubstrateAnnotationExtractor.java index 241e0a63f8d7..349bcf22fa06 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/SubstrateAnnotationExtractor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/SubstrateAnnotationExtractor.java @@ -49,6 +49,10 @@ import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; import com.oracle.graal.pointsto.infrastructure.OriginalMethodProvider; import com.oracle.graal.pointsto.infrastructure.WrappedElement; +import com.oracle.graal.pointsto.meta.BaseLayerElement; +import com.oracle.graal.pointsto.meta.BaseLayerField; +import com.oracle.graal.pointsto.meta.BaseLayerMethod; +import com.oracle.graal.pointsto.meta.BaseLayerType; import com.oracle.svm.hosted.annotation.AnnotationMetadata.AnnotationExtractionError; import com.oracle.svm.util.ReflectionUtil; @@ -181,7 +185,9 @@ private AnnotationValue[] getAnnotationData(AnnotatedElement element, boolean de } AnnotatedElement root = findRoot(cur); - if (root != null) { + if (root instanceof BaseLayerElement baseLayerElement) { + result = Arrays.stream(baseLayerElement.getBaseLayerAnnotations()).map(AnnotationValue::new).toList().toArray(new AnnotationValue[0]); + } else if (root != null) { result = concat(result, declaredOnly ? getDeclaredAnnotationDataFromRoot(root) : getAnnotationDataFromRoot(root)); } return result; @@ -441,7 +447,9 @@ private static AnnotatedElement unwrap(AnnotatedElement element) { private static AnnotatedElement findRoot(AnnotatedElement element) { assert !(element instanceof WrappedElement || element instanceof AnnotationWrapper); try { - if (element instanceof ResolvedJavaType type) { + if (element instanceof BaseLayerType || element instanceof BaseLayerMethod || element instanceof BaseLayerField) { + return element; + } else if (element instanceof ResolvedJavaType type) { return OriginalClassProvider.getJavaClass(type); } else if (element instanceof ResolvedJavaMethod method) { return OriginalMethodProvider.getJavaMethod(method); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisToHostedGraphTransplanter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisToHostedGraphTransplanter.java index 6e927794baef..3bef0d99b08c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisToHostedGraphTransplanter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisToHostedGraphTransplanter.java @@ -93,7 +93,7 @@ public StructuredGraph transplantGraph(DebugContext debug, HostedMethod hMethod, * The graph in the analysis universe is no longer necessary once it is transplanted into * the hosted universe. */ - aMethod.setAnalyzedGraph(null); + aMethod.clearAnalyzedGraph(); /* * The static analysis always needs NodeSourcePosition. But for AOT compilation, we only diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index 678bd0628fa1..5786036da661 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -418,7 +418,7 @@ public void finish(DebugContext debug) { * but are no longer reachable now. */ for (HostedMethod method : universe.getMethods()) { - method.wrapped.setAnalyzedGraph(null); + method.wrapped.clearAnalyzedGraph(); } if (ImageSingletons.contains(HostedHeapDumpFeature.class)) { @@ -639,20 +639,22 @@ private void parseAheadOfTimeCompiledMethods() { } if (hMethod.isEntryPoint() || SubstrateCompilationDirectives.singleton().isForcedCompilation(hMethod) || hMethod.wrapped.isDirectRootMethod() && hMethod.wrapped.isSimplyImplementationInvoked()) { - if (hMethod.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(hMethod)) { baseLayerMethods.add(hMethod); - } else { + } + if (hasGraph(hMethod)) { ensureParsed(hMethod, null, new EntryPointReason()); } } if (hMethod.wrapped.isVirtualRootMethod()) { for (HostedMethod impl : hMethod.getImplementations()) { - if (impl.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(impl)) { baseLayerMethods.add(impl); - continue; } VMError.guarantee(impl.wrapped.isImplementationInvoked()); - ensureParsed(impl, null, new EntryPointReason()); + if (hasGraph(impl)) { + ensureParsed(impl, null, new EntryPointReason()); + } } } } @@ -662,20 +664,22 @@ private void parseAheadOfTimeCompiledMethods() { for (SubstrateForeignCallLinkage linkage : foreignCallsProvider.getForeignCalls().values()) { HostedMethod method = (HostedMethod) linkage.getDescriptor().findMethod(runtimeConfig.getProviders().getMetaAccess()); if (method.wrapped.isDirectRootMethod() && method.wrapped.isSimplyImplementationInvoked()) { - if (method.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(method)) { baseLayerMethods.add(method); - } else { + } + if (hasGraph(method)) { ensureParsed(method, null, new EntryPointReason()); } } if (method.wrapped.isVirtualRootMethod()) { for (HostedMethod impl : method.getImplementations()) { - if (impl.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(impl)) { baseLayerMethods.add(impl); - continue; } VMError.guarantee(impl.wrapped.isImplementationInvoked()); - ensureParsed(impl, null, new EntryPointReason()); + if (hasGraph(impl)) { + ensureParsed(impl, null, new EntryPointReason()); + } } } } @@ -771,10 +775,6 @@ protected EncodedGraph lookupEncodedGraph(ResolvedJavaMethod method, BytecodePro @Override protected LoopScope trySimplifyInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { - if (((HostedMethod) callTarget.targetMethod()).wrapped.isInBaseLayer()) { - /* Cannot inline base layer method. */ - return null; - } return super.trySimplifyInvoke(methodScope, loopScope, invokeData, callTarget); } } @@ -940,9 +940,10 @@ public void scheduleEntryPoints() { if (hMethod.isEntryPoint() || SubstrateCompilationDirectives.singleton().isForcedCompilation(hMethod) || hMethod.wrapped.isDirectRootMethod() && hMethod.wrapped.isSimplyImplementationInvoked()) { - if (hMethod.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(hMethod)) { baseLayerMethods.add(hMethod); - } else { + } + if (canBeCompiled(hMethod)) { ensureCompiled(hMethod, new EntryPointReason()); } } @@ -950,12 +951,13 @@ public void scheduleEntryPoints() { MultiMethod.MultiMethodKey key = hMethod.getMultiMethodKey(); assert key != DEOPT_TARGET_METHOD && key != SubstrateCompilationDirectives.RUNTIME_COMPILED_METHOD : "unexpected method as virtual root " + hMethod; for (HostedMethod impl : hMethod.getImplementations()) { - if (impl.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(impl)) { baseLayerMethods.add(impl); - continue; } VMError.guarantee(impl.wrapped.isImplementationInvoked()); - ensureCompiled(impl, new EntryPointReason()); + if (canBeCompiled(impl)) { + ensureCompiled(impl, new EntryPointReason()); + } } } if (hMethod.wrapped.isIntrinsicMethod() && hMethod.wrapped.isInBaseLayer()) { @@ -1068,12 +1070,13 @@ private void defaultParseFunction(DebugContext debug, HostedMethod method, Compi private void ensureParsed(HostedMethod method, CompileReason reason, CallTargetNode targetNode, HostedMethod invokeTarget, boolean isIndirect) { if (isIndirect) { for (HostedMethod invokeImplementation : invokeTarget.getImplementations()) { - if (invokeImplementation.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(invokeImplementation)) { baseLayerMethods.add(invokeImplementation); - continue; } handleSpecialization(method, targetNode, invokeTarget, invokeImplementation); - ensureParsed(invokeImplementation, method, new VirtualCallReason(method, invokeImplementation, reason)); + if (hasGraph(invokeImplementation)) { + ensureParsed(invokeImplementation, method, new VirtualCallReason(method, invokeImplementation, reason)); + } } } else { /* @@ -1086,17 +1089,30 @@ private void ensureParsed(HostedMethod method, CompileReason reason, CallTargetN * already applied during parsing before we reach this point, so we look at the "simple" * implementation invoked status. */ - if (invokeTarget.wrapped.isSimplyImplementationInvoked()) { - if (invokeTarget.wrapped.isInBaseLayer()) { + if (invokeTarget.wrapped.isSimplyImplementationInvoked() || invokeTarget.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(invokeTarget)) { baseLayerMethods.add(invokeTarget); - return; } handleSpecialization(method, targetNode, invokeTarget, invokeTarget); - ensureParsed(invokeTarget, method, new DirectCallReason(method, reason)); + if (hasGraph(invokeTarget)) { + ensureParsed(invokeTarget, method, new DirectCallReason(method, reason)); + } } } } + private static boolean compiledInPriorLayer(HostedMethod method) { + return method.isCompiledInPriorLayer() || (!SubstrateOptions.UseSharedLayerGraphs.getValue() && method.wrapped.isInBaseLayer()); + } + + private static boolean hasGraph(HostedMethod method) { + return !method.wrapped.isInBaseLayer() || method.wrapped.getAnalyzedGraph() != null; + } + + private static boolean canBeCompiled(HostedMethod method) { + return !method.wrapped.isInBaseLayer() || method.compilationInfo.getCompilationGraph() != null; + } + @SuppressWarnings("unused") protected void notifyBeforeEncode(HostedMethod method, StructuredGraph graph) { HostedProviders providers = (HostedProviders) runtimeConfig.lookupBackend(method).getProviders(); @@ -1337,18 +1353,20 @@ protected void ensureCalleesCompiled(HostedMethod method, CompileReason reason, if (infopoint instanceof Call call) { HostedMethod callTarget = (HostedMethod) call.target; if (call.direct || isDynamicallyResolvedCall(result, call)) { - if (callTarget.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(callTarget)) { baseLayerMethods.add(callTarget); - } else { + } + if (canBeCompiled(callTarget)) { ensureCompiled(callTarget, new DirectCallReason(method, reason)); } } else if (callTarget != null && callTarget.getImplementations() != null) { for (HostedMethod impl : callTarget.getImplementations()) { - if (impl.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(impl)) { baseLayerMethods.add(impl); - continue; } - ensureCompiled(impl, new VirtualCallReason(method, callTarget, reason)); + if (canBeCompiled(impl)) { + ensureCompiled(impl, new VirtualCallReason(method, callTarget, reason)); + } } } } @@ -1372,11 +1390,12 @@ protected final void ensureCompiledForMethodPointerConstants(HostedMethod method if (constant instanceof SubstrateMethodPointerConstant) { MethodPointer pointer = ((SubstrateMethodPointerConstant) constant).pointer(); HostedMethod referencedMethod = (HostedMethod) pointer.getMethod(); - if (referencedMethod.wrapped.isInBaseLayer()) { + if (compiledInPriorLayer(referencedMethod)) { baseLayerMethods.add(referencedMethod); - continue; } - ensureCompiled(referencedMethod, new MethodPointerConstantReason(method, referencedMethod, reason)); + if (canBeCompiled(referencedMethod)) { + ensureCompiled(referencedMethod, new MethodPointerConstantReason(method, referencedMethod, reason)); + } } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java index 2ebe820aca98..1aba7a6d4239 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethod.java @@ -105,4 +105,8 @@ public StructuredGraph buildGraph(DebugContext debug, AnalysisMethod method, Hos public ResolvedJavaMethod getTargetConstructor() { return targetConstructor; } + + public boolean throwAllocatedObject() { + return throwAllocatedObject; + } } 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 23a8f53f71ed..f55470187f25 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 @@ -24,12 +24,15 @@ */ package com.oracle.svm.hosted.heap; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.ANNOTATIONS_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_ID_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.C_ENTRY_POINT_LITERAL_CODE_POINTER; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_KEYS; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_OBJECTS; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.METHOD_POINTER_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.PERSISTED; +import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.nio.file.Path; @@ -38,44 +41,117 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.UnmodifiableEconomicMap; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.impl.CEntryPointLiteralCodePointer; +import com.oracle.graal.pointsto.flow.AnalysisParsedGraph; import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.heap.ImageHeapInstance; import com.oracle.graal.pointsto.heap.ImageLayerLoader; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.graal.pointsto.meta.BaseLayerMethod; -import com.oracle.graal.pointsto.util.AnalysisFuture; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton.PersistFlags; import com.oracle.svm.core.meta.MethodPointer; +import com.oracle.svm.core.threadlocal.VMThreadLocalInfo; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.SVMHost; +import com.oracle.svm.hosted.imagelayer.HostedDynamicLayerInfo; +import com.oracle.svm.hosted.meta.HostedArrayClass; +import com.oracle.svm.hosted.meta.HostedField; +import com.oracle.svm.hosted.meta.HostedInstanceClass; +import com.oracle.svm.hosted.meta.HostedInterface; +import com.oracle.svm.hosted.meta.HostedMethod; +import com.oracle.svm.hosted.meta.HostedPrimitiveType; +import com.oracle.svm.hosted.meta.HostedUniverse; import com.oracle.svm.hosted.meta.RelocatableConstant; +import com.oracle.svm.hosted.thread.LayeredVMThreadLocalCollector; +import com.oracle.svm.hosted.thread.VMThreadLocalCollector; import com.oracle.svm.hosted.util.IdentityHashCodeUtil; import com.oracle.svm.util.LogUtils; import com.oracle.svm.util.ReflectionUtil; +import jdk.graal.compiler.nodes.EncodedGraph; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.ResolvedJavaMethod; +import sun.reflect.annotation.AnnotationParser; +import sun.reflect.annotation.AnnotationType; public class SVMImageLayerLoader extends ImageLayerLoader { private final Field dynamicHubArrayHubField; + private HostedUniverse hostedUniverse; + public SVMImageLayerLoader(List loaderPaths) { super(new SVMImageLayerSnapshotUtil(), loaderPaths); dynamicHubArrayHubField = ReflectionUtil.lookupField(DynamicHub.class, "arrayHub"); } + public void setHostedUniverse(HostedUniverse hostedUniverse) { + this.hostedUniverse = hostedUniverse; + } + + public HostedUniverse getHostedUniverse() { + return hostedUniverse; + } + + @Override + protected Annotation[] getAnnotations(EconomicMap elementData) { + List annotationNames = get(elementData, ANNOTATIONS_TAG); + + return annotationNames.stream().map(s -> { + Class annotationType = cast(lookupBaseLayerTypeInHostVM(s)); + return AnnotationParser.annotationForMap(annotationType, AnnotationType.getInstance(annotationType).memberDefaults()); + }).toList().toArray(new Annotation[0]); + } + + @Override + protected void initializeBaseLayerMethod(AnalysisMethod analysisMethod, EconomicMap methodData) { + if (!HostedDynamicLayerInfo.singleton().isCompiled(analysisMethod.getId()) && hasAnalysisParsedGraph(analysisMethod)) { + /* + * GR-55294: When the analysis elements from the base layer will be able to be + * materialized after the analysis, this will not be needed anymore. + */ + analysisMethod.ensureGraphParsed(universe.getBigbang()); + analysisMethod.setAnalyzedGraph(((AnalysisParsedGraph) analysisMethod.getGraph()).getEncodedGraph()); + } + super.initializeBaseLayerMethod(analysisMethod, methodData); + } + + @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); + } + } + } + + @Override + protected void loadAllAnalysisElements(String encoding) { + for (String line : encoding.lines().toList()) { + if (line.contains(HostedInstanceClass.class.getName()) || line.contains(HostedPrimitiveType.class.getName()) || line.contains(HostedArrayClass.class.getName()) || + line.contains(HostedInterface.class.getName())) { + getAnalysisType(getId(line)); + } else if (line.contains(HostedMethod.class.getName())) { + getAnalysisMethod(getId(line)); + } else if (line.contains(HostedField.class.getName())) { + getAnalysisField(getId(line)); + } + } + super.loadAllAnalysisElements(encoding); + } + @Override protected void prepareConstantRelinking(EconomicMap constantData, int identityHashCode, int id) { Integer tid = get(constantData, CLASS_ID_TAG); @@ -87,40 +163,23 @@ protected void prepareConstantRelinking(EconomicMap constantData } @Override - protected boolean delegateProcessing(String constantType, Object constantValue, Object[] values, int i) { + protected boolean delegateProcessing(String constantType, Object constantValue, List constantData, Object[] values, int i) { if (constantType.equals(METHOD_POINTER_TAG)) { AnalysisType methodPointerType = metaAccess.lookupJavaType(MethodPointer.class); int mid = (int) constantValue; - AnalysisMethod method = methods.get(mid); - if (method != null) { - values[i] = new RelocatableConstant(new MethodPointer(method), methodPointerType); - } else { - AnalysisFuture task = new AnalysisFuture<>(() -> { - ResolvedJavaMethod resolvedMethod = methods.get(mid); - if (resolvedMethod == null) { - /* - * The method is not loaded yet, so we use a placeholder until it is - * available. - */ - resolvedMethod = new BaseLayerMethod(); - missingMethodTasks.computeIfAbsent(mid, unused -> ConcurrentHashMap.newKeySet()).add(new AnalysisFuture<>(() -> { - AnalysisMethod analysisMethod = methods.get(mid); - VMError.guarantee(analysisMethod != null, "Method with method id %d should be loaded.", mid); - RelocatableConstant constant = new RelocatableConstant(new MethodPointer(analysisMethod), methodPointerType); - values[i] = constant; - return constant; - })); - } - JavaConstant methodPointer = new RelocatableConstant(new MethodPointer(resolvedMethod), methodPointerType); - values[i] = methodPointer; - return methodPointer; - }); - values[i] = task; - missingMethodTasks.computeIfAbsent(mid, unused -> ConcurrentHashMap.newKeySet()).add(task); - } + AnalysisMethod method = getAnalysisMethod(mid); + values[i] = new RelocatableConstant(new MethodPointer(method), methodPointerType); + return true; + } else if (constantType.equals(C_ENTRY_POINT_LITERAL_CODE_POINTER)) { + AnalysisType cEntryPointerLiteralPointerType = metaAccess.lookupJavaType(CEntryPointLiteralCodePointer.class); + String methodName = (String) constantValue; + Class definingClass = lookupBaseLayerTypeInHostVM((String) constantData.get(2)); + List parameters = cast(constantData.get(3)); + Class[] parameterTypes = parameters.stream().map(ImageLayerLoader::lookupBaseLayerTypeInHostVM).toList().toArray(new Class[0]); + values[i] = new RelocatableConstant(new CEntryPointLiteralCodePointer(definingClass, methodName, parameterTypes), cEntryPointerLiteralPointerType); return true; } - return super.delegateProcessing(constantType, constantValue, values, i); + return super.delegateProcessing(constantType, constantValue, constantData, values, i); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoaderHelper.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoaderHelper.java new file mode 100644 index 000000000000..e9e358dafc3e --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerLoaderHelper.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.heap; + +import static com.oracle.graal.pointsto.heap.ImageLayerLoader.get; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FACTORY_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.METHOD_TYPE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TARGET_CONSTRUCTOR_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.THROW_ALLOCATED_OBJECT_TAG; + +import org.graalvm.collections.EconomicMap; + +import com.oracle.graal.pointsto.heap.ImageLayerLoader; +import com.oracle.graal.pointsto.heap.ImageLayerLoaderHelper; +import com.oracle.svm.hosted.code.FactoryMethodSupport; + +public class SVMImageLayerLoaderHelper extends ImageLayerLoaderHelper { + public SVMImageLayerLoaderHelper(ImageLayerLoader imageLayerLoader) { + super(imageLayerLoader); + } + + @Override + protected boolean loadMethod(EconomicMap methodData, int mid) { + String methodType = get(methodData, METHOD_TYPE_TAG); + if (methodType == null) { + return false; + } + if (methodType.equals(FACTORY_TAG)) { + int constructorId = get(methodData, TARGET_CONSTRUCTOR_TAG); + boolean throwAllocatedObject = get(methodData, THROW_ALLOCATED_OBJECT_TAG); + FactoryMethodSupport.singleton().lookup(imageLayerLoader.getMetaAccess(), imageLayerLoader.getAnalysisMethod(constructorId), throwAllocatedObject); + return true; + } + return false; + } +} 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 060556a3c57b..571dbcd6387d 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 @@ -32,21 +32,60 @@ import java.lang.reflect.Executable; import java.lang.reflect.Field; 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 com.oracle.graal.pointsto.heap.ImageLayerLoader; import com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil; +import com.oracle.graal.pointsto.heap.ImageLayerWriter; import com.oracle.graal.pointsto.meta.AnalysisField; 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.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.hosted.code.FactoryMethod; import com.oracle.svm.hosted.code.IncompatibleClassChangeFallbackMethod; +import com.oracle.svm.hosted.meta.HostedArrayClass; +import com.oracle.svm.hosted.meta.HostedInstanceClass; +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.util.ReflectionUtil; +import jdk.graal.compiler.api.replacements.SnippetReflectionProvider; +import jdk.graal.compiler.nodes.EncodedGraph; +import jdk.graal.compiler.util.ObjectCopier; import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaMethod; @@ -66,6 +105,47 @@ public class SVMImageLayerSnapshotUtil extends ImageLayerSnapshotUtil { protected final Map> fieldsToRelink = new HashMap<>(); + @SuppressWarnings("this-escape") + public SVMImageLayerSnapshotUtil() { + 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); + } + + @Override + protected boolean shouldAddExternalValue(Class type) { + return FastThreadLocal.class.isAssignableFrom(type) || super.shouldAddExternalValue(type); + } + @Override public String getTypeIdentifier(AnalysisType type) { if (type.toJavaName(true).contains(GENERATED_SERIALIZATION)) { @@ -139,4 +219,156 @@ public Set getRelinkedFields(AnalysisType type, AnalysisMetaAccess meta } return result; } + + @Override + public GraphEncoder getGraphEncoder(ImageLayerWriter imageLayerWriter) { + return new SVMGraphEncoder(externalValues, imageLayerWriter); + } + + @Override + public GraphDecoder getGraphDecoder(ImageLayerLoader imageLayerLoader, SnippetReflectionProvider snippetReflectionProvider) { + return new SVMGraphDecoder(EncodedGraph.class.getClassLoader(), (SVMImageLayerLoader) imageLayerLoader, snippetReflectionProvider); + } + + public static class SVMGraphEncoder extends GraphEncoder { + @SuppressWarnings("this-escape") + public SVMGraphEncoder(List externalValues, ImageLayerWriter imageLayerWriter) { + super(externalValues, imageLayerWriter); + addBuiltin(new HostedTypeBuiltIn(null)); + addBuiltin(new HostedMethodBuiltIn(null)); + addBuiltin(new HostedOptionValuesBuiltIn()); + addBuiltin(new HostedSnippetReflectionProviderBuiltIn(null)); + addBuiltin(new CInterfaceLocationIdentityBuiltIn()); + addBuiltin(new FastThreadLocalLocationIdentityBuiltIn()); + } + } + + public static class SVMGraphDecoder extends GraphDecoder { + @SuppressWarnings("this-escape") + public SVMGraphDecoder(ClassLoader classLoader, SVMImageLayerLoader svmImageLayerLoader, SnippetReflectionProvider snippetReflectionProvider) { + super(classLoader, svmImageLayerLoader); + addBuiltin(new HostedTypeBuiltIn(svmImageLayerLoader)); + addBuiltin(new HostedMethodBuiltIn(svmImageLayerLoader)); + addBuiltin(new HostedOptionValuesBuiltIn()); + addBuiltin(new HostedSnippetReflectionProviderBuiltIn(snippetReflectionProvider)); + addBuiltin(new CInterfaceLocationIdentityBuiltIn()); + addBuiltin(new FastThreadLocalLocationIdentityBuiltIn()); + } + } + + public static class HostedTypeBuiltIn extends ObjectCopier.Builtin { + private final SVMImageLayerLoader svmImageLayerLoader; + + protected HostedTypeBuiltIn(SVMImageLayerLoader svmImageLayerLoader) { + super(HostedType.class, HostedInstanceClass.class, HostedArrayClass.class); + this.svmImageLayerLoader = svmImageLayerLoader; + } + + @Override + protected String encode(ObjectCopier.Encoder encoder, Object obj) { + return String.valueOf(((HostedType) obj).getWrapped().getId()); + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + AnalysisType type = svmImageLayerLoader.getAnalysisType(Integer.parseInt(encoded)); + return svmImageLayerLoader.getHostedUniverse().lookup(type); + } + } + + public static class HostedMethodBuiltIn extends ObjectCopier.Builtin { + private final SVMImageLayerLoader svmImageLayerLoader; + + protected HostedMethodBuiltIn(SVMImageLayerLoader svmImageLayerLoader) { + super(HostedMethod.class); + this.svmImageLayerLoader = svmImageLayerLoader; + } + + @Override + protected String encode(ObjectCopier.Encoder encoder, Object obj) { + return String.valueOf(((HostedMethod) obj).getWrapped().getId()); + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + AnalysisMethod method = svmImageLayerLoader.getAnalysisMethod(Integer.parseInt(encoded)); + return svmImageLayerLoader.getHostedUniverse().lookup(method); + } + } + + public static class HostedOptionValuesBuiltIn extends ObjectCopier.Builtin { + protected HostedOptionValuesBuiltIn() { + super(HostedOptionValues.class); + } + + @Override + protected String encode(ObjectCopier.Encoder encoder, Object obj) { + return ""; + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + return HostedOptionValues.singleton(); + } + } + + public static class HostedSnippetReflectionProviderBuiltIn extends ObjectCopier.Builtin { + private final SnippetReflectionProvider snippetReflectionProvider; + + protected HostedSnippetReflectionProviderBuiltIn(SnippetReflectionProvider snippetReflectionProvider) { + super(SnippetReflectionProvider.class, HostedSnippetReflectionProvider.class); + this.snippetReflectionProvider = snippetReflectionProvider; + } + + @Override + protected String encode(ObjectCopier.Encoder encoder, Object obj) { + return ""; + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + return snippetReflectionProvider; + } + } + + public static class CInterfaceLocationIdentityBuiltIn extends ObjectCopier.Builtin { + protected CInterfaceLocationIdentityBuiltIn() { + super(CInterfaceLocationIdentity.class); + } + + @Override + protected String encode(ObjectCopier.Encoder encoder, Object obj) { + CInterfaceLocationIdentity cInterfaceLocationIdentity = (CInterfaceLocationIdentity) obj; + return cInterfaceLocationIdentity.toString(); + } + + @Override + protected Object decode(ObjectCopier.Decoder decoder, Class concreteType, String encoding, String encoded) { + return new CInterfaceLocationIdentity(encoded); + } + } + + public static class FastThreadLocalLocationIdentityBuiltIn extends ObjectCopier.Builtin { + protected FastThreadLocalLocationIdentityBuiltIn() { + super(FastThreadLocal.FastThreadLocalLocationIdentity.class); + } + + @Override + 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(); + } + + @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); + return fastThreadLocal.getLocationIdentity(); + } + } } 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 26afdb2117ec..40d246777f57 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 @@ -25,6 +25,7 @@ package com.oracle.svm.hosted.heap; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.CLASS_ID_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.C_ENTRY_POINT_LITERAL_CODE_POINTER; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.HUB_IDENTITY_HASH_CODE_TAG; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_KEYS; import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.IMAGE_SINGLETON_OBJECTS; @@ -35,6 +36,7 @@ import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.STATIC_PRIMITIVE_FIELDS_TAG; import java.util.ArrayList; +import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.List; @@ -42,16 +44,16 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.c.function.RelocatedPointer; +import org.graalvm.nativeimage.impl.CEntryPointLiteralCodePointer; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.HostVM; import com.oracle.graal.pointsto.heap.ImageHeapConstant; import com.oracle.graal.pointsto.heap.ImageLayerWriter; -import com.oracle.graal.pointsto.infrastructure.Universe; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; -import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.StaticFieldsSupport; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.hub.DynamicHub; @@ -81,34 +83,37 @@ public class SVMImageLayerWriter extends ImageLayerWriter { private NativeImageHeap nativeImageHeap; + private HostedUniverse hUniverse; - public SVMImageLayerWriter() { - super(new SVMImageLayerSnapshotUtil()); + public SVMImageLayerWriter(boolean useSharedLayerGraphs) { + super(useSharedLayerGraphs, new SVMImageLayerSnapshotUtil()); } public void setNativeImageHeap(NativeImageHeap nativeImageHeap) { this.nativeImageHeap = nativeImageHeap; } - @Override - protected void persistHook(Universe universe, AnalysisUniverse analysisUniverse) { - HostedUniverse hostedUniverse = (HostedUniverse) universe; + public void setHostedUniverse(HostedUniverse hUniverse) { + this.hUniverse = hUniverse; + } - ImageHeapConstant staticPrimitiveFields = (ImageHeapConstant) hostedUniverse.getSnippetReflection().forObject(StaticFieldsSupport.getStaticPrimitiveFields()); - ImageHeapConstant staticObjectFields = (ImageHeapConstant) hostedUniverse.getSnippetReflection().forObject(StaticFieldsSupport.getStaticObjectFields()); + @Override + protected void persistHook() { + ImageHeapConstant staticPrimitiveFields = (ImageHeapConstant) hUniverse.getSnippetReflection().forObject(StaticFieldsSupport.getStaticPrimitiveFields()); + ImageHeapConstant staticObjectFields = (ImageHeapConstant) hUniverse.getSnippetReflection().forObject(StaticFieldsSupport.getStaticObjectFields()); jsonMap.put(STATIC_PRIMITIVE_FIELDS_TAG, getConstantId(staticPrimitiveFields)); jsonMap.put(STATIC_OBJECT_FIELDS_TAG, getConstantId(staticObjectFields)); } @Override - protected void persistType(AnalysisType type, AnalysisUniverse analysisUniverse, EconomicMap typeMap) { - HostVM hostVM = analysisUniverse.hostVM(); + protected void persistType(AnalysisType type, EconomicMap typeMap) { + HostVM hostVM = aUniverse.hostVM(); SVMHost svmHost = (SVMHost) hostVM; DynamicHub hub = svmHost.dynamicHub(type); typeMap.put(HUB_IDENTITY_HASH_CODE_TAG, System.identityHashCode(hub)); - super.persistType(type, analysisUniverse, typeMap); + super.persistType(type, typeMap); } @Override @@ -154,40 +159,30 @@ private static void handleNameConflict(String message) { } @Override - protected void persistField(AnalysisField field, Universe universe, EconomicMap fieldMap) { - HostedUniverse hostedUniverse = (HostedUniverse) universe; - HostedField hostedField = hostedUniverse.lookup(field); + protected void persistField(AnalysisField field, EconomicMap fieldMap) { + HostedField hostedField = hUniverse.lookup(field); int location = hostedField.getLocation(); if (hostedField.isStatic() && location > 0) { fieldMap.put(LOCATION_TAG, location); } - super.persistField(field, universe, fieldMap); + super.persistField(field, fieldMap); } @Override - protected void persistConstant(AnalysisUniverse analysisUniverse, ImageHeapConstant imageHeapConstant, EconomicMap constantMap, EconomicMap constantsMap) { + protected void persistConstant(ImageHeapConstant imageHeapConstant, EconomicMap constantMap) { ObjectInfo objectInfo = nativeImageHeap.getConstantInfo(imageHeapConstant); if (objectInfo != null) { constantMap.put(OBJECT_OFFSET_TAG, String.valueOf(objectInfo.getOffset())); } - super.persistConstant(analysisUniverse, imageHeapConstant, constantMap, constantsMap); + super.persistConstant(imageHeapConstant, constantMap); } @Override public void persistConstantRelinkingInfo(EconomicMap constantMap, BigBang bb, Class clazz, JavaConstant hostedObject, int id) { ResolvedJavaType type = bb.getConstantReflectionProvider().asJavaType(hostedObject); if (type instanceof AnalysisType analysisType) { - /* - * Until another solution for implementing a stable name for $$TypeSwitch classes is - * found, the constant containing a DynamicHub corresponding to a $$TypeSwitch class is - * not persisted as it would not be possible to relink it. Considering that those - * classes are only used as a container for a static method, recreating the constant in - * the extension image alongside the class should not cause too much issues. - */ - if (!isTypeSwitch(analysisType)) { - constantMap.put(CLASS_ID_TAG, analysisType.getId()); - constantsToRelink.add(id); - } + constantMap.put(CLASS_ID_TAG, analysisType.getId()); + constantsToRelink.add(id); } else { super.persistConstantRelinkingInfo(constantMap, bb, clazz, hostedObject, id); } @@ -196,14 +191,21 @@ public void persistConstantRelinkingInfo(EconomicMap constantMap @Override protected boolean delegateProcessing(List> data, Object constant) { if (constant instanceof RelocatableConstant relocatableConstant) { - data.add(List.of(METHOD_POINTER_TAG, getRelocatableConstantMethodId(relocatableConstant))); - return true; + RelocatedPointer pointer = relocatableConstant.getPointer(); + if (pointer instanceof MethodPointer methodPointer) { + data.add(List.of(METHOD_POINTER_TAG, getRelocatableConstantMethodId(methodPointer))); + return true; + } else if (pointer instanceof CEntryPointLiteralCodePointer cEntryPointLiteralCodePointer) { + data.add(List.of(C_ENTRY_POINT_LITERAL_CODE_POINTER, cEntryPointLiteralCodePointer.methodName, cEntryPointLiteralCodePointer.definingClass.getName(), + Arrays.stream(cEntryPointLiteralCodePointer.parameterTypes).map(Class::getName))); + return true; + } } return super.delegateProcessing(data, constant); } - private static int getRelocatableConstantMethodId(RelocatableConstant relocatableConstant) { - ResolvedJavaMethod method = ((MethodPointer) relocatableConstant.getPointer()).getMethod(); + private static int getRelocatableConstantMethodId(MethodPointer methodPointer) { + ResolvedJavaMethod method = methodPointer.getMethod(); if (method instanceof HostedMethod hostedMethod) { return getMethodId(hostedMethod.wrapped); } else { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriterHelper.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriterHelper.java new file mode 100644 index 000000000000..85f05c51eb6d --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageLayerWriterHelper.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted.heap; + +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.FACTORY_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.METHOD_TYPE_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.TARGET_CONSTRUCTOR_TAG; +import static com.oracle.graal.pointsto.heap.ImageLayerSnapshotUtil.THROW_ALLOCATED_OBJECT_TAG; + +import org.graalvm.collections.EconomicMap; + +import com.oracle.graal.pointsto.heap.ImageLayerWriter; +import com.oracle.graal.pointsto.heap.ImageLayerWriterHelper; +import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.svm.hosted.code.FactoryMethod; + +public class SVMImageLayerWriterHelper extends ImageLayerWriterHelper { + public SVMImageLayerWriterHelper(ImageLayerWriter imageLayerWriter) { + super(imageLayerWriter); + } + + @Override + protected void persistMethod(AnalysisMethod method, EconomicMap methodMap) { + if (method.wrapped instanceof FactoryMethod factoryMethod) { + methodMap.put(METHOD_TYPE_TAG, FACTORY_TAG); + AnalysisMethod targetConstructor = method.getUniverse().lookup(factoryMethod.getTargetConstructor()); + if (!method.isReachable() && !imageLayerWriter.isMethodPersisted(targetConstructor)) { + imageLayerWriter.persistAnalysisParsedGraph(targetConstructor); + imageLayerWriter.persistMethod(targetConstructor); + } + methodMap.put(TARGET_CONSTRUCTOR_TAG, targetConstructor.getId()); + methodMap.put(THROW_ALLOCATED_OBJECT_TAG, factoryMethod.throwAllocatedObject()); + } + super.persistMethod(method, methodMap); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java index db5c32cd7ed7..4eb35be13871 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/HostedDynamicLayerInfo.java @@ -84,6 +84,10 @@ public Pair getPriorLayerMethodLocation(SharedMethod s return Pair.create(basePointer, offset); } + public boolean isCompiled(int id) { + return methodIdToOffsetMap.containsKey(id); + } + void registerOffset(HostedMethod method) { int offset = method.getCodeAddressOffset(); int methodID = method.getWrapped().getId(); 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 633b8ef524a3..38596aca900b 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 @@ -24,10 +24,10 @@ */ package com.oracle.svm.hosted.imagelayer; -import static com.oracle.svm.core.SubstrateOptions.LayerUse; -import static com.oracle.svm.core.SubstrateOptions.LayerCreate; import static com.oracle.svm.core.SubstrateOptions.IncludeAllFromModule; import static com.oracle.svm.core.SubstrateOptions.IncludeAllFromPath; +import static com.oracle.svm.core.SubstrateOptions.LayerCreate; +import static com.oracle.svm.core.SubstrateOptions.LayerUse; import static com.oracle.svm.core.SubstrateOptions.imageLayerEnabledHandler; import static com.oracle.svm.hosted.imagelayer.LayerArchiveSupport.MODULE_OPTION; import static com.oracle.svm.hosted.imagelayer.LayerArchiveSupport.PACKAGE_OPTION; @@ -131,7 +131,6 @@ public static void processLayerOptions(EconomicMap, Object> values) SubstrateOptions.LayeredBaseImageAnalysis.update(values, true); SubstrateOptions.ClosedTypeWorld.update(values, false); SubstrateOptions.StripDebugInfo.update(values, false); - SubstrateOptions.AOTTrivialInline.update(values, false); if (imageLayerEnabledHandler != null) { imageLayerEnabledHandler.onOptionEnabled(values); } @@ -150,9 +149,7 @@ public static void processLayerOptions(EconomicMap, Object> values) throw UserError.abort("Option %s requires a layer file argument, e.g., %s=layer-file.nil.", optionName, optionName); } SubstrateOptions.ClosedTypeWorld.update(values, false); - /* Ignore any potential undefined references caused by inlining in base layer. */ - SubstrateOptions.IgnoreUndefinedReferences.update(values, true); - SubstrateOptions.AOTTrivialInline.update(values, false); + SubstrateOptions.ParseRuntimeOptions.update(values, false); if (imageLayerEnabledHandler != null) { imageLayerEnabledHandler.onOptionEnabled(values); } @@ -175,7 +172,7 @@ public static HostedImageLayerBuildingSupport initialize(HostedOptionValues valu if (isEnabled(LayerCreate, values)) { LayerOption layerOption = LayerOption.parse(LayerCreate.getValue(values).lastValue().orElseThrow()); writeLayerArchiveSupport = new WriteLayerArchiveSupport(archiveSupport, layerOption.fileName()); - writer = new SVMImageLayerWriter(); + writer = new SVMImageLayerWriter(SubstrateOptions.UseSharedLayerGraphs.getValue(values)); } SVMImageLayerLoader loader = null; LoadLayerArchiveSupport loadLayerArchiveSupport = null; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java index 2e08e2deb691..a384b9001a13 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LoadImageSingletonFeature.java @@ -216,6 +216,13 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { */ config.registerAsInHeap(slotInfo.keyClass()); } + /* + * GR-55294: The constants have to be created before the end of the analysis + * as new types cannot be created later. + */ + for (int priorId : getCrossLayerSingletonMappingInfo().getPriorLayerObjectIDs(slotInfo.keyClass())) { + HostedImageLayerBuildingSupport.singleton().getLoader().getOrCreateConstant(priorId); + } } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java index 2e03a209f047..1ece4bf75140 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedMethod.java @@ -68,6 +68,7 @@ import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.code.CompilationInfo; import com.oracle.svm.hosted.code.SubstrateCompilationDirectives; +import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; import jdk.graal.compiler.api.replacements.Snippet; import jdk.graal.compiler.debug.JavaMethodContext; @@ -113,6 +114,7 @@ public final class HostedMethod extends HostedElement implements SharedMethod, W private int codeAddressOffset; private boolean codeAddressOffsetValid; private boolean compiled; + private boolean compiledInPriorLayer; /** * All concrete methods that can actually be called when calling this method. This includes all @@ -250,6 +252,14 @@ public boolean isCompiled() { return compiled; } + public void setCompiledInPriorLayer() { + this.compiledInPriorLayer = true; + } + + public boolean isCompiledInPriorLayer() { + return compiledInPriorLayer; + } + public String getUniqueShortName() { return uniqueShortName; } @@ -268,7 +278,7 @@ public ImageCodeInfo getImageCodeInfo() { @Override public boolean forceIndirectCall() { - return wrapped.isInBaseLayer(); + return isCompiledInPriorLayer(); } @Override @@ -484,6 +494,14 @@ public Type[] getGenericParameterTypes() { @Override public boolean canBeInlined() { + /* + * GR-55278: Graphs that contain references to $$Lambda types cannot be persisted. Those + * methods should not be inlined in the base layer as we need to be able to call them from + * the extension layers. + */ + if (HostedImageLayerBuildingSupport.buildingSharedLayer() && !HostedImageLayerBuildingSupport.singleton().getWriter().persistedMethodGraph(wrapped)) { + return false; + } return wrapped.canBeInlined(); } 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 25c10e49d801..c1af300c33ff 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 @@ -57,6 +57,7 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.graal.pointsto.meta.BaseLayerMethod; import com.oracle.graal.pointsto.meta.BaseLayerType; import com.oracle.graal.pointsto.results.StrengthenGraphs; import com.oracle.svm.common.meta.MultiMethod; @@ -96,6 +97,8 @@ import com.oracle.svm.hosted.config.DynamicHubLayout; import com.oracle.svm.hosted.config.HybridLayout; import com.oracle.svm.hosted.heap.PodSupport; +import com.oracle.svm.hosted.imagelayer.HostedDynamicLayerInfo; +import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.hosted.substitute.ComputedValueField; import com.oracle.svm.hosted.substitute.DeletedMethod; @@ -337,7 +340,10 @@ private HostedMethod makeMethod(AnalysisMethod aMethod) { AnalysisType aDeclaringClass = aMethod.getDeclaringClass(); HostedType hDeclaringClass = lookupType(aDeclaringClass); ResolvedSignature signature = makeSignature(aMethod.getSignature()); - ConstantPool constantPool = makeConstantPool(aMethod.getConstantPool(), aDeclaringClass); + ConstantPool constantPool = null; + if (!(aMethod.getWrapped() instanceof BaseLayerMethod)) { + constantPool = makeConstantPool(aMethod.getConstantPool(), aDeclaringClass); + } ExceptionHandler[] aHandlers = aMethod.getExceptionHandlers(); ExceptionHandler[] sHandlers = new ExceptionHandler[aHandlers.length]; @@ -353,6 +359,9 @@ private HostedMethod makeMethod(AnalysisMethod aMethod) { } HostedMethod hMethod = HostedMethod.create(hUniverse, aMethod, hDeclaringClass, signature, constantPool, sHandlers); + if (HostedImageLayerBuildingSupport.buildingExtensionLayer() && HostedDynamicLayerInfo.singleton().isCompiled(hMethod.wrapped.getId())) { + hMethod.setCompiledInPriorLayer(); + } boolean isCFunction = aMethod.getAnnotation(CFunction.class) != null; boolean hasCFunctionOptions = aMethod.getAnnotation(CFunctionOptions.class) != null; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/LayeredVMThreadLocalCollector.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/LayeredVMThreadLocalCollector.java index 9975c70bff3d..b42ec4ca085c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/LayeredVMThreadLocalCollector.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/thread/LayeredVMThreadLocalCollector.java @@ -29,6 +29,9 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Stream; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader; @@ -59,6 +62,15 @@ record ThreadInfo(int size, int offset) { } final Map 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; @@ -71,9 +83,14 @@ 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) { /* @@ -97,12 +114,12 @@ public int sortAndAssignOffsets() { } else { assert nextOffset != -1; - for (VMThreadLocalInfo info : threadLocals.values()) { + Stream.concat(priorLayerThreadLocals.stream(), threadLocals.values().stream()).forEach(info -> { 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;