From d97ab456e254c6aba79f9e9f3e79fad19ebe378f Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Wed, 26 Feb 2025 18:45:07 +0100 Subject: [PATCH 1/2] Add support for deferring class initialization to app layer. --- .../oracle/graal/pointsto/ObjectScanner.java | 8 +- .../com/oracle/graal/pointsto/api/HostVM.java | 14 + .../heap/ImageHeapRelocatableConstant.java | 5 + .../graal/pointsto/heap/ImageHeapScanner.java | 10 +- .../graal/pointsto/meta/AnalysisField.java | 24 + .../oracle/svm/core/StaticFieldsSupport.java | 20 +- .../com/oracle/svm/core/SubstrateOptions.java | 4 + .../svm/core/hub/ConstantPoolProvider.java | 2 +- .../imagelayer/DynamicImageLayerInfo.java | 11 +- .../SharedLayerSnapshotCapnProtoSchema.capnp | 4 +- .../hosted/HostedStaticFieldSupportImpl.java | 85 +++- .../src/com/oracle/svm/hosted/SVMHost.java | 21 +- .../AnalysisConstantReflectionProvider.java | 4 + .../StaticFinalFieldFoldingFeature.java | 2 +- .../oracle/svm/hosted/image/NativeImage.java | 2 +- .../svm/hosted/image/NativeImageHeap.java | 26 +- .../hosted/image/NativeImageHeapWriter.java | 17 +- .../CrossLayerConstantRegistry.java | 2 +- .../CrossLayerConstantRegistryFeature.java | 8 +- .../ImageHeapRelocatableConstantFeature.java | 127 +++++ .../ImageHeapRelocatableConstantSupport.java | 48 ++ .../imagelayer/ImageLayerSectionFeature.java | 4 +- .../LayeredClassInitialization.java | 38 ++ .../imagelayer/LayeredStaticFieldSupport.java | 444 ++++++++++++++++++ .../imagelayer/LoadImageSingletonFeature.java | 2 +- .../imagelayer/SVMImageLayerLoader.java | 11 +- .../imagelayer/SVMImageLayerWriter.java | 35 +- ...redLayerSnapshotCapnProtoSchemaHolder.java | 24 +- .../oracle/svm/hosted/meta/HostedField.java | 50 +- .../meta/SharedConstantFieldProvider.java | 4 + .../svm/hosted/meta/UniverseBuilder.java | 107 +++-- 31 files changed, 1018 insertions(+), 145 deletions(-) create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageHeapRelocatableConstantFeature.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageHeapRelocatableConstantSupport.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredClassInitialization.java create mode 100644 substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredStaticFieldSupport.java diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java index c6ed0ef8d090..7017aa4d6fbe 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/ObjectScanner.java @@ -109,7 +109,7 @@ public void scanBootImageHeapRoots(Comparator fieldComparator, Co } for (AnalysisField field : fields) { if (Modifier.isStatic(field.getModifiers()) && field.isRead()) { - execute(() -> scanRootField(field)); + execute(() -> scanStaticFieldRoot(field)); } } @@ -155,9 +155,9 @@ protected void scanEmbeddedRoot(JavaConstant root, Object position) { * * @param field the scanned root field */ - protected final void scanRootField(AnalysisField field) { - if (field.isInBaseLayer()) { - // skip base layer roots + protected final void scanStaticFieldRoot(AnalysisField field) { + if (!field.installableInLayer()) { + // skip fields not installable in this layer return; } scanField(field, null, null); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java index 09a89f1f05e0..780268c7376c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/HostVM.java @@ -391,6 +391,20 @@ public boolean enableReachableInCurrentLayer() { return false; } + public boolean buildingImageLayer() { + return false; + } + + @SuppressWarnings("unused") + public boolean installableInLayer(AnalysisField aField) { + return true; + } + + @SuppressWarnings("unused") + public boolean preventConstantFolding(AnalysisField aField) { + return false; + } + /** * Helpers to determine what analysis actions should be taken for a given Multi-Method version. */ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapRelocatableConstant.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapRelocatableConstant.java index cb3658d05ba2..44d5692d1ea7 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapRelocatableConstant.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapRelocatableConstant.java @@ -77,4 +77,9 @@ public JavaConstant uncompress() { public ImageHeapConstant forObjectClone() { throw AnalysisError.shouldNotReachHere("Unsupported in ImageHeapRelocatableConstant"); } + + @Override + public String toString() { + return "(ImageHeapRelocatableConstant) " + super.toString(); + } } 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 7f98994fbd79..08a5a4ba0b24 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 @@ -133,10 +133,10 @@ public void onFieldRead(AnalysisField field) { AnalysisType declaringClass = field.getDeclaringClass(); if (field.isStatic()) { FieldScan reason = new FieldScan(field); - if (field.isInBaseLayer()) { + if (!field.installableInLayer()) { /* - * For base layer static fields we don't want to scan the constant value, but - * instead inject its type state in the field flow. This will be propagated to any + * For non-installable static fields we do not scan the constant value, but instead + * inject its type state in the field flow. This will be propagated to any * corresponding field loads. * * GR-52421: the field state needs to be serialized from the base layer analysis @@ -146,9 +146,7 @@ public void onFieldRead(AnalysisField field) { } else if (bb.trackPrimitiveValues() && field.getStorageKind().isPrimitive()) { ((PointsToAnalysisField) field).saturatePrimitiveField(); } - return; - } - if (isValueAvailable(field)) { + } else if (isValueAvailable(field)) { JavaConstant fieldValue = readStaticFieldValue(field); if (fieldValue instanceof ImageHeapConstant imageHeapConstant && field.isFinal()) { AnalysisError.guarantee(imageHeapConstant.getOrigin() != null, "The origin of the constant %s should have been registered before", imageHeapConstant); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java index c5b8d0f1df42..e8a5a6e276c3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java @@ -107,6 +107,13 @@ public abstract class AnalysisField extends AnalysisElement implements WrappedJa */ protected Object fieldValueInterceptor; + /** + * When building layered images, for static fields we must keep track of what layer's static + * fields array the field is assigned in. This also impacts when the underlying value can be + * read and/or constant folded. + */ + private final boolean isLayeredStaticField; + @SuppressWarnings("this-escape") public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField) { super(universe.hostVM.enableTrackAcrossLayers()); @@ -152,6 +159,7 @@ public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField) id = universe.computeNextFieldId(); isInBaseLayer = false; } + isLayeredStaticField = isStatic() && universe.hostVM.buildingImageLayer(); } @Override @@ -188,6 +196,22 @@ public boolean isInBaseLayer() { return isInBaseLayer; } + public boolean installableInLayer() { + if (isLayeredStaticField) { + return getUniverse().hostVM.installableInLayer(this); + } else { + return true; + } + } + + public boolean preventConstantFolding() { + if (isLayeredStaticField) { + return getUniverse().hostVM.preventConstantFolding(this); + } else { + return false; + } + } + @Override public int hashCode() { return id; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/StaticFieldsSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/StaticFieldsSupport.java index ce2330faa11a..fcf9c4a210b1 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/StaticFieldsSupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/StaticFieldsSupport.java @@ -54,6 +54,7 @@ import jdk.graal.compiler.graph.NodeClass; import jdk.graal.compiler.nodeinfo.NodeInfo; import jdk.graal.compiler.nodes.ConstantNode; +import jdk.graal.compiler.nodes.StructuredGraph; import jdk.graal.compiler.nodes.ValueNode; import jdk.graal.compiler.nodes.calc.FloatingNode; import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; @@ -99,9 +100,9 @@ static HostedStaticFieldSupport singleton() { return ImageSingletons.lookup(HostedStaticFieldSupport.class); } - JavaConstant getStaticPrimitiveFieldsConstant(int layerNum, Function toConstant); + JavaConstant getStaticFieldsBaseConstant(int layerNum, boolean primitive, Function toConstant); - JavaConstant getStaticObjectFieldsConstant(int layerNum, Function toConstant); + FloatingNode getStaticFieldsBaseReplacement(int layerNum, boolean primitive, LoweringTool tool, StructuredGraph graph); boolean isPrimitive(ResolvedJavaField field); @@ -137,7 +138,7 @@ public static JavaConstant getStaticFieldsConstant(ResolvedJavaField field, Func var hostedSupport = HostedStaticFieldSupport.singleton(); boolean primitive = hostedSupport.isPrimitive(field); int layerNum = getInstalledLayerNum(field); - return primitive ? hostedSupport.getStaticPrimitiveFieldsConstant(layerNum, toConstant) : hostedSupport.getStaticObjectFieldsConstant(layerNum, toConstant); + return hostedSupport.getStaticFieldsBaseConstant(layerNum, primitive, toConstant); } public static int getInstalledLayerNum(ResolvedJavaField field) { @@ -249,25 +250,22 @@ public void lower(LoweringTool tool) { */ return; } - JavaConstant constant; + FloatingNode replacement; if (SubstrateUtil.HOSTED) { /* * Build-time version of lowering. */ - Function toConstantFunction = (obj) -> tool.getSnippetReflection().forObject(obj); - HostedStaticFieldSupport hostedSupport = HostedStaticFieldSupport.singleton(); - constant = primitive ? hostedSupport.getStaticPrimitiveFieldsConstant(layerNum, toConstantFunction) - : hostedSupport.getStaticObjectFieldsConstant(layerNum, toConstantFunction); + replacement = hostedSupport.getStaticFieldsBaseReplacement(layerNum, primitive, tool, graph()); } else { /* * JIT version of lowering. */ - constant = tool.getSnippetReflection() + JavaConstant constant = tool.getSnippetReflection() .forObject(primitive ? StaticFieldsSupport.getStaticPrimitiveFieldsAtRuntime(layerNum) : StaticFieldsSupport.getStaticObjectFieldsAtRuntime(layerNum)); + replacement = ConstantNode.forConstant(constant, tool.getMetaAccess(), graph()); } - assert constant.isNonNull() : constant; - replaceAndDelete(ConstantNode.forConstant(constant, tool.getMetaAccess(), graph())); + replaceAndDelete(graph().addOrUniqueWithInputs(replacement)); } } 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 ee84b85293f0..ed9c94909ef2 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 @@ -167,6 +167,10 @@ protected void onValueUpdate(EconomicMap, Object> values, Boolean o @Option(help = "Mark singleton as application layer only")// public static final HostedOptionKey ApplicationLayerOnlySingletons = new HostedOptionKey<>(AccumulatingLocatableMultiOptionValue.Strings.build()); + @Option(help = "Register class as being initialized in the app layer.")// + public static final HostedOptionKey ApplicationLayerInitializedClasses = new HostedOptionKey<>( + AccumulatingLocatableMultiOptionValue.Strings.build()); + @APIOption(name = "libc")// @Option(help = "Selects the libc implementation to use. Available implementations: glibc, musl, bionic")// public static final HostedOptionKey UseLibC = new HostedOptionKey<>(null) { diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ConstantPoolProvider.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ConstantPoolProvider.java index f81422a60fe9..dcedd42930c5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ConstantPoolProvider.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/ConstantPoolProvider.java @@ -44,7 +44,7 @@ * {@link Target_jdk_internal_reflect_ConstantPool}. */ public class ConstantPoolProvider implements MultiLayeredImageSingleton, UnsavedSingleton { - private final Target_jdk_internal_reflect_ConstantPool constantPool = new Target_jdk_internal_reflect_ConstantPool(DynamicImageLayerInfo.singleton().layerNumber); + private final Target_jdk_internal_reflect_ConstantPool constantPool = new Target_jdk_internal_reflect_ConstantPool(DynamicImageLayerInfo.getCurrentLayerNumber()); public static ConstantPoolProvider[] singletons() { return MultiLayeredImageSingleton.getAllLayers(ConstantPoolProvider.class); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/DynamicImageLayerInfo.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/DynamicImageLayerInfo.java index 1c827c2e991d..a77872e8554d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/DynamicImageLayerInfo.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/imagelayer/DynamicImageLayerInfo.java @@ -29,13 +29,14 @@ import org.graalvm.nativeimage.Platforms; import com.oracle.svm.core.graal.code.CGlobalDataInfo; +import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.meta.SharedMethod; @Platforms(Platform.HOSTED_ONLY.class) public abstract class DynamicImageLayerInfo { public static final int CREMA_LAYER_ID = Byte.MAX_VALUE; - public final int layerNumber; + private final int layerNumber; public final int nextLayerNumber; public final int numLayers; @@ -56,4 +57,12 @@ public record PriorLayerMethodLocation(CGlobalDataInfo base, int offset) { * Returns a (Base, Offset) pair which can be used to call a method defined in a prior layer. */ public abstract PriorLayerMethodLocation getPriorLayerMethodLocation(SharedMethod method); + + public static int getCurrentLayerNumber() { + if (!ImageLayerBuildingSupport.buildingImageLayer()) { + return MultiLayeredImageSingleton.UNUSED_LAYER_NUMBER; + } else { + return singleton().layerNumber; + } + } } diff --git a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp index 5a24d2853806..c726c1b4c59c 100644 --- a/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp +++ b/substratevm/src/com.oracle.svm.hosted/resources/SharedLayerSnapshotCapnProtoSchema.capnp @@ -128,7 +128,7 @@ struct PersistedAnalysisField { declaringTypeId @2 :TypeId; typeId @3 :TypeId; position @4 :Int32; - location @5 :Int32; + location @5 :Int32; # note currently we only read information about static fields' location modifiers @6 :Int32; isInternal @7 :Bool; isAccessed @8 :Bool; @@ -139,6 +139,8 @@ struct PersistedAnalysisField { isSynthetic @13 :Bool; annotationList @14 :List(Annotation); name @15 :Text; + priorInstalledLayerNum @16 :Int32; + assignmentStatus @17 :Int32; } struct CEntryPointLiteralReference { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedStaticFieldSupportImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedStaticFieldSupportImpl.java index 5473c3867a91..e7b415857382 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedStaticFieldSupportImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/HostedStaticFieldSupportImpl.java @@ -34,42 +34,74 @@ import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.meta.SharedField; import com.oracle.svm.hosted.imagelayer.HostedImageLayerBuildingSupport; +import com.oracle.svm.hosted.imagelayer.LayeredStaticFieldSupport; import com.oracle.svm.hosted.meta.HostedField; +import jdk.graal.compiler.nodes.ConstantNode; +import jdk.graal.compiler.nodes.StructuredGraph; +import jdk.graal.compiler.nodes.calc.FloatingNode; +import jdk.graal.compiler.nodes.spi.LoweringTool; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.ResolvedJavaField; @AutomaticallyRegisteredImageSingleton(StaticFieldsSupport.HostedStaticFieldSupport.class) public class HostedStaticFieldSupportImpl implements StaticFieldsSupport.HostedStaticFieldSupport { - @Override - public JavaConstant getStaticPrimitiveFieldsConstant(int layerNum, Function toConstant) { + private enum State { + UNUSED, + CURRENT_LAYER, + PRIOR_LAYER, + FUTURE_APP_LAYER, + } + + private State determineState(int layerNum) { if (layerNum == MultiLayeredImageSingleton.UNUSED_LAYER_NUMBER) { - return toConstant.apply(StaticFieldsSupport.getCurrentLayerStaticPrimitiveFields()); + return State.UNUSED; } else { - int currentLayerNum = DynamicImageLayerInfo.singleton().layerNumber; + int currentLayerNum = getCurrentLayerNumber(); if (currentLayerNum == layerNum) { - return toConstant.apply(StaticFieldsSupport.getCurrentLayerStaticPrimitiveFields()); - } else { + return State.CURRENT_LAYER; + } else if (layerNum < currentLayerNum) { assert layerNum == 0 && currentLayerNum == 1; - return HostedImageLayerBuildingSupport.singleton().getLoader().getBaseLayerStaticPrimitiveFields(); + return State.PRIOR_LAYER; + } else { + assert layerNum == LayeredStaticFieldSupport.getAppLayerNumber() && currentLayerNum == 0; + return State.FUTURE_APP_LAYER; } } } @Override - public JavaConstant getStaticObjectFieldsConstant(int layerNum, Function toConstant) { - if (layerNum == MultiLayeredImageSingleton.UNUSED_LAYER_NUMBER) { - return toConstant.apply(StaticFieldsSupport.getCurrentLayerStaticObjectFields()); - } else { - int currentLayerNum = DynamicImageLayerInfo.singleton().layerNumber; - if (currentLayerNum == layerNum) { - return toConstant.apply(StaticFieldsSupport.getCurrentLayerStaticObjectFields()); - } else { - assert layerNum == 0 && currentLayerNum == 1; - return HostedImageLayerBuildingSupport.singleton().getLoader().getBaseLayerStaticObjectFields(); + public JavaConstant getStaticFieldsBaseConstant(int layerNum, boolean primitive, Function toConstant) { + return switch (determineState(layerNum)) { + case UNUSED, CURRENT_LAYER -> { + Object hostedObject = primitive ? StaticFieldsSupport.getCurrentLayerStaticPrimitiveFields() : StaticFieldsSupport.getCurrentLayerStaticObjectFields(); + yield toConstant.apply(hostedObject); } - } + case PRIOR_LAYER -> + primitive ? HostedImageLayerBuildingSupport.singleton().getLoader().getBaseLayerStaticPrimitiveFields() + : HostedImageLayerBuildingSupport.singleton().getLoader().getBaseLayerStaticObjectFields(); + case FUTURE_APP_LAYER -> + LayeredStaticFieldSupport.singleton().getAppLayerStaticFieldBaseConstant(primitive); + }; + } + + @Override + public FloatingNode getStaticFieldsBaseReplacement(int layerNum, boolean primitive, LoweringTool tool, StructuredGraph graph) { + return switch (determineState(layerNum)) { + case UNUSED, CURRENT_LAYER -> { + Object hostedObject = primitive ? StaticFieldsSupport.getCurrentLayerStaticPrimitiveFields() : StaticFieldsSupport.getCurrentLayerStaticObjectFields(); + JavaConstant constant = tool.getSnippetReflection().forObject(hostedObject); + yield ConstantNode.forConstant(constant, tool.getMetaAccess(), graph); + } + case PRIOR_LAYER -> { + var constant = primitive ? HostedImageLayerBuildingSupport.singleton().getLoader().getBaseLayerStaticPrimitiveFields() + : HostedImageLayerBuildingSupport.singleton().getLoader().getBaseLayerStaticObjectFields(); + yield ConstantNode.forConstant(constant, tool.getMetaAccess(), graph); + } + case FUTURE_APP_LAYER -> + LayeredStaticFieldSupport.singleton().getAppLayerStaticFieldsBaseReplacement(primitive, tool, graph); + }; } @Override @@ -80,6 +112,17 @@ public boolean isPrimitive(ResolvedJavaField field) { return ((HostedField) field).getStorageKind().isPrimitive(); } + private int currentLayerCache = MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED; + + private int getCurrentLayerNumber() { + if (currentLayerCache == MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED) { + int newLayerNumber = DynamicImageLayerInfo.getCurrentLayerNumber(); + assert newLayerNumber != MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED; + currentLayerCache = newLayerNumber; + } + return currentLayerCache; + } + @Override public int getInstalledLayerNum(ResolvedJavaField field) { assert ImageLayerBuildingSupport.buildingImageLayer(); @@ -87,7 +130,11 @@ public int getInstalledLayerNum(ResolvedJavaField field) { return sField.getInstalledLayerNum(); } else { AnalysisField aField = (AnalysisField) field; - return (ImageLayerBuildingSupport.buildingInitialLayer() || aField.isInBaseLayer()) ? 0 : 1; + return switch (LayeredStaticFieldSupport.singleton().getAssignmentStatus(aField)) { + case UNDECIDED -> getCurrentLayerNumber(); + case PRIOR_LAYER -> LayeredStaticFieldSupport.singleton().getPriorInstalledLayerNum(aField); + case APP_LAYER_REQUESTED, APP_LAYER_DEFERRED -> LayeredStaticFieldSupport.getAppLayerNumber(); + }; } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java index 10980fce6cf2..96c45a4a045d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SVMHost.java @@ -118,6 +118,7 @@ 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.imagelayer.LayeredStaticFieldSupport; import com.oracle.svm.hosted.imagelayer.SVMImageLayerLoader; import com.oracle.svm.hosted.meta.HostedField; import com.oracle.svm.hosted.meta.HostedType; @@ -209,6 +210,8 @@ public enum UsageKind { private final boolean isClosedTypeWorld = SubstrateOptions.useClosedTypeWorld(); private final boolean enableTrackAcrossLayers; private final boolean enableReachableInCurrentLayer; + private final boolean buildingImageLayer = ImageLayerBuildingSupport.buildingImageLayer(); + private final LayeredStaticFieldSupport layeredStaticFieldSupport; @SuppressWarnings("this-escape") public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializationSupport classInitializationSupport, AnnotationSubstitutionProcessor annotationSubstitutions, @@ -237,7 +240,7 @@ public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializatio } else { parsingSupport = null; } - layerId = ImageLayerBuildingSupport.buildingImageLayer() ? DynamicImageLayerInfo.singleton().layerNumber : 0; + layerId = ImageLayerBuildingSupport.buildingImageLayer() ? DynamicImageLayerInfo.getCurrentLayerNumber() : 0; useBaseLayer = ImageLayerBuildingSupport.buildingExtensionLayer(); if (ImageLayerBuildingSupport.buildingSharedLayer()) { initializeExcludedFields(); @@ -245,6 +248,7 @@ public SVMHost(OptionValues options, ImageClassLoader loader, ClassInitializatio enableTrackAcrossLayers = ImageLayerBuildingSupport.buildingSharedLayer(); enableReachableInCurrentLayer = ImageLayerBuildingSupport.buildingExtensionLayer(); + layeredStaticFieldSupport = ImageLayerBuildingSupport.buildingImageLayer() ? LayeredStaticFieldSupport.singleton() : null; } /** @@ -1061,6 +1065,21 @@ public boolean enableReachableInCurrentLayer() { return enableReachableInCurrentLayer; } + @Override + public boolean buildingImageLayer() { + return buildingImageLayer; + } + + @Override + public boolean installableInLayer(AnalysisField aField) { + return layeredStaticFieldSupport.installableInLayer(aField); + } + + @Override + public boolean preventConstantFolding(AnalysisField aField) { + return layeredStaticFieldSupport.preventConstantFolding(aField); + } + private final List> neverInlineTrivialHandlers = new CopyOnWriteArrayList<>(); public void registerNeverInlineTrivialHandler(BiPredicate handler) { 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 b7e23ba4be4f..2d36165c87c9 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 @@ -229,6 +229,10 @@ public JavaConstant readValue(AnalysisField field, JavaConstant receiver, boolea } } + if (field.preventConstantFolding()) { + return null; + } + if (receiver instanceof ImageHeapInstance imageHeapInstance && imageHeapInstance.isInBaseLayer() && imageHeapInstance.nullFieldValues()) { return null; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingFeature.java index 28bb47970199..698d84ecf1df 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/fieldfolding/StaticFinalFieldFoldingFeature.java @@ -283,7 +283,7 @@ private void analyzeParsedMethod(Stage stage, AnalysisMethod method, StructuredG for (Node n : graph.getNodes()) { if (n instanceof StoreFieldNode node) { AnalysisField field = (AnalysisField) node.field(); - if (field.isStatic() && field.isFinal() && !field.isInBaseLayer()) { + if (field.isStatic() && field.isFinal() && field.installableInLayer()) { if (isClassInitializer && field.getDeclaringClass().equals(method.getDeclaringClass())) { analyzeStoreInClassInitializer(node, field, optimizableFields, ineligibleFields); } else { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java index d2ff1c3a6bc4..b8d88d02e708 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImage.java @@ -415,7 +415,7 @@ private ObjectFile.Symbol defineRelocationForSymbol(String name, long position) public static String getTextSectionStartSymbol() { if (ImageLayerBuildingSupport.buildingImageLayer()) { - return String.format("__svm_layer_code_section_%s", DynamicImageLayerInfo.singleton().layerNumber); + return String.format("__svm_layer_code_section_%s", DynamicImageLayerInfo.getCurrentLayerNumber()); } else { return "__svm_code_section"; } 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 afbbe67dea80..00b99d4da95a 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 @@ -303,11 +303,7 @@ private void addStaticFields() { * fields manually. */ for (HostedField field : hUniverse.getFields()) { - if (field.wrapped.isInBaseLayer()) { - /* Base layer static field values are accessed via the base layer arrays. */ - continue; - } - if (Modifier.isStatic(field.getModifiers()) && field.hasLocation() && field.getType().getStorageKind() == JavaKind.Object && field.isRead()) { + if (field.getWrapped().installableInLayer() && Modifier.isStatic(field.getModifiers()) && field.hasLocation() && field.getType().getStorageKind() == JavaKind.Object && field.isRead()) { assert field.isWritten() || !field.isValueAvailable() || MaterializedConstantFields.singleton().contains(field.wrapped); /* GR-56699 currently static fields cannot be ImageHeapRelocatableConstants. */ addConstant(readConstantField(field, null), false, field); @@ -630,8 +626,10 @@ private void addObjectToImageHeap(final JavaConstant constant, boolean immutable recursiveAddObject(hub, false, info); if (hMetaAccess.isInstanceOf(constant, Object[].class)) { VMError.guarantee(constant instanceof ImageHeapConstant, "Expected an ImageHeapConstant, found %s", constant); - relocatable = addConstantArrayElements(constant, length, false, info); + var result = addConstantArrayElements(constant, length, false, info); references = true; + relocatable = result.relocatable(); + patched = result.patched(); } written = true; /* How to know if any of the array elements are written? */ } catch (AnalysisError.TypeNotFoundError ex) { @@ -774,17 +772,26 @@ private boolean addArrayElements(Object[] array, boolean otherFieldsRelocatable, return relocatable; } - private boolean addConstantArrayElements(JavaConstant array, int length, boolean otherFieldsRelocatable, Object reason) { + record AddConstantArrayResult(boolean relocatable, boolean patched) { + + } + + private AddConstantArrayResult addConstantArrayElements(JavaConstant array, int length, boolean otherFieldsRelocatable, Object reason) { boolean relocatable = otherFieldsRelocatable; + boolean patched = false; for (int idx = 0; idx < length; idx++) { JavaConstant value = hConstantReflection.readArrayElement(array, idx); /* Object replacement is done as part as constant refection. */ if (spawnIsolates()) { relocatable = relocatable || value instanceof RelocatableConstant; } - recursiveAddConstant(value, false, reason); + if (value instanceof ImageHeapRelocatableConstant) { + patched = true; + } else { + recursiveAddConstant(value, false, reason); + } } - return relocatable; + return new AddConstantArrayResult(relocatable, patched); } /* @@ -816,6 +823,7 @@ static class AddObjectData { this.original = original; this.immutableFromParent = immutableFromParent; this.reason = reason; + VMError.guarantee(!(original instanceof ImageHeapRelocatableConstant)); } final JavaConstant original; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java index c55f091309ad..d1cb0f06e50d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeImageHeapWriter.java @@ -139,11 +139,7 @@ private void writeStaticFields(RelocatableBuffer buffer) { ObjectInfo primitiveFields = heap.getObjectInfo(StaticFieldsSupport.getCurrentLayerStaticPrimitiveFields()); ObjectInfo objectFields = heap.getObjectInfo(StaticFieldsSupport.getCurrentLayerStaticObjectFields()); for (HostedField field : heap.hUniverse.getFields()) { - if (field.wrapped.isInBaseLayer()) { - /* Base layer static field values are accessed via the base layer arrays. */ - continue; - } - if (Modifier.isStatic(field.getModifiers()) && field.hasLocation() && field.isRead()) { + if (field.getWrapped().installableInLayer() && Modifier.isStatic(field.getModifiers()) && field.hasLocation() && field.isRead()) { assert field.isWritten() || !field.isValueAvailable() || MaterializedConstantFields.singleton().contains(field.wrapped); ObjectInfo fields = (field.getStorageKind() == JavaKind.Object) ? objectFields : primitiveFields; writeField(buffer, fields, field, null, null); @@ -484,8 +480,15 @@ private void writeObject(ObjectInfo info, RelocatableBuffer buffer) { writePrimitiveArray(info, buffer, objectLayout, kind, imageHeapArray.getArray(), length); } else { heap.hConstantReflection.forEachArrayElement(constant, (element, index) -> { - final int elementIndex = getIndexInBuffer(info, objectLayout.getArrayElementOffset(kind, index)); - writeConstant(buffer, elementIndex, kind, element, info); + long elementOffset = objectLayout.getArrayElementOffset(kind, index); + final int elementIndex = getIndexInBuffer(info, elementOffset); + if (element instanceof ImageHeapRelocatableConstant ihcConstant) { + int heapOffset = NumUtil.safeToInt(info.getOffset() + elementOffset); + CrossLayerConstantRegistryFeature.singleton().markFutureHeapConstantPatchSite(ihcConstant, heapOffset); + fillReferenceWithGarbage(buffer, elementIndex); + } else { + writeConstant(buffer, elementIndex, kind, element, info); + } }); } } else { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistry.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistry.java index 013f8985a3aa..856d14da9520 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistry.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistry.java @@ -71,7 +71,7 @@ static CrossLayerConstantRegistry singletonOrNull() { * {@link #finalizeFutureHeapConstant}. The constant can be retrieved via {@link #getConstant} * in all layers except the layer which calls {@link #finalizeFutureHeapConstant}. */ - void registerFutureHeapConstant(String keyName, AnalysisType futureType); + ImageHeapConstant registerFutureHeapConstant(String keyName, AnalysisType futureType); /** * Registers a value to associate with a prior {@link #registerFutureHeapConstant} registration. diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java index f671a4adec22..8f08ab148761 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/CrossLayerConstantRegistryFeature.java @@ -326,10 +326,12 @@ public void registerHeapConstant(String keyName, Object obj) { } @Override - public void registerFutureHeapConstant(String keyName, AnalysisType futureType) { + public ImageHeapConstant registerFutureHeapConstant(String keyName, AnalysisType futureType) { assert futureType != null; - var futureConstant = new FutureConstantCandidateInfo(ImageHeapRelocatableConstant.create(futureType, keyName)); - registerConstantCandidate(keyName, futureConstant); + var imageHeapConstant = ImageHeapRelocatableConstant.create(futureType, keyName); + var constantInfo = new FutureConstantCandidateInfo(imageHeapConstant); + registerConstantCandidate(keyName, constantInfo); + return constantInfo.constant(); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageHeapRelocatableConstantFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageHeapRelocatableConstantFeature.java new file mode 100644 index 000000000000..21550db2ffc7 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageHeapRelocatableConstantFeature.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2025, 2025, 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.imagelayer; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.graal.pointsto.heap.ImageHeapObjectArray; +import com.oracle.graal.pointsto.heap.ImageHeapRelocatableConstant; +import com.oracle.svm.core.config.ConfigurationValues; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.graal.nodes.SubstrateCompressionNode; +import com.oracle.svm.core.graal.nodes.SubstrateNarrowOopStamp; +import com.oracle.svm.core.heap.ReferenceAccess; +import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.hosted.FeatureImpl; + +import jdk.graal.compiler.core.common.type.AbstractObjectStamp; +import jdk.graal.compiler.core.common.type.StampFactory; +import jdk.graal.compiler.nodes.ConstantNode; +import jdk.graal.compiler.nodes.NamedLocationIdentity; +import jdk.graal.compiler.nodes.StructuredGraph; +import jdk.graal.compiler.nodes.calc.FloatingNode; +import jdk.graal.compiler.nodes.memory.FloatingReadNode; +import jdk.graal.compiler.nodes.memory.address.OffsetAddressNode; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.MetaAccessProvider; + +/** + * We implement support for loading {@link ImageHeapRelocatableConstant}s directly within graphs via + * storing an array in the image heap whose elements consist of the + * {@link ImageHeapRelocatableConstant}s referenced from within graphs. Within the graphs themselves + * the accesses are converted to loads from {@link #finalizedImageHeapRelocatableConstantsArray}. + */ +@AutomaticallyRegisteredFeature +public class ImageHeapRelocatableConstantFeature extends ImageHeapRelocatableConstantSupport implements InternalFeature { + + final Map constantToInfoMap = new ConcurrentHashMap<>(); + final AtomicInteger nextIndex = new AtomicInteger(); + JavaConstant finalizedImageHeapRelocatableConstantsArray; + boolean sealed = false; + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return ImageLayerBuildingSupport.buildingImageLayer(); + } + + @Override + public void duringSetup(DuringSetupAccess access) { + ImageSingletons.add(ImageHeapRelocatableConstantSupport.class, this); + } + + @Override + public void afterAnalysis(AfterAnalysisAccess access) { + sealed = true; + var config = (FeatureImpl.AfterAnalysisAccessImpl) access; + + final JavaConstant[] elements = new JavaConstant[constantToInfoMap.size()]; + constantToInfoMap.forEach((constantValue, index) -> { + assert elements[index] == null : elements[index]; + elements[index] = constantValue; + }); + + finalizedImageHeapRelocatableConstantsArray = ImageHeapObjectArray.createUnbackedImageHeapArray(config.getMetaAccess().lookupJavaType(Object[].class), elements); + } + + private JavaConstant getImageHeapRelocatableConstantsArray() { + assert finalizedImageHeapRelocatableConstantsArray != null; + return finalizedImageHeapRelocatableConstantsArray; + } + + private int getAssignedIndex(ImageHeapRelocatableConstant constant) { + assert constantToInfoMap.containsKey(constant); + return constantToInfoMap.get(constant); + } + + @Override + void registerLoadableConstant(ImageHeapRelocatableConstant constant) { + constantToInfoMap.computeIfAbsent(constant, (key) -> { + assert !sealed; + return nextIndex.getAndIncrement(); + }); + } + + @Override + FloatingNode emitLoadConstant(StructuredGraph graph, MetaAccessProvider metaAccess, ImageHeapRelocatableConstant constant) { + /* + * We need to load the appropriate spot from the array storing all image heap relocatable + * constants referenced from the text section. + */ + long arrayOffset = ConfigurationValues.getObjectLayout().getArrayElementOffset(JavaKind.Object, getAssignedIndex(constant)); + var array = getImageHeapRelocatableConstantsArray(); + var address = new OffsetAddressNode(ConstantNode.forConstant(array, metaAccess, graph), ConstantNode.forLong(arrayOffset)); + + var compressEncoding = ReferenceAccess.singleton().getCompressEncoding(); + var compressedStamp = SubstrateNarrowOopStamp.compressed((AbstractObjectStamp) StampFactory.forConstant(constant, metaAccess), compressEncoding); + var read = new FloatingReadNode(address, NamedLocationIdentity.FINAL_LOCATION, graph.start(), compressedStamp); + return SubstrateCompressionNode.uncompress(graph, graph.addOrUniqueWithInputs(read), compressEncoding); + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageHeapRelocatableConstantSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageHeapRelocatableConstantSupport.java new file mode 100644 index 000000000000..244912ac972e --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageHeapRelocatableConstantSupport.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025, 2025, 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.imagelayer; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.graal.pointsto.heap.ImageHeapRelocatableConstant; + +import jdk.graal.compiler.nodes.StructuredGraph; +import jdk.graal.compiler.nodes.calc.FloatingNode; +import jdk.vm.ci.meta.MetaAccessProvider; + +/** + * {@link ImageHeapRelocatableConstant}s registered via this support are allowed to be directly + * referenced within graphs. + */ +public abstract class ImageHeapRelocatableConstantSupport { + + static ImageHeapRelocatableConstantSupport singleton() { + return ImageSingletons.lookup(ImageHeapRelocatableConstantSupport.class); + } + + abstract void registerLoadableConstant(ImageHeapRelocatableConstant constant); + + abstract FloatingNode emitLoadConstant(StructuredGraph graph, MetaAccessProvider metaAccess, ImageHeapRelocatableConstant constant); +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageLayerSectionFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageLayerSectionFeature.java index 1748f8400e3c..944bc2a40430 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageLayerSectionFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/ImageLayerSectionFeature.java @@ -139,7 +139,7 @@ private static String getLayerName(int layerNumber) { } private static ImageLayerSectionImpl createImageLayerSection() { - CGlobalData initialSectionStart = ImageLayerBuildingSupport.buildingInitialLayer() ? CGlobalDataFactory.forSymbol(getLayerName(DynamicImageLayerInfo.singleton().layerNumber)) : null; + CGlobalData initialSectionStart = ImageLayerBuildingSupport.buildingInitialLayer() ? CGlobalDataFactory.forSymbol(getLayerName(DynamicImageLayerInfo.getCurrentLayerNumber())) : null; CGlobalData cachedImageFDs; CGlobalData cachedImageHeapOffsets; CGlobalData cachedImageHeapRelocations; @@ -221,7 +221,7 @@ public void afterAbstractImageCreation(AfterAbstractImageCreationAccess access) } // this symbol must be global when it will be read by the prior section - objectFile.createDefinedSymbol(getLayerName(DynamicImageLayerInfo.singleton().layerNumber), layeredImageSection, 0, 0, false, ImageLayerBuildingSupport.buildingExtensionLayer()); + objectFile.createDefinedSymbol(getLayerName(DynamicImageLayerInfo.getCurrentLayerNumber()), layeredImageSection, 0, 0, false, ImageLayerBuildingSupport.buildingExtensionLayer()); if (numSingletonSlots != 0) { assert ImageLayerBuildingSupport.buildingApplicationLayer() : "Currently only application layer is supported"; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredClassInitialization.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredClassInitialization.java new file mode 100644 index 000000000000..778cd2f6b64b --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredClassInitialization.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025, 2025, 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.imagelayer; + +import org.graalvm.nativeimage.ImageSingletons; + +import jdk.vm.ci.meta.MetaAccessProvider; + +public abstract class LayeredClassInitialization { + + static LayeredClassInitialization singleton() { + return ImageSingletons.lookup(LayeredClassInitialization.class); + } + + abstract void initializeClassInAppLayer(Class c, MetaAccessProvider meta); +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredStaticFieldSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredStaticFieldSupport.java new file mode 100644 index 000000000000..8f16c66d69d6 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/LayeredStaticFieldSupport.java @@ -0,0 +1,444 @@ +/* + * Copyright (c) 2025, 2025, 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.imagelayer; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.graal.pointsto.heap.ImageHeapRelocatableConstant; +import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisType; +import com.oracle.graal.pointsto.util.AnalysisError; +import com.oracle.svm.core.BuildPhaseProvider; +import com.oracle.svm.core.StaticFieldsSupport; +import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature; +import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton; +import com.oracle.svm.core.feature.InternalFeature; +import com.oracle.svm.core.imagelayer.BuildingImageLayerPredicate; +import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo; +import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonLoader; +import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; +import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingletonBuilderFlags; +import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.FeatureImpl; +import com.oracle.svm.hosted.meta.HostedField; +import com.oracle.svm.hosted.meta.HostedUniverse; +import com.oracle.svm.hosted.meta.UniverseBuilder; + +import jdk.graal.compiler.nodes.StructuredGraph; +import jdk.graal.compiler.nodes.calc.FloatingNode; +import jdk.graal.compiler.nodes.spi.LoweringTool; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.MetaAccessProvider; + +/** + * This class keeps track of the location of static fields assigned in previous layers as well as + * what fields have been registered as having their installation deferred until the application + * layer. + */ +@AutomaticallyRegisteredImageSingleton(value = LayeredClassInitialization.class, onlyWith = BuildingImageLayerPredicate.class) +public class LayeredStaticFieldSupport extends LayeredClassInitialization implements LayeredImageSingleton { + /** + * In the initial layer, this refers to fields which must wait until the app layer to be + * installed. + * + * In the app layer, this refers to fields which were referenced in the prior layer (i.e. have + * status {@link LayerAssignmentStatus#APP_LAYER_DEFERRED}). + */ + final Set appLayerFields; + + final Map assignmentStatusMap; + final Map priorInstalledLayerNum; + final Map priorInstalledLocation; + + public static final String appLayerPrimitiveStaticFieldsBaseName = "APPLAYER_PRIMITIVE_STATICFIELDSBASE"; + public static final String appLayerObjectStaticFieldsBaseName = "APPLAYER_OBJECT_STATICFIELDSBASE"; + + private volatile ImageHeapRelocatableConstant appLayerPrimitiveStaticFieldsBase; + private volatile ImageHeapRelocatableConstant appLayerObjectStaticFieldsBase; + + final UniverseBuilder.StaticFieldOffsets appLayerStaticFieldOffsets; + + private final boolean inAppLayer; + + LayeredStaticFieldSupport() { + this(ConcurrentHashMap.newKeySet(), new UniverseBuilder.StaticFieldOffsets()); + } + + private LayeredStaticFieldSupport(Set appLayerFields, UniverseBuilder.StaticFieldOffsets appLayerStaticFieldOffsets) { + this.appLayerFields = appLayerFields; + assignmentStatusMap = new ConcurrentHashMap<>(); + inAppLayer = ImageLayerBuildingSupport.buildingApplicationLayer(); + priorInstalledLayerNum = inAppLayer ? new ConcurrentHashMap<>() : null; + priorInstalledLocation = inAppLayer ? new ConcurrentHashMap<>() : null; + this.appLayerStaticFieldOffsets = appLayerStaticFieldOffsets; + } + + /** + * Tracks to what layer a static field has been installed/assigned. + */ + public enum LayerAssignmentStatus { + /** + * This field has yet to be assigned a layer. + */ + UNDECIDED, + /** + * Was installed in a prior layer. + */ + PRIOR_LAYER, + /** + * This field has been registered as being deferred to the app layer, but has yet to be + * referenced. + */ + APP_LAYER_REQUESTED, + /** + * This field has both been registered as being deferred to the app layer and also accessed + * in a shared layer. Hence, it must be installed in the app layer. + */ + APP_LAYER_DEFERRED, + } + + public static LayeredStaticFieldSupport singleton() { + return (LayeredStaticFieldSupport) ImageSingletons.lookup(LayeredClassInitialization.class); + } + + @SuppressWarnings("unchecked") + private static AnalysisField getAnalysisField(Object obj) { + if (obj instanceof AnalysisField aField) { + return aField; + } else { + var supplier = (Supplier) obj; + return supplier.get(); + } + } + + public void initializeFromFieldData(AnalysisField aField, SharedLayerSnapshotCapnProtoSchemaHolder.PersistedAnalysisField.Reader fieldData) { + Object previous = priorInstalledLayerNum.put(aField, fieldData.getPriorInstalledLayerNum()); + assert previous == null : previous; + previous = assignmentStatusMap.put(aField, LayerAssignmentStatus.values()[fieldData.getAssignmentStatus()]); + assert previous == null : previous; + previous = priorInstalledLocation.put(aField, fieldData.getLocation()); + assert previous == null : previous; + } + + private void installFieldInAppLayer(Field field, MetaAccessProvider meta) { + assert !inAppLayer && !BuildPhaseProvider.isAnalysisFinished(); + + AnalysisField aField = (AnalysisField) meta.lookupJavaField(field); + var added = appLayerFields.add(aField); + assert added; + + /* + * Trigger build-time initialization of the class if it was not already initialized (and the + * class is registered as build-time initialized). + */ + boolean initialized = aField.getDeclaringClass().isInitialized(); + AnalysisError.guarantee(initialized, "Only fields for classes which are build-time initialized can be declared as deferred to application layer: %s", aField); + + // register this field as requiring app layer + var previous = assignmentStatusMap.put(aField, LayerAssignmentStatus.APP_LAYER_REQUESTED); + if (previous != null) { + throw AnalysisError.userError(String.format("Field has a prior assignment. This is due to registering an app layer deferred field too late. Field: %s. Previous: %s", field, previous)); + } + + // ensure the appropriate future layer constant exists + var registry = CrossLayerConstantRegistry.singletonOrNull(); + if (field.getType().isPrimitive()) { + if (appLayerPrimitiveStaticFieldsBase == null) { + AnalysisType futureType = (AnalysisType) meta.lookupJavaType(byte[].class); + synchronized (this) { + if (appLayerPrimitiveStaticFieldsBase == null) { + appLayerPrimitiveStaticFieldsBase = (ImageHeapRelocatableConstant) registry.registerFutureHeapConstant(appLayerPrimitiveStaticFieldsBaseName, futureType); + ImageHeapRelocatableConstantSupport.singleton().registerLoadableConstant(appLayerPrimitiveStaticFieldsBase); + } + } + } + } else if (appLayerObjectStaticFieldsBase == null) { + AnalysisType futureType = (AnalysisType) meta.lookupJavaType(Object[].class); + synchronized (this) { + if (appLayerObjectStaticFieldsBase == null) { + appLayerObjectStaticFieldsBase = (ImageHeapRelocatableConstant) registry.registerFutureHeapConstant(appLayerObjectStaticFieldsBaseName, futureType); + ImageHeapRelocatableConstantSupport.singleton().registerLoadableConstant(appLayerObjectStaticFieldsBase); + } + } + } + } + + @Override + void initializeClassInAppLayer(Class c, MetaAccessProvider meta) { + for (var field : c.getDeclaredFields()) { + if (Modifier.isStatic(field.getModifiers())) { + installFieldInAppLayer(field, meta); + } + } + } + + public LayerAssignmentStatus getAssignmentStatus(AnalysisField analysisField) { + return assignmentStatusMap.computeIfAbsent(analysisField, (f) -> { + if (!(inAppLayer && analysisField.isInBaseLayer())) { + return LayerAssignmentStatus.UNDECIDED; + } + throw VMError.shouldNotReachHere("base layer types should have already been initialized"); + }); + } + + public int getPriorInstalledLayerNum(AnalysisField analysisField) { + if (!(inAppLayer && analysisField.isInBaseLayer())) { + return MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED; + } + + assert priorInstalledLayerNum.containsKey(analysisField); + return priorInstalledLayerNum.get(analysisField); + } + + public boolean preventConstantFolding(AnalysisField aField) { + var state = getAssignmentStatus(aField); + return switch (state) { + case UNDECIDED, PRIOR_LAYER -> false; + case APP_LAYER_REQUESTED, APP_LAYER_DEFERRED -> !inAppLayer; + }; + } + + public boolean installableInLayer(AnalysisField aField) { + var state = getAssignmentStatus(aField); + return switch (state) { + case UNDECIDED -> { + assert getPriorInstalledLayerNum(aField) == MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED; + yield true; + } + case PRIOR_LAYER -> { + assert aField.isInBaseLayer(); + yield false; + } + case APP_LAYER_REQUESTED, APP_LAYER_DEFERRED -> inAppLayer; + }; + } + + public UniverseBuilder.StaticFieldOffsets getAppLayerStaticFieldOffsets() { + assert inAppLayer; + return appLayerStaticFieldOffsets; + } + + public void reinitializeKnownFields(List staticFields) { + assert ImageLayerBuildingSupport.buildingExtensionLayer(); + int currentLayerNum = DynamicImageLayerInfo.getCurrentLayerNumber(); + for (var hField : staticFields) { + AnalysisField aField = hField.getWrapped(); + LayerAssignmentStatus state = getAssignmentStatus(aField); + if (state == LayerAssignmentStatus.PRIOR_LAYER) { + int layerNum = getPriorInstalledLayerNum(aField); + assert priorInstalledLocation.containsKey(aField); + int location = priorInstalledLocation.get(aField); + hField.setLocation(location, layerNum); + } else if (state == LayerAssignmentStatus.APP_LAYER_DEFERRED) { + assert inAppLayer; + assert priorInstalledLocation.containsKey(aField); + int location = priorInstalledLocation.get(aField); + hField.setLocation(location, currentLayerNum); + } + } + } + + public boolean skipStaticField(HostedField field, Function traditionalSkipFieldLogic) { + AnalysisField aField = field.getWrapped(); + LayerAssignmentStatus state = getAssignmentStatus(aField); + return switch (state) { + case UNDECIDED -> traditionalSkipFieldLogic.apply(field); + /* This field's location has already been decided. */ + case PRIOR_LAYER -> false; + case APP_LAYER_REQUESTED -> { + if (inAppLayer) { + /* + * If the value was requested in the prior layers but was never used, then the + * regular logic can proceed. + */ + yield traditionalSkipFieldLogic.apply(field); + } else { + /* + * If a field is accessed, then we do not constant fold the value, for we need + * to ensure the value created in the app layer is used. + */ + boolean isAccessed = aField.isAccessed(); + if (isAccessed) { + var previous = assignmentStatusMap.put(aField, LayerAssignmentStatus.APP_LAYER_DEFERRED); + assert previous == LayerAssignmentStatus.APP_LAYER_REQUESTED; + } + yield !isAccessed; + } + } + /* This field must be assigned a location. */ + case APP_LAYER_DEFERRED -> false; + }; + } + + public boolean wasReinitialized(HostedField field) { + var state = getAssignmentStatus(field.getWrapped()); + assert state == LayerAssignmentStatus.PRIOR_LAYER || state == LayerAssignmentStatus.APP_LAYER_DEFERRED; + + return true; + } + + /* + * Since we currently are limited to 2 layers, we know the app layer will always be the 2nd + * layer (layerNum === 1). In the future we will need to add a marker id to the hosted field and + * add additional branching logic when reading the hosted field to return this value. + */ + public static int getAppLayerNumber() { + return 1; + } + + public UniverseBuilder.StaticFieldOffsets getFutureLayerOffsets(HostedField field, int layerNum) { + assert ImageLayerBuildingSupport.buildingSharedLayer(); + AnalysisField aField = field.getWrapped(); + VMError.guarantee(getAssignmentStatus(aField) == LayerAssignmentStatus.APP_LAYER_DEFERRED); + assert layerNum == getAppLayerNumber() : layerNum; + + return appLayerStaticFieldOffsets; + } + + public FloatingNode getAppLayerStaticFieldsBaseReplacement(boolean primitive, LoweringTool tool, StructuredGraph graph) { + ImageHeapRelocatableConstant constant = primitive ? appLayerPrimitiveStaticFieldsBase : appLayerObjectStaticFieldsBase; + assert constant != null; + return ImageHeapRelocatableConstantSupport.singleton().emitLoadConstant(graph, tool.getMetaAccess(), constant); + } + + public JavaConstant getAppLayerStaticFieldBaseConstant(boolean primitive) { + var result = primitive ? appLayerPrimitiveStaticFieldsBase : appLayerObjectStaticFieldsBase; + assert result != null; + return result; + } + + void loadAllAppLayerFields() { + appLayerFields.forEach(LayeredStaticFieldSupport::getAnalysisField); + } + + @Override + public EnumSet getImageBuilderFlags() { + return LayeredImageSingletonBuilderFlags.BUILDTIME_ACCESS_ONLY; + } + + @Override + public PersistFlags preparePersist(ImageSingletonWriter writer) { + writer.writeInt("appLayerPrimitiveFieldStartingOffset", appLayerStaticFieldOffsets.nextPrimitiveField); + writer.writeInt("appLayerObjectFieldStartingOffset", appLayerStaticFieldOffsets.nextObjectField); + + HostedUniverse hUniverse = ((SVMImageLayerWriter.ImageSingletonWriterImpl) writer).getHostedUniverse(); + List knownLocations = new ArrayList<>(); + appLayerFields.forEach(obj -> { + AnalysisField aField = getAnalysisField(obj); + HostedField hField = hUniverse.lookup(aField); + if (hField.hasLocation()) { + assert getAssignmentStatus(aField) == LayerAssignmentStatus.APP_LAYER_DEFERRED; + knownLocations.add(aField.getId()); + } + }); + + writer.writeIntList("appLayerFieldsWithKnownLocations", knownLocations); + + return PersistFlags.CREATE; + } + + @SuppressWarnings("unused") + public static Object createFromLoader(ImageSingletonLoader loader) { + + Set appLayerFieldsWithKnownLocations = new HashSet<>(); + for (int id : loader.readIntList("appLayerFieldsWithKnownLocations")) { + Supplier aFieldSupplier = () -> HostedImageLayerBuildingSupport.singleton().getLoader().getAnalysisFieldForBaseLayerId(id); + appLayerFieldsWithKnownLocations.add(aFieldSupplier); + } + + var appLayerStaticFieldsOffsets = new UniverseBuilder.StaticFieldOffsets(); + appLayerStaticFieldsOffsets.nextPrimitiveField = loader.readInt("appLayerPrimitiveFieldStartingOffset"); + appLayerStaticFieldsOffsets.nextObjectField = loader.readInt("appLayerObjectFieldStartingOffset"); + return new LayeredStaticFieldSupport(Collections.unmodifiableSet(appLayerFieldsWithKnownLocations), appLayerStaticFieldsOffsets); + } +} + +@AutomaticallyRegisteredFeature +class LayeredStaticFieldSupportBaseLayerFeature implements InternalFeature { + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return ImageLayerBuildingSupport.buildingInitialLayer(); + } + + /** + * Register all application layered fields declared via the commandline. + */ + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + var config = (FeatureImpl.BeforeAnalysisAccessImpl) access; + var metaAccess = config.getMetaAccess(); + for (String className : SubstrateOptions.ApplicationLayerInitializedClasses.getValue().values()) { + LayeredClassInitialization.singleton().initializeClassInAppLayer(config.getImageClassLoader().findClassOrFail(className), metaAccess); + } + } +} + +@AutomaticallyRegisteredFeature +class LayeredStaticFieldSupportAppLayerFeature implements InternalFeature { + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return ImageLayerBuildingSupport.buildingApplicationLayer(); + } + + /** + * We must ensure all static fields with a known location are loaded so that they are installed + * during image heap writing. + */ + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + LayeredStaticFieldSupport.singleton().loadAllAppLayerFields(); + } + + /** + * We must finalize the future constants used within the base layer to ensure they are linked. + */ + @Override + public void beforeCompilation(BeforeCompilationAccess access) { + var singleton = CrossLayerConstantRegistry.singletonOrNull(); + if (singleton.constantExists(LayeredStaticFieldSupport.appLayerPrimitiveStaticFieldsBaseName)) { + singleton.finalizeFutureHeapConstant(LayeredStaticFieldSupport.appLayerPrimitiveStaticFieldsBaseName, StaticFieldsSupport.getCurrentLayerStaticPrimitiveFields()); + } + if (singleton.constantExists(LayeredStaticFieldSupport.appLayerObjectStaticFieldsBaseName)) { + singleton.finalizeFutureHeapConstant(LayeredStaticFieldSupport.appLayerObjectStaticFieldsBaseName, StaticFieldsSupport.getCurrentLayerStaticObjectFields()); + } + } +} 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 b77235c5a67b..cc3dba4ea0ee 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 @@ -319,7 +319,7 @@ ImageHeapObjectArray createMultiLayerArray(Class key, AnalysisType arrayType, if (ImageSingletons.contains(key)) { var singleton = LayeredImageSingletonSupport.singleton().lookup(key, true, true); JavaConstant singletonConstant = snippetReflectionProvider.forObject(singleton); - installElement.accept(singletonConstant, layerInfo.layerNumber); + installElement.accept(singletonConstant, DynamicImageLayerInfo.getCurrentLayerNumber()); } // finally fill any missing holes diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java index f00890f15296..37fb1fc64935 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerLoader.java @@ -175,7 +175,6 @@ public class SVMImageLayerLoader extends ImageLayerLoader { protected final Map stringToConstant = new ConcurrentHashMap<>(); protected final Map, Integer> enumToConstant = new ConcurrentHashMap<>(); protected final Map objectOffsets = new ConcurrentHashMap<>(); - protected final Map fieldLocations = new ConcurrentHashMap<>(); private final Map, Boolean> capturingClasses = new ConcurrentHashMap<>(); private final Map methodHandleCallers = new ConcurrentHashMap<>(); @@ -187,6 +186,7 @@ public class SVMImageLayerLoader extends ImageLayerLoader { protected AnalysisUniverse universe; protected AnalysisMetaAccess metaAccess; protected HostedValuesProvider hostedValuesProvider; + private final LayeredStaticFieldSupport layeredStaticFieldSupport = LayeredStaticFieldSupport.singleton(); public SVMImageLayerLoader(SVMImageLayerSnapshotUtil imageLayerSnapshotUtil, HostedImageLayerBuildingSupport imageLayerBuildingSupport, SharedLayerSnapshot.Reader snapshot, FileChannel graphChannel, boolean useSharedLayerGraphs) { @@ -1147,9 +1147,8 @@ public void addBaseLayerField(AnalysisField analysisField) { public void initializeBaseLayerField(AnalysisField analysisField) { PersistedAnalysisField.Reader fieldData = getFieldData(analysisField); assert fieldData != null : "The field should be in the base layer"; - int location = fieldData.getLocation(); - if (location != 0) { - fieldLocations.put(analysisField, location); + if (analysisField.isStatic()) { + layeredStaticFieldSupport.initializeFromFieldData(analysisField, fieldData); } boolean isAccessed = fieldData.getIsAccessed(); @@ -1630,10 +1629,6 @@ public Long getObjectOffset(JavaConstant javaConstant) { return objectOffsets.get(ImageHeapConstant.getConstantID(imageHeapConstant)); } - public int getFieldLocation(AnalysisField field) { - return fieldLocations.get(field); - } - public ImageHeapConstant getBaseLayerStaticPrimitiveFields() { return getOrCreateConstant(snapshot.getStaticPrimitiveFieldsConstantId()); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java index be25b4b47705..b6bd701dffb2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SVMImageLayerWriter.java @@ -110,6 +110,7 @@ import com.oracle.svm.core.layeredimagesingleton.ImageSingletonWriter; import com.oracle.svm.core.layeredimagesingleton.InitialLayerOnlyImageSingleton; import com.oracle.svm.core.layeredimagesingleton.LayeredImageSingleton; +import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.layeredimagesingleton.RuntimeOnlyWrapper; import com.oracle.svm.core.meta.MethodPointer; import com.oracle.svm.core.reflect.serialize.SerializationSupport; @@ -647,12 +648,6 @@ private void persistField(AnalysisField field, Supplier 0) { - builder.setLocation(location); - } - Field originalField = OriginalFieldProvider.getJavaField(field); if (originalField != null && !originalField.getDeclaringClass().equals(field.getDeclaringClass().getJavaClass())) { builder.setClassName(originalField.getDeclaringClass().getName()); @@ -664,6 +659,22 @@ private void persistField(AnalysisField field, Supplier, Object>> layeredIma } String key = singletonInfo.getKey().getName(); if (!singletonInfoMap.containsKey(singleton)) { - var writer = new ImageSingletonWriterImpl(snapshotBuilder); + var writer = new ImageSingletonWriterImpl(snapshotBuilder, hUniverse); var flags = singleton.preparePersist(writer); boolean persistData = flags == LayeredImageSingleton.PersistFlags.CREATE; var info = new SingletonPersistInfo(flags, persistData ? nextID++ : -1, persistData ? writer.getKeyValueStore() : null); @@ -1154,15 +1165,21 @@ public void writeConstant(JavaConstant constant, ConstantReference.Builder build public static class ImageSingletonWriterImpl implements ImageSingletonWriter { private final EconomicMap keyValueStore = EconomicMap.create(); private final SharedLayerSnapshot.Builder snapshotBuilder; + private final HostedUniverse hUniverse; - ImageSingletonWriterImpl(SharedLayerSnapshot.Builder snapshotBuilder) { + ImageSingletonWriterImpl(SharedLayerSnapshot.Builder snapshotBuilder, HostedUniverse hUniverse) { this.snapshotBuilder = snapshotBuilder; + this.hUniverse = hUniverse; } EconomicMap getKeyValueStore() { return keyValueStore; } + public HostedUniverse getHostedUniverse() { + return hUniverse; + } + private static boolean nonNullEntries(List list) { return list.stream().filter(Objects::isNull).findAny().isEmpty(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java index 5980a97fcdf3..ab50a1ed8a54 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/imagelayer/SharedLayerSnapshotCapnProtoSchemaHolder.java @@ -2021,7 +2021,7 @@ public final org.capnproto.PrimitiveList.Int.Reader getCallers() { public static class PersistedAnalysisField { - public static final org.capnproto.StructSize STRUCT_SIZE = new org.capnproto.StructSize((short)4,(short)3); + public static final org.capnproto.StructSize STRUCT_SIZE = new org.capnproto.StructSize((short)5,(short)3); public static final class Factory extends org.capnproto.StructFactory { public Factory() { } @@ -2181,6 +2181,20 @@ public final void setName(String value) { public final org.capnproto.Text.Builder initName(int size) { return _initPointerField(org.capnproto.Text.factory, 2, size); } + public final int getPriorInstalledLayerNum() { + return _getIntField(7); + } + public final void setPriorInstalledLayerNum(int value) { + _setIntField(7, value); + } + + public final int getAssignmentStatus() { + return _getIntField(8); + } + public final void setAssignmentStatus(int value) { + _setIntField(8, value); + } + } public static final class Reader extends org.capnproto.StructReader { @@ -2261,6 +2275,14 @@ public org.capnproto.Text.Reader getName() { return _getPointerField(org.capnproto.Text.factory, 2, null, 0, 0); } + public final int getPriorInstalledLayerNum() { + return _getIntField(7); + } + + public final int getAssignmentStatus() { + return _getIntField(8); + } + } } 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 0503266bc741..e424b1f536b3 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 @@ -24,6 +24,8 @@ */ package com.oracle.svm.hosted.meta; +import static com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED; + import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; import com.oracle.graal.pointsto.infrastructure.WrappedJavaField; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -56,7 +58,7 @@ public HostedField(AnalysisField wrapped, HostedType holder, HostedType type) { this.holder = holder; this.type = type; this.location = LOC_UNINITIALIZED; - this.installedLayerNum = MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED; + this.installedLayerNum = LAYER_NUM_UNINSTALLED; } @Override @@ -64,23 +66,37 @@ public AnalysisField getWrapped() { return wrapped; } - protected void setLocation(int location, int installLayerNum) { - wrapped.checkGuaranteeFolded(); - assert this.location == LOC_UNINITIALIZED && this.installedLayerNum == MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED; - assert location >= 0; - assert installLayerNum != MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED; + public void setLocation(int newLocation, int newInstallLayerNum) { + assert this.location == LOC_UNINITIALIZED; + assert newLocation >= 0 || newLocation == LOC_UNMATERIALIZED_STATIC_CONSTANT; + + if (newLocation != LOC_UNMATERIALIZED_STATIC_CONSTANT) { + wrapped.checkGuaranteeFolded(); + } + this.location = newLocation; + + setInstalledLayerNum(newInstallLayerNum); + } + + private void setInstalledLayerNum(int newInstallLayerNum) { + assert this.installedLayerNum == LAYER_NUM_UNINSTALLED; + assert newInstallLayerNum != LAYER_NUM_UNINSTALLED; if (wrapped.isStatic()) { - assert ImageLayerBuildingSupport.buildingImageLayer() ? installLayerNum >= 0 : installLayerNum == MultiLayeredImageSingleton.UNUSED_LAYER_NUMBER; + assert ImageLayerBuildingSupport.buildingImageLayer() ? newInstallLayerNum >= 0 : newInstallLayerNum == MultiLayeredImageSingleton.UNUSED_LAYER_NUMBER; } else { - assert installLayerNum == MultiLayeredImageSingleton.NONSTATIC_FIELD_LAYER_NUMBER; + assert newInstallLayerNum == MultiLayeredImageSingleton.NONSTATIC_FIELD_LAYER_NUMBER; } - this.location = location; - this.installedLayerNum = installLayerNum; + this.installedLayerNum = newInstallLayerNum; } - protected void setUnmaterializedStaticConstant() { - assert this.location == LOC_UNINITIALIZED && isStatic(); - this.location = LOC_UNMATERIALIZED_STATIC_CONSTANT; + protected void setUnmaterializedStaticConstant(int newInstalledLayerNum) { + assert isStatic(); + if (location == LOC_UNMATERIALIZED_STATIC_CONSTANT) { + // already set via prior layer + assert installedLayerNum != newInstalledLayerNum; + return; + } + setLocation(LOC_UNMATERIALIZED_STATIC_CONSTANT, newInstalledLayerNum); } public boolean isUnmaterialized() { @@ -186,11 +202,13 @@ public ResolvedJavaField unwrapTowardsOriginalField() { return wrapped; } + public boolean hasInstalledLayerNum() { + return !(installedLayerNum == LAYER_NUM_UNINSTALLED || installedLayerNum == MultiLayeredImageSingleton.NONSTATIC_FIELD_LAYER_NUMBER); + } + @Override public int getInstalledLayerNum() { - VMError.guarantee(!(installedLayerNum == MultiLayeredImageSingleton.LAYER_NUM_UNINSTALLED || installedLayerNum == MultiLayeredImageSingleton.NONSTATIC_FIELD_LAYER_NUMBER), - "Bad installed layer value: %s %s", - installedLayerNum, this); + VMError.guarantee(hasInstalledLayerNum(), "Bad installed layer value: %s %s", installedLayerNum, this); return installedLayerNum; } } 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 f0c1412a38fc..25eab0bf37f2 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 @@ -80,6 +80,10 @@ public boolean isStableField(ResolvedJavaField field, ConstantFieldTool tool) private boolean allowConstantFolding(ResolvedJavaField field, ConstantFieldTool tool) { var aField = asAnalysisField(field); + if (aField.preventConstantFolding()) { + return false; + } + /* * During compiler optimizations, it is possible to see field loads with a constant receiver * of a wrong type that might not even be an ImageHeapConstant. Also, we need to ensure that 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 eb84bf298f13..d5513b9c3c87 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 @@ -84,6 +84,7 @@ import com.oracle.svm.core.hub.DynamicHub; import com.oracle.svm.core.hub.DynamicHubSupport; import com.oracle.svm.core.hub.LayoutEncoding; +import com.oracle.svm.core.imagelayer.DynamicImageLayerInfo; import com.oracle.svm.core.imagelayer.ImageLayerBuildingSupport; import com.oracle.svm.core.layeredimagesingleton.MultiLayeredImageSingleton; import com.oracle.svm.core.meta.MethodPointer; @@ -93,7 +94,6 @@ 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.FieldValueInterceptionSupport; import com.oracle.svm.hosted.annotation.CustomSubstitutionMethod; import com.oracle.svm.hosted.classinitialization.ClassInitializationSupport; import com.oracle.svm.hosted.config.DynamicHubLayout; @@ -101,12 +101,13 @@ 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.imagelayer.SVMImageLayerLoader; +import com.oracle.svm.hosted.imagelayer.LayeredStaticFieldSupport; import com.oracle.svm.hosted.substitute.AnnotationSubstitutionProcessor; import com.oracle.svm.hosted.substitute.DeletedMethod; import com.oracle.svm.util.ReflectionUtil; import jdk.graal.compiler.core.common.NumUtil; +import jdk.graal.compiler.debug.Assertions; import jdk.graal.compiler.debug.DebugContext; import jdk.graal.compiler.debug.Indent; import jdk.internal.vm.annotation.Contended; @@ -196,8 +197,6 @@ public void build(DebugContext debug) { VTableBuilder.buildTables(hUniverse, hMetaAccess); buildHubs(); - processFieldLocations(); - hUniverse.orderedMethods = new ArrayList<>(hUniverse.methods.values()); Collections.sort(hUniverse.orderedMethods, HostedUniverse.METHOD_COMPARATOR); hUniverse.orderedFields = new ArrayList<>(hUniverse.fields.values()); @@ -722,15 +721,24 @@ private static int getAlignmentAdjustment(int offset, int alignment) { return alignedOffset - offset; } + private static boolean skipStaticField(HostedField field, LayeredStaticFieldSupport layeredStaticFieldSupport) { + if (layeredStaticFieldSupport != null) { + return layeredStaticFieldSupport.skipStaticField(field, UniverseBuilder::skipStaticField0); + } else { + return skipStaticField0(field); + } + } + /** * Determines whether a static field does not need to be written to the native-image heap. */ - private static boolean skipStaticField(HostedField field) { - if (field.wrapped.isWritten() || MaterializedConstantFields.singleton().contains(field.wrapped)) { + private static boolean skipStaticField0(HostedField field) { + AnalysisField aField = field.getWrapped(); + if (aField.isWritten() || MaterializedConstantFields.singleton().contains(aField)) { return false; } - if (!field.wrapped.isAccessed()) { + if (!aField.isAccessed()) { // if the field is never accessed then it does not need to be materialized return true; } @@ -739,7 +747,7 @@ private static boolean skipStaticField(HostedField field) { * The field can be treated as a constant. Check if constant is available. */ - var interceptor = field.getWrapped().getFieldValueInterceptor(); + var interceptor = aField.getFieldValueInterceptor(); if (interceptor == null) { return true; } @@ -758,44 +766,70 @@ private static boolean skipStaticField(HostedField field) { return available; } + public static class StaticFieldOffsets { + public int nextPrimitiveField = 0; + public int nextObjectField = 0; + } + private void layoutStaticFields() { - ArrayList fields = new ArrayList<>(); + ArrayList staticFields = new ArrayList<>(); for (HostedField field : hUniverse.fields.values()) { if (Modifier.isStatic(field.getModifiers())) { - fields.add(field); + staticFields.add(field); } } // Sort so that a) all Object fields are consecutive, and b) bigger types come first. - Collections.sort(fields, HostedUniverse.FIELD_COMPARATOR_RELAXED); + Collections.sort(staticFields, HostedUniverse.FIELD_COMPARATOR_RELAXED); ObjectLayout layout = ConfigurationValues.getObjectLayout(); - int nextPrimitiveField = 0; - int nextObjectField = 0; + StaticFieldOffsets currentLayerOffsets = new StaticFieldOffsets(); + LayeredStaticFieldSupport layeredStaticFieldSupport = null; + if (ImageLayerBuildingSupport.buildingImageLayer()) { + layeredStaticFieldSupport = LayeredStaticFieldSupport.singleton(); + if (ImageLayerBuildingSupport.buildingExtensionLayer()) { + layeredStaticFieldSupport.reinitializeKnownFields(staticFields); + } + if (ImageLayerBuildingSupport.buildingApplicationLayer()) { + currentLayerOffsets = layeredStaticFieldSupport.getAppLayerStaticFieldOffsets(); + } + } @SuppressWarnings("unchecked") List[] fieldsOfTypes = (List[]) new ArrayList[DynamicHubSupport.currentLayer().getMaxTypeId()]; - SVMImageLayerLoader loader = ImageLayerBuildingSupport.buildingExtensionLayer() ? HostedImageLayerBuildingSupport.singleton().getLoader() : null; - for (HostedField field : fields) { - if (skipStaticField(field)) { - // does not require memory. + int currentLayer = DynamicImageLayerInfo.getCurrentLayerNumber(); + boolean checkLayerNum = ImageLayerBuildingSupport.buildingImageLayer(); + for (HostedField field : staticFields) { + if (skipStaticField(field, layeredStaticFieldSupport)) { + // no assignment needed + if (field.isReachable()) { + // record that field was not materialized + field.setUnmaterializedStaticConstant(currentLayer); + } } else { - int layerNum = StaticFieldsSupport.getInstalledLayerNum(field.wrapped); - if (field.wrapped.isInBaseLayer()) { - field.setLocation(loader.getFieldLocation(field.wrapped), layerNum); - } else if (field.getStorageKind() == JavaKind.Object) { - field.setLocation(NumUtil.safeToInt(layout.getArrayElementOffset(JavaKind.Object, nextObjectField)), layerNum); - nextObjectField += 1; + int layerNum = StaticFieldsSupport.getInstalledLayerNum(field.getWrapped()); + if (field.getLocation() != HostedField.LOC_UNINITIALIZED) { + assert layeredStaticFieldSupport.wasReinitialized(field); } else { - int fieldSize = layout.sizeInBytes(field.getStorageKind()); - while (layout.getArrayElementOffset(JavaKind.Byte, nextPrimitiveField) % fieldSize != 0) { - // Insert padding byte for alignment - nextPrimitiveField++; + StaticFieldOffsets offsets = currentLayerOffsets; + if (checkLayerNum && currentLayer != layerNum) { + assert currentLayer < layerNum : Assertions.errorMessage(currentLayer, layerNum); + offsets = layeredStaticFieldSupport.getFutureLayerOffsets(field, layerNum); + } + if (field.getStorageKind() == JavaKind.Object) { + field.setLocation(NumUtil.safeToInt(layout.getArrayElementOffset(JavaKind.Object, offsets.nextObjectField)), layerNum); + offsets.nextObjectField += 1; + } else { + int fieldSize = layout.sizeInBytes(field.getStorageKind()); + while (layout.getArrayElementOffset(JavaKind.Byte, offsets.nextPrimitiveField) % fieldSize != 0) { + // Insert padding byte for alignment + offsets.nextPrimitiveField++; + } + field.setLocation(NumUtil.safeToInt(layout.getArrayElementOffset(JavaKind.Byte, offsets.nextPrimitiveField)), layerNum); + offsets.nextPrimitiveField += fieldSize; } - field.setLocation(NumUtil.safeToInt(layout.getArrayElementOffset(JavaKind.Byte, nextPrimitiveField)), layerNum); - nextPrimitiveField += fieldSize; } } @@ -816,8 +850,8 @@ private void layoutStaticFields() { } } - Object[] staticObjectFields = new Object[nextObjectField]; - byte[] staticPrimitiveFields = new byte[nextPrimitiveField]; + Object[] staticObjectFields = new Object[currentLayerOffsets.nextObjectField]; + byte[] staticPrimitiveFields = new byte[currentLayerOffsets.nextPrimitiveField]; StaticFieldsSupport.setData(staticObjectFields, staticPrimitiveFields); /* After initializing the static field arrays add them to the shadow heap. */ aUniverse.getHeapScanner().rescanObject(StaticFieldsSupport.getCurrentLayerStaticObjectFields()); @@ -1003,17 +1037,6 @@ private static boolean excludeFromReferenceMap(HostedField field) { } return false; } - - private void processFieldLocations() { - var fieldValueInterceptionSupport = FieldValueInterceptionSupport.singleton(); - for (HostedField hField : hUniverse.fields.values()) { - AnalysisField aField = hField.wrapped; - - if (hField.isReachable() && !hField.hasLocation() && Modifier.isStatic(hField.getModifiers()) && !aField.isWritten() && fieldValueInterceptionSupport.isValueAvailable(aField)) { - hField.setUnmaterializedStaticConstant(); - } - } - } } @AutomaticallyRegisteredFeature From 598d59cb9ac594ea360cf5c5d98af7672530ff8b Mon Sep 17 00:00:00 2001 From: Tom Shull Date: Fri, 28 Mar 2025 16:33:13 +0100 Subject: [PATCH 2/2] reduce overhead of analysismethodcalleewalker --- .../code/AnalysisMethodCalleeWalker.java | 10 ++++------ .../code/RestrictHeapAccessCalleesImpl.java | 20 +++++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisMethodCalleeWalker.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisMethodCalleeWalker.java index b4393a8cbd57..52c2a5be7e01 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisMethodCalleeWalker.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/AnalysisMethodCalleeWalker.java @@ -24,8 +24,7 @@ */ package com.oracle.svm.hosted.code; -import java.util.ArrayList; -import java.util.List; +import org.graalvm.collections.EconomicSet; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.InvokeInfo; @@ -41,10 +40,10 @@ public class AnalysisMethodCalleeWalker { /** A stack of methods that are currently being examined, to detect cycles in the call graph. */ - private final List path; + private final EconomicSet path; public AnalysisMethodCalleeWalker() { - path = new ArrayList<>(); + path = EconomicSet.create(); } /** @@ -65,14 +64,13 @@ public boolean walkMethod(AnalysisMethod method, CallPathVisitor visitor) { } VisitResult walkMethodAndCallees(AnalysisMethod method, AnalysisMethod caller, BytecodePosition invokePosition, CallPathVisitor visitor) { - if (path.contains(method)) { + if (!path.add(method)) { /* * If the method is already on the path then I am in the middle of visiting it, so just * keep walking. */ return VisitResult.CUT; } - path.add(method); try { VisitResult directResult = visitor.visitMethod(method, caller, invokePosition, path.size()); if (directResult != VisitResult.CONTINUE) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/RestrictHeapAccessCalleesImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/RestrictHeapAccessCalleesImpl.java index 22fc61f68288..ad0691e4b2b3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/RestrictHeapAccessCalleesImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/RestrictHeapAccessCalleesImpl.java @@ -25,14 +25,14 @@ package com.oracle.svm.hosted.code; import java.lang.reflect.Constructor; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Set; +import org.graalvm.collections.EconomicSet; +import org.graalvm.collections.UnmodifiableEconomicSet; import org.graalvm.nativeimage.ImageSingletons; import com.oracle.graal.pointsto.meta.AnalysisMethod; @@ -62,7 +62,7 @@ public class RestrictHeapAccessCalleesImpl implements RestrictHeapAccessCallees private Map calleeToCallerMap; /** AssertionErrors are cut points, because their allocations are removed. */ - private List assertionErrorConstructorList; + private UnmodifiableEconomicSet assertionErrorConstructorList; /** Initialize the set of callees only once. */ private boolean initialized; @@ -70,12 +70,12 @@ public class RestrictHeapAccessCalleesImpl implements RestrictHeapAccessCallees /** Constructor for the singleton instance. */ public RestrictHeapAccessCalleesImpl() { calleeToCallerMap = Collections.emptyMap(); - this.assertionErrorConstructorList = Collections.emptyList(); + this.assertionErrorConstructorList = EconomicSet.create(); initialized = false; } /** This gets called multiple times, but I only need one AnalysisMethod to be happy. */ - public void setAssertionErrorConstructors(List resolvedConstructorList) { + public void setAssertionErrorConstructors(EconomicSet resolvedConstructorList) { if (assertionErrorConstructorList.isEmpty()) { assertionErrorConstructorList = resolvedConstructorList; } @@ -171,10 +171,10 @@ static class MethodAggregator extends AnalysisMethodCalleeWalker.CallPathVisitor private final Map calleeToCallerMap; /** The constructor {@link AssertionError#AssertionError()}. */ - private final List assertionErrorConstructorList; + private final UnmodifiableEconomicSet assertionErrorConstructorList; /** Constructor. */ - MethodAggregator(Map calleeToCallerMap, List assertionErrorConstructorList) { + MethodAggregator(Map calleeToCallerMap, UnmodifiableEconomicSet assertionErrorConstructorList) { this.calleeToCallerMap = calleeToCallerMap; this.assertionErrorConstructorList = assertionErrorConstructorList; } @@ -266,12 +266,12 @@ public void afterRegistration(AfterRegistrationAccess access) { /** This is called during analysis, to find the AssertionError constructors. */ @Override public void duringAnalysis(DuringAnalysisAccess access) { - List assertionErrorConstructorList = initializeAssertionErrorConstructors(access); + EconomicSet assertionErrorConstructorList = initializeAssertionErrorConstructors(access); ((RestrictHeapAccessCalleesImpl) ImageSingletons.lookup(RestrictHeapAccessCallees.class)).setAssertionErrorConstructors(assertionErrorConstructorList); } - private static List initializeAssertionErrorConstructors(DuringAnalysisAccess access) { - final List result = new ArrayList<>(); + private static EconomicSet initializeAssertionErrorConstructors(DuringAnalysisAccess access) { + final EconomicSet result = EconomicSet.create(9); result.add(findAssertionConstructor(access)); result.add(findAssertionConstructor(access, boolean.class)); result.add(findAssertionConstructor(access, char.class));