From efdb234880d147c2517194421ce390fb7b25cbe8 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Fri, 4 Aug 2023 16:41:20 +0200 Subject: [PATCH 1/6] Refactor use of AnalysisField.isValueAvailable(). --- .../graal/pointsto/heap/ImageHeapScanner.java | 4 +- .../ameta/AnalysisConstantFieldProvider.java | 2 +- .../AnalysisConstantReflectionProvider.java | 38 ++++++++++--------- .../svm/hosted/ameta/ReadableJavaField.java | 7 ++++ .../StaticFinalFieldFoldingNodePlugin.java | 2 +- .../svm/hosted/heap/SVMImageHeapScanner.java | 6 +-- .../svm/hosted/image/NativeImageHeap.java | 7 +++- .../meta/SharedConstantFieldProvider.java | 6 +-- 8 files changed, 40 insertions(+), 32 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index 5980d4a99e62..752dba946779 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -477,8 +477,8 @@ protected void onObjectReachable(ImageHeapConstant imageHeapConstant, ScanReason } } - public boolean isValueAvailable(@SuppressWarnings("unused") AnalysisField field) { - return true; + public boolean isValueAvailable(AnalysisField field) { + return field.isValueAvailable(); } protected String formatReason(String message, ScanReason reason) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java index b2182aa39fdf..5b61e6ea32e4 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantFieldProvider.java @@ -45,7 +45,7 @@ public AnalysisConstantFieldProvider(MetaAccessProvider metaAccess, SVMHost host @Override public T readConstantField(ResolvedJavaField f, ConstantFieldTool analysisTool) { AnalysisField field = (AnalysisField) f; - if (!field.isValueAvailable()) { + if (!ReadableJavaField.isValueAvailable(field)) { return null; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java index dc3aa3afcd73..e539ed8702cb 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java @@ -42,6 +42,7 @@ import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; +import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.svm.core.RuntimeAssertionsSupport; import com.oracle.svm.core.annotate.InjectAccessors; import com.oracle.svm.core.graal.meta.SharedConstantReflectionProvider; @@ -199,11 +200,12 @@ public JavaConstant readValue(UniverseMetaAccess suppliedMetaAccess, AnalysisFie value = readSimulatedValue(field); } if (value == null && receiver instanceof ImageHeapConstant) { + AnalysisError.guarantee(field.isValueAvailable(), "Value not yet available for %s", field); ImageHeapInstance heapObject = (ImageHeapInstance) receiver; value = heapObject.readFieldValue(field); } if (value == null) { - value = universe.lookup(ReadableJavaField.readFieldValue(suppliedMetaAccess, classInitializationSupport, field.wrapped, universe.toHosted(receiver))); + value = doReadValue(field, universe.toHosted(receiver), suppliedMetaAccess); } return interceptValue(suppliedMetaAccess, field, value); } @@ -217,24 +219,24 @@ public ValueSupplier readHostedFieldValue(AnalysisField field, Hos } } - if (field.wrapped instanceof ReadableJavaField) { - ReadableJavaField readableField = (ReadableJavaField) field.wrapped; - if (readableField.isValueAvailableBeforeAnalysis()) { - /* Materialize and return the value. */ - return ValueSupplier.eagerValue(universe.lookup(readableField.readValue(metaAccess, classInitializationSupport, receiver))); - } else { - /* - * Return a lazy value. This applies to RecomputeFieldValue.Kind.FieldOffset and - * RecomputeFieldValue.Kind.Custom. The value becomes available during hosted - * universe building and is installed by calling - * ComputedValueField.processSubstrate() or by ComputedValueField.readValue(). - * Attempts to materialize the value earlier will result in an error. - */ - return ValueSupplier.lazyValue(() -> universe.lookup(readableField.readValue(hMetaAccess, classInitializationSupport, receiver)), - readableField::isValueAvailable); - } + if (ReadableJavaField.isValueAvailable(field)) { + /* Materialize and return the value. */ + return ValueSupplier.eagerValue(doReadValue(field, receiver, metaAccess)); } - return ValueSupplier.eagerValue(universe.lookup(ReadableJavaField.readFieldValue(metaAccess, classInitializationSupport, field.wrapped, receiver))); + /* + * Return a lazy value. First, this applies to fields annotated with + * RecomputeFieldValue.Kind.FieldOffset and RecomputeFieldValue.Kind.Custom whose value + * becomes available during hosted universe building and is installed by calling + * ComputedValueField.processSubstrate() or ComputedValueField.readValue(). Secondly, this + * applies to fields annotated with @UnknownObjectField whose value is set directly either + * during analysis or in a later phase. Attempts to materialize the value before it becomes + * available will result in an error. + */ + return ValueSupplier.lazyValue(() -> doReadValue(field, receiver, hMetaAccess), () -> ReadableJavaField.isValueAvailable(field)); + } + + private JavaConstant doReadValue(AnalysisField field, JavaConstant receiver, UniverseMetaAccess access) { + return universe.lookup(ReadableJavaField.readFieldValue(access, classInitializationSupport, field.wrapped, receiver)); } /** diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/ReadableJavaField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/ReadableJavaField.java index 6888e97cd445..c183cefdf031 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/ReadableJavaField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/ReadableJavaField.java @@ -38,6 +38,13 @@ public interface ReadableJavaField extends ResolvedJavaField { + static boolean isValueAvailable(AnalysisField field) { + if (field.wrapped instanceof ReadableJavaField readableField) { + return readableField.isValueAvailable(); + } + return field.isValueAvailable(); + } + static JavaConstant readFieldValue(MetaAccessProvider metaAccess, ClassInitializationSupport classInitializationSupport, ResolvedJavaField field, JavaConstant receiver) { assert !(field instanceof AnalysisField) && !(field instanceof HostedField) : "must have been unwrapped"; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingNodePlugin.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingNodePlugin.java index 4d9f18270834..6810029380c5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingNodePlugin.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingNodePlugin.java @@ -80,7 +80,7 @@ public boolean handleLoadStaticField(GraphBuilderContext b, ResolvedJavaField fi return false; } - if (aField.wrapped instanceof ReadableJavaField && !((ReadableJavaField) aField.wrapped).isValueAvailable()) { + if (!ReadableJavaField.isValueAvailable(aField)) { /* * Cannot optimize static field whose value is recomputed and is not yet available, * i.e., it may depend on analysis/compilation derived data. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java index 40280dd3d0bd..1b2725da4a0f 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/heap/SVMImageHeapScanner.java @@ -111,11 +111,7 @@ protected ImageHeapConstant getOrCreateImageHeapConstant(JavaConstant javaConsta @Override public boolean isValueAvailable(AnalysisField field) { - if (field.wrapped instanceof ReadableJavaField) { - ReadableJavaField readableField = (ReadableJavaField) field.wrapped; - return readableField.isValueAvailable(); - } - return super.isValueAvailable(field); + return ReadableJavaField.isValueAvailable(field); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java index 05dfbee81738..0ef281182c94 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeap.java @@ -504,7 +504,12 @@ private void addObjectToImageHeap(final JavaConstant constant, boolean immutable final boolean fieldsAreImmutable = hMetaAccess.isInstanceOf(constant, String.class); for (HostedField field : clazz.getInstanceFields(true)) { boolean fieldRelocatable = false; - if (field.isRead() && + /* + * Fields that are only available after heap layout, such as + * StringInternSupport.imageInternedStrings and all ImageHeapInfo fields will + * not be processed. + */ + if (field.isRead() && field.isValueAvailable() && !field.equals(hybridArrayField) && !field.equals(hybridTypeIDSlotsField)) { if (field.getJavaKind() == JavaKind.Object) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java index f6a7a2c50470..b3fc97e3bccd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/SharedConstantFieldProvider.java @@ -53,10 +53,8 @@ public SharedConstantFieldProvider(MetaAccessProvider metaAccess, SVMHost hostVM @Override public T readConstantField(ResolvedJavaField field, ConstantFieldTool analysisTool) { - if (asAnalysisField(field).getWrapped() instanceof ReadableJavaField readableField) { - if (!readableField.isValueAvailable()) { - return null; - } + if (!ReadableJavaField.isValueAvailable(asAnalysisField(field))) { + return null; } return super.readConstantField(field, analysisTool); } From 70c177af76f929438d4b09c12247ce89efc1ad23 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Mon, 7 Aug 2023 15:41:14 +0200 Subject: [PATCH 2/6] Check availability via ReadableJavaField.isValueAvailable(). --- .../svm/hosted/ameta/AnalysisConstantReflectionProvider.java | 2 +- .../src/com/oracle/svm/hosted/meta/HostedField.java | 3 ++- .../src/com/oracle/svm/hosted/meta/UniverseBuilder.java | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java index e539ed8702cb..bc9188cfb1e6 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/AnalysisConstantReflectionProvider.java @@ -200,7 +200,7 @@ public JavaConstant readValue(UniverseMetaAccess suppliedMetaAccess, AnalysisFie value = readSimulatedValue(field); } if (value == null && receiver instanceof ImageHeapConstant) { - AnalysisError.guarantee(field.isValueAvailable(), "Value not yet available for %s", field); + AnalysisError.guarantee(ReadableJavaField.isValueAvailable(field), "Value not yet available for %s", field); ImageHeapInstance heapObject = (ImageHeapInstance) receiver; value = heapObject.readFieldValue(field); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java index add91cf1890f..2737b32b2a43 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedField.java @@ -30,6 +30,7 @@ import com.oracle.graal.pointsto.infrastructure.WrappedJavaField; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.svm.core.meta.SharedField; +import com.oracle.svm.hosted.ameta.ReadableJavaField; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.JavaTypeProfile; @@ -119,7 +120,7 @@ public boolean isWritten() { @Override public boolean isValueAvailable() { - return wrapped.isValueAvailable(); + return ReadableJavaField.isValueAvailable(wrapped); } @Override 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 39baf9f87627..dd7fd73d3b9d 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 @@ -94,6 +94,7 @@ import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.hosted.HostedConfiguration; import com.oracle.svm.hosted.NativeImageOptions; +import com.oracle.svm.hosted.ameta.ReadableJavaField; import com.oracle.svm.hosted.annotation.CustomSubstitutionMethod; import com.oracle.svm.hosted.config.HybridLayout; import com.oracle.svm.hosted.heap.PodSupport; @@ -1116,7 +1117,7 @@ private void processFieldLocations() { ((ComputedValueField) aField.wrapped).processSubstrate(hMetaAccess); } - if (!hField.hasLocation() && Modifier.isStatic(hField.getModifiers()) && !aField.isWritten() && aField.isValueAvailable()) { + if (!hField.hasLocation() && Modifier.isStatic(hField.getModifiers()) && !aField.isWritten() && ReadableJavaField.isValueAvailable(aField)) { hField.setUnmaterializedStaticConstant(); } } From 283b2afb3ac14b2741a3d2563bb6db9d78764a5e Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Fri, 4 Aug 2023 12:14:09 +0200 Subject: [PATCH 3/6] Refactor Graal object scanning during analysis. Cannot rescan unknown fields during analysis, they are not available. Just rescan their value if they reference objects. --- .../com/oracle/svm/core/hub/DynamicHub.java | 1 + .../com/oracle/svm/graal/GraalSupport.java | 32 +++++++++++-------- .../hosted/GraalGraphObjectReplacer.java | 19 +++-------- .../oracle/svm/graal/meta/SubstrateField.java | 4 +-- 4 files changed, 26 insertions(+), 30 deletions(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index 9714ea3de02a..51388df7948a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -314,6 +314,7 @@ public final class DynamicHub implements AnnotatedElement, java.lang.reflect.Typ * Back link to the SubstrateType used by the substrate meta access. Only used for the subset of * types for which a SubstrateType exists. */ + @UnknownObjectField(fullyQualifiedTypes = "com.oracle.svm.graal.meta.SubstrateType")// private SharedType metaType; /** diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java index b2c7e18efb8e..c42a0b1a95b8 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/GraalSupport.java @@ -27,7 +27,6 @@ import static org.graalvm.word.LocationIdentity.ANY_LOCATION; import java.io.PrintStream; -import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Arrays; @@ -73,6 +72,7 @@ import org.graalvm.word.Pointer; import org.graalvm.word.WordFactory; +import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.c.CGlobalData; import com.oracle.svm.core.c.CGlobalDataFactory; import com.oracle.svm.core.config.ConfigurationValues; @@ -86,7 +86,6 @@ import com.oracle.svm.core.util.ImageHeapMap; import com.oracle.svm.graal.meta.SubstrateMethod; import com.oracle.svm.hosted.FeatureImpl.DuringAnalysisAccessImpl; -import com.oracle.svm.util.ReflectionUtil; /** * Holds data that is pre-computed during native image generation and accessed at run time during a @@ -129,11 +128,6 @@ public class GraalSupport { protected final DiagnosticsOutputDirectory outputDirectory = new DiagnosticsOutputDirectory(RuntimeOptionValues.singleton()); protected final Map compilationProblemsPerAction = new EnumMap<>(ExceptionAction.class); - private static final Field graphEncodingField = ReflectionUtil.lookupField(GraalSupport.class, "graphEncoding"); - private static final Field graphObjectsField = ReflectionUtil.lookupField(GraalSupport.class, "graphObjects"); - private static final Field graphNodeTypesField = ReflectionUtil.lookupField(GraalSupport.class, "graphNodeTypes"); - private static final Field methodsToCompileField = ReflectionUtil.lookupField(GraalSupport.class, "methodsToCompile"); - private static final CGlobalData nextIsolateId = CGlobalDataFactory.createWord((Pointer) WordFactory.unsigned(1L)); private volatile long isolateId = 0; @@ -234,7 +228,7 @@ public static boolean setMethodsToCompile(DuringAnalysisAccessImpl config, Subst GraalSupport support = get(); if (!Arrays.equals(support.methodsToCompile, methodsToCompile)) { support.methodsToCompile = methodsToCompile; - GraalSupport.rescan(config, support, methodsToCompileField); + GraalSupport.rescan(config, methodsToCompile); result = true; } return result; @@ -274,28 +268,38 @@ public static boolean setGraphEncoding(FeatureAccess a, byte[] graphEncoding, Ob boolean result = false; if (!Arrays.equals(support.graphEncoding, graphEncoding)) { support.graphEncoding = graphEncoding; - GraalSupport.rescan(a, support, graphEncodingField); result = true; } if (!Arrays.deepEquals(support.graphObjects, graphObjects)) { support.graphObjects = graphObjects; - GraalSupport.rescan(a, support, graphObjectsField); + GraalSupport.rescan(a, graphObjects); result = true; } if (!Arrays.equals(support.graphNodeTypes, graphNodeTypes)) { support.graphNodeTypes = graphNodeTypes; - GraalSupport.rescan(a, support, graphNodeTypesField); + GraalSupport.rescan(a, graphNodeTypes); result = true; } return result; } - private static void rescan(FeatureAccess a, GraalSupport support, Field field) { - if (a instanceof DuringAnalysisAccessImpl) { - ((DuringAnalysisAccessImpl) a).rescanField(support, field); + private static void rescan(FeatureAccess a, Object object) { + if (a instanceof DuringAnalysisAccessImpl access) { + rescan(access.getUniverse(), object); } } + /** + * Rescan Graal objects during analysis. The fields that point to these objects are annotated + * with {@link UnknownObjectField} so their value is not processed during analysis, only their + * declared type is injected in the type flow graphs. Their eventual value becomes available + * after analysis. Later when the field is read the lazy value supplier scans the final value + * and patches the shadow heap. + */ + public static void rescan(AnalysisUniverse universe, Object object) { + universe.getHeapScanner().rescanObject(object); + } + @Platforms(Platform.HOSTED_ONLY.class) public static void registerImmutableObjects(CompilationAccess access) { access.registerAsImmutable(get().graphEncoding); diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java index c79525081653..133b13edc964 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/GraalGraphObjectReplacer.java @@ -24,7 +24,6 @@ */ package com.oracle.svm.graal.hosted; -import java.lang.reflect.Field; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -56,6 +55,7 @@ import com.oracle.svm.core.util.HostedStringDeduplication; import com.oracle.svm.core.util.ObservableImageHeapMapProvider; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.graal.GraalSupport; import com.oracle.svm.graal.SubstrateGraalRuntime; import com.oracle.svm.graal.meta.SubstrateField; import com.oracle.svm.graal.meta.SubstrateMethod; @@ -107,11 +107,6 @@ public class GraalGraphObjectReplacer implements Function { private final HostedStringDeduplication stringTable; - private final Field substrateFieldTypeField; - private final Field substrateFieldDeclaringClassField; - private final Field dynamicHubMetaTypeField; - private final Field substrateTypeRawAllInstanceFieldsField; - private final Class jvmciCleanerClass = ReflectionUtil.lookupClass(false, "jdk.vm.ci.hotspot.Cleaner"); /** @@ -125,10 +120,6 @@ public GraalGraphObjectReplacer(AnalysisUniverse aUniverse, SubstrateProviders s this.sProviders = sProviders; this.universeFactory = universeFactory; this.stringTable = HostedStringDeduplication.singleton(); - substrateFieldTypeField = ReflectionUtil.lookupField(SubstrateField.class, "type"); - substrateFieldDeclaringClassField = ReflectionUtil.lookupField(SubstrateField.class, "declaringClass"); - dynamicHubMetaTypeField = ReflectionUtil.lookupField(DynamicHub.class, "metaType"); - substrateTypeRawAllInstanceFieldsField = ReflectionUtil.lookupField(SubstrateType.class, "rawAllInstanceFields"); } public void setGraalRuntime(SubstrateGraalRuntime sGraalRuntime) { @@ -289,8 +280,8 @@ public synchronized SubstrateField createField(ResolvedJavaField original) { sField = newField; sField.setLinks(createType(aField.getType()), createType(aField.getDeclaringClass())); - aUniverse.getHeapScanner().rescanField(sField, substrateFieldTypeField); - aUniverse.getHeapScanner().rescanField(sField, substrateFieldDeclaringClassField); + GraalSupport.rescan(aUniverse, sField.getType()); + GraalSupport.rescan(aUniverse, sField.getDeclaringClass()); } } return sField; @@ -338,10 +329,10 @@ public synchronized SubstrateType createType(JavaType original) { if (sType == null) { sType = newType; hub.setMetaType(sType); - aUniverse.getHeapScanner().rescanField(hub, dynamicHubMetaTypeField); + GraalSupport.rescan(aUniverse, hub.getMetaType()); sType.setRawAllInstanceFields(createAllInstanceFields(aType)); - aUniverse.getHeapScanner().rescanField(sType, substrateTypeRawAllInstanceFieldsField); + GraalSupport.rescan(aUniverse, sType.getRawAllInstanceFields()); createType(aType.getSuperclass()); createType(aType.getComponentType()); for (AnalysisType aInterface : aType.getInterfaces()) { diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java index 46ee9d5cb435..fcaca720e75d 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java @@ -50,8 +50,8 @@ public class SubstrateField implements SharedField { protected static final SubstrateField[] EMPTY_ARRAY = new SubstrateField[0]; - SubstrateType type; - SubstrateType declaringClass; + @UnknownObjectField SubstrateType type; + @UnknownObjectField SubstrateType declaringClass; private final String name; private final int modifiers; private int hashCode; From 8a37b19505115ed2138575653937aae13b77bf4e Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Tue, 1 Aug 2023 19:24:42 +0200 Subject: [PATCH 4/6] Fix @UnkownObjectField availability. --- .../src/com/oracle/svm/core/heapdump/HeapDumpUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heapdump/HeapDumpUtils.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heapdump/HeapDumpUtils.java index 40b7c984f9de..385c917fc1ae 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heapdump/HeapDumpUtils.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heapdump/HeapDumpUtils.java @@ -37,6 +37,7 @@ import org.graalvm.word.UnsignedWord; import org.graalvm.word.WordFactory; +import com.oracle.svm.core.BuildPhaseProvider.AfterCompilation; import com.oracle.svm.core.NeverInline; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.code.CodeInfo; @@ -64,7 +65,7 @@ */ public class HeapDumpUtils { - @UnknownObjectField private byte[] fieldsMap; + @UnknownObjectField(availability = AfterCompilation.class) private byte[] fieldsMap; /** Extra methods for testing. */ private final TestingBackDoor testingBackDoor; From 3f74e7e5723b5228c23c777638f76b98313c2ffb Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Wed, 2 Aug 2023 17:10:12 +0200 Subject: [PATCH 5/6] Propagate `null` values even when field value is not yet available. --- .../graal/pointsto/heap/ImageHeapScanner.java | 36 +++++++++++-------- .../svm/core/genscavenge/ImageHeapInfo.java | 32 ++++++++--------- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java index 752dba946779..4f5fcde13628 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java @@ -118,17 +118,19 @@ public void scanEmbeddedRoot(JavaConstant root, BytecodePosition position) { public void onFieldRead(AnalysisField field) { assert field.isRead(); /* Check if the value is available before accessing it. */ - if (isValueAvailable(field)) { - FieldScan reason = new FieldScan(field); - AnalysisType declaringClass = field.getDeclaringClass(); - if (field.isStatic()) { + FieldScan reason = new FieldScan(field); + AnalysisType declaringClass = field.getDeclaringClass(); + if (field.isStatic()) { + if (isValueAvailable(field)) { JavaConstant fieldValue = declaringClass.getOrComputeData().readFieldValue(field); markReachable(fieldValue, reason); notifyAnalysis(field, null, fieldValue, reason); - } else { - /* Trigger field scanning for the already processed objects. */ - postTask(() -> onInstanceFieldRead(field, declaringClass, reason)); + } else if (field.canBeNull()) { + notifyAnalysis(field, null, JavaConstant.NULL_POINTER, reason); } + } else { + /* Trigger field scanning for the already processed objects. */ + postTask(() -> onInstanceFieldRead(field, declaringClass, reason)); } } @@ -136,9 +138,7 @@ private void onInstanceFieldRead(AnalysisField field, AnalysisType type, FieldSc for (AnalysisType subtype : type.getSubTypes()) { for (ImageHeapConstant imageHeapConstant : imageHeap.getReachableObjects(subtype)) { ImageHeapInstance imageHeapInstance = (ImageHeapInstance) imageHeapConstant; - JavaConstant fieldValue = imageHeapInstance.readFieldValue(field); - markReachable(fieldValue, reason); - notifyAnalysis(field, imageHeapInstance, fieldValue, reason); + updateInstanceField(field, imageHeapInstance, reason, null); } /* Subtypes include this type itself. */ if (!subtype.equals(type)) { @@ -468,15 +468,23 @@ protected void onObjectReachable(ImageHeapConstant imageHeapConstant, ScanReason } else if (imageHeapConstant instanceof ImageHeapInstance imageHeapInstance) { for (ResolvedJavaField javaField : objectType.getInstanceFields(true)) { AnalysisField field = (AnalysisField) javaField; - if (field.isRead() && isValueAvailable(field)) { - JavaConstant fieldValue = imageHeapInstance.readFieldValue(field); - markReachable(fieldValue, reason, onAnalysisModified); - notifyAnalysis(field, imageHeapInstance, fieldValue, reason, onAnalysisModified); + if (field.isRead()) { + updateInstanceField(field, imageHeapInstance, reason, onAnalysisModified); } } } } + private void updateInstanceField(AnalysisField field, ImageHeapInstance imageHeapInstance, ScanReason reason, Consumer onAnalysisModified) { + if (isValueAvailable(field)) { + JavaConstant fieldValue = imageHeapInstance.readFieldValue(field); + markReachable(fieldValue, reason, onAnalysisModified); + notifyAnalysis(field, imageHeapInstance, fieldValue, reason, onAnalysisModified); + } else if (field.canBeNull()) { + notifyAnalysis(field, imageHeapInstance, JavaConstant.NULL_POINTER, reason, onAnalysisModified); + } + } + public boolean isValueAvailable(AnalysisField field) { return field.isValueAvailable(); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapInfo.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapInfo.java index 191748d82cc1..d6819af09d97 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapInfo.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/ImageHeapInfo.java @@ -47,29 +47,29 @@ public final class ImageHeapInfo { /** Indicates no chunk with {@link #initialize} chunk offset parameters. */ public static final long NO_CHUNK = -1; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object firstReadOnlyPrimitiveObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object lastReadOnlyPrimitiveObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object firstReadOnlyPrimitiveObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object lastReadOnlyPrimitiveObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object firstReadOnlyReferenceObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object lastReadOnlyReferenceObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object firstReadOnlyReferenceObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object lastReadOnlyReferenceObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object firstReadOnlyRelocatableObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object lastReadOnlyRelocatableObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object firstReadOnlyRelocatableObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object lastReadOnlyRelocatableObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object firstWritablePrimitiveObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object lastWritablePrimitiveObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object firstWritablePrimitiveObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object lastWritablePrimitiveObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object firstWritableReferenceObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object lastWritableReferenceObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object firstWritableReferenceObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object lastWritableReferenceObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object firstWritableHugeObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object lastWritableHugeObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object firstWritableHugeObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object lastWritableHugeObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object firstReadOnlyHugeObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object lastReadOnlyHugeObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object firstReadOnlyHugeObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object lastReadOnlyHugeObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object firstObject; - @UnknownObjectField(availability = AfterHeapLayout.class) public Object lastObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object firstObject; + @UnknownObjectField(availability = AfterHeapLayout.class, canBeNull = true) public Object lastObject; // All offsets are relative to the heap base. @UnknownPrimitiveField(availability = AfterHeapLayout.class) public long offsetOfFirstWritableAlignedChunk; From a9ec7794c4c2601e5657f200f12ec31c5dab42dd Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Thu, 3 Aug 2023 23:22:16 +0200 Subject: [PATCH 6/6] Scan RuntimeModuleSupport.bootLayer prototype object. --- .../oracle/svm/hosted/ModuleLayerFeature.java | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java index a85de883ffa6..e2a5f40568d1 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ModuleLayerFeature.java @@ -66,9 +66,11 @@ import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.heap.UnknownObjectField; import com.oracle.svm.core.jdk.Resources; import com.oracle.svm.core.jdk.RuntimeModuleSupport; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.util.LogUtils; import com.oracle.svm.util.ModuleSupport; import com.oracle.svm.util.ReflectionUtil; @@ -124,17 +126,6 @@ public void duringSetup(DuringSetupAccess access) { FeatureImpl.DuringSetupAccessImpl accessImpl = (FeatureImpl.DuringSetupAccessImpl) access; moduleLayerFeatureUtils = new ModuleLayerFeatureUtils(accessImpl.imageClassLoader); - /* - * Generate a temporary module layer to serve as a runtime boot module layer until the - * analysis is finished. - */ - Set baseModules = ModuleLayer.boot().modules() - .stream() - .map(Module::getName) - .collect(Collectors.toSet()); - Function clf = moduleLayerFeatureUtils::getClassLoaderForBootLayerModule; - ModuleLayer runtimeBootLayer = synthesizeRuntimeModuleLayer(new ArrayList<>(List.of(ModuleLayer.empty())), accessImpl.imageClassLoader, baseModules, Set.of(), clf, null); - RuntimeModuleSupport.instance().setBootLayer(runtimeBootLayer); RuntimeModuleSupport.instance().setHostedToRuntimeModuleMapper(moduleLayerFeatureUtils::getOrCreateRuntimeModuleForHostedModule); /* @@ -168,6 +159,28 @@ public void afterRegistration(AfterRegistrationAccess access) { } } + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + scanRuntimeBootLayerPrototype((BeforeAnalysisAccessImpl) access); + } + + /** + * Generate a temporary module layer to serve as a prototype object for + * {@link RuntimeModuleSupport}.bootLayer. The value doesn't need to actually be set in the + * field, just scanned such that the analysis sees the deep type hierarchy. The field value + * wouldn't be processed during analysis anyway since the field is annotated with + * {@link UnknownObjectField}, only the field declared type is injected in the type flow graphs. + * The concrete value is set in {@link ModuleLayerFeature#afterAnalysis}. Later when the field + * is read the lazy value supplier scans the concrete value and patches the shadow heap. + */ + private void scanRuntimeBootLayerPrototype(BeforeAnalysisAccessImpl accessImpl) { + Set baseModules = ModuleLayer.boot().modules().stream().map(Module::getName).collect(Collectors.toSet()); + Function clf = moduleLayerFeatureUtils::getClassLoaderForBootLayerModule; + ModuleLayer runtimeBootLayer = synthesizeRuntimeModuleLayer(new ArrayList<>(List.of(ModuleLayer.empty())), accessImpl.imageClassLoader, baseModules, Set.of(), clf, null); + /* Only scan the value if module support is enabled and bootLayer field is reachable. */ + accessImpl.registerReachabilityHandler((a) -> accessImpl.rescanObject(runtimeBootLayer), ReflectionUtil.lookupField(RuntimeModuleSupport.class, "bootLayer")); + } + @Override public void afterAnalysis(AfterAnalysisAccess access) { FeatureImpl.AfterAnalysisAccessImpl accessImpl = (FeatureImpl.AfterAnalysisAccessImpl) access;