From e2e25695810311f75bf33833adcf3e76d664a05c Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Thu, 13 Jun 2024 09:57:56 -0700 Subject: [PATCH] Track never-null instance fields in the static analysis --- .../com/oracle/graal/pointsto/BigBang.java | 2 +- .../graal/pointsto/api/PointstoOptions.java | 3 + .../graal/pointsto/flow/FieldTypeFlow.java | 17 +--- .../pointsto/flow/MethodTypeFlowBuilder.java | 9 ++ .../flow/context/object/AnalysisObject.java | 6 +- .../ContextSensitiveAnalysisObject.java | 6 +- .../graal/pointsto/heap/ImageHeapScanner.java | 7 +- .../graal/pointsto/meta/AnalysisField.java | 83 +++++-------------- .../pointsto/meta/PointsToAnalysisField.java | 8 +- .../pointsto/meta/PointsToAnalysisType.java | 13 +++ .../InlineBeforeAnalysisGraphDecoder.java | 2 +- .../phases/InlineBeforeAnalysisPolicy.java | 4 +- .../pointsto/results/StrengthenGraphs.java | 22 ++++- .../graal/pointsto/typestate/TypeState.java | 9 ++ .../allocationprofile/AllocationCounter.java | 3 +- .../com/oracle/svm/core/hub/DynamicHub.java | 2 +- .../RuntimeCompilationFeature.java | 6 +- .../oracle/svm/graal/meta/SubstrateField.java | 3 +- .../oracle/svm/graal/meta/SubstrateType.java | 3 +- .../hosted/ameta/CustomTypeFieldHandler.java | 23 ++--- .../analysis/NativeImagePointsToAnalysis.java | 5 +- ...NativeImageReachabilityAnalysisEngine.java | 4 +- .../PointsToCustomTypeFieldHandler.java | 54 ++++++------ .../SimulateClassInitializerPolicy.java | 2 +- .../svm/hosted/code/FactoryMethodSupport.java | 5 ++ .../InlineBeforeAnalysisPolicyImpl.java | 4 +- .../InlineBeforeAnalysisPolicyUtils.java | 39 ++++++--- .../hosted/reflect/ReflectionDataBuilder.java | 8 ++ 28 files changed, 186 insertions(+), 166 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java index 6307f6991fb4..90f84898dbff 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/BigBang.java @@ -108,7 +108,7 @@ default void onFieldAccessed(AnalysisField field) { } @SuppressWarnings("unused") - default void injectFieldTypes(AnalysisField aField, AnalysisType... customTypes) { + default void injectFieldTypes(AnalysisField aField, List customTypes, boolean canBeNull) { } @SuppressWarnings("unused") diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java index bfb6d0f166eb..d7dff7ca210a 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java @@ -149,6 +149,9 @@ protected void onValueUpdate(EconomicMap, Object> values, Boolean o @Option(help = "Run conditional elimination before static analysis.", type = Expert)// public static final OptionKey ConditionalEliminationBeforeAnalysis = new OptionKey<>(true); + @Option(help = "Track in the static analysis whether an instance field is never null.")// + public static final OptionKey TrackNeverNullInstanceFields = new OptionKey<>(true); + /** * Controls the static analysis context sensitivity. Available values: *

diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldTypeFlow.java index 402543b61c8e..168c4d483987 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldTypeFlow.java @@ -39,21 +39,6 @@ public class FieldTypeFlow extends TypeFlow { private static final AtomicReferenceFieldUpdater FILTER_FLOW_UPDATER = AtomicReferenceFieldUpdater.newUpdater(FieldTypeFlow.class, FieldFilterTypeFlow.class, "filterFlow"); - private static TypeState initialFieldState(AnalysisField field) { - if (field.getStorageKind().isPrimitive()) { - return TypeState.forPrimitiveConstant(0); - } else if (field.canBeNull()) { - /* - * All object type instance fields of a new object can be null. Instance fields are null - * in the time between the new-instance and the first write to a field. This is even - * true for non-null final fields because even final fields are null until they are - * initialized in a constructor. - */ - return TypeState.forNull(); - } - return TypeState.forEmpty(); - } - /** The holder of the field flow (null for static fields). */ private final AnalysisObject object; @@ -65,7 +50,7 @@ public FieldTypeFlow(AnalysisField field, AnalysisType type) { } public FieldTypeFlow(AnalysisField field, AnalysisType type, AnalysisObject object) { - super(field, filterUncheckedInterface(type), initialFieldState(field)); + super(field, filterUncheckedInterface(type), TypeState.forEmpty()); this.object = object; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java index cf9cb8a4f433..ee8097982b71 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java @@ -252,6 +252,10 @@ public static void registerUsedElements(PointsToAnalysis bb, StructuredGraph gra NewInstanceNode node = (NewInstanceNode) n; AnalysisType type = (AnalysisType) node.instanceClass(); type.registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node)); + for (var f : type.getInstanceFields(true)) { + var field = (AnalysisField) f; + field.getInitialFlow().addState(bb, TypeState.defaultValueForKind(field.getStorageKind())); + } } else if (n instanceof NewInstanceWithExceptionNode) { NewInstanceWithExceptionNode node = (NewInstanceWithExceptionNode) n; @@ -1441,6 +1445,11 @@ protected void processCommitAllocation(CommitAllocationNode commitAllocationNode AnalysisField field = (AnalysisField) ((VirtualInstanceNode) virtualObject).field(i); processStoreField(commitAllocationNode, field, object, value, value.getStackKind(), state); } + } else { + if (!type.isArray()) { + AnalysisField field = (AnalysisField) ((VirtualInstanceNode) virtualObject).field(i); + field.getInitialFlow().addState(bb, TypeState.defaultValueForKind(field.getStorageKind())); + } } } objectStartIndex += virtualObject.entryCount(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java index b130d4e84e5a..e0cddbeeb550 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java @@ -262,9 +262,9 @@ private void checkField(PointsToAnalysis bb, TypeFlow objectFlow, BytecodePos @SuppressWarnings("unused") protected void linkFieldFlows(PointsToAnalysis bb, AnalysisField field, FieldTypeStore fieldStore) { // link the initial instance field flow to the field write flow - field.getInitialInstanceFieldFlow().addUse(bb, fieldStore.writeFlow()); - // link the field read flow to the context insensitive instance field flow - fieldStore.readFlow().addUse(bb, field.getInstanceFieldFlow()); + field.getInitialFlow().addUse(bb, fieldStore.writeFlow()); + // link the field read flow to the sink flow that accumulates all field types + fieldStore.readFlow().addUse(bb, field.getSinkFlow()); } /** diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ContextSensitiveAnalysisObject.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ContextSensitiveAnalysisObject.java index e69f3eab74ae..377e89cb842d 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ContextSensitiveAnalysisObject.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ContextSensitiveAnalysisObject.java @@ -163,9 +163,9 @@ public FieldTypeFlow getInstanceFieldFlow(PointsToAnalysis bb, TypeFlow objec @Override protected void linkFieldFlows(PointsToAnalysis bb, AnalysisField field, FieldTypeStore fieldStore) { // link the initial instance field flow to the field write flow - field.getInitialInstanceFieldFlow().addUse(bb, fieldStore.writeFlow()); - // link the field read flow to the instance field flow - fieldStore.readFlow().addUse(bb, field.getInstanceFieldFlow()); + field.getInitialFlow().addUse(bb, fieldStore.writeFlow()); + // link the field read flow to the sink flow that accumulates all field types + fieldStore.readFlow().addUse(bb, field.getSinkFlow()); // Also link the field read flow the field flow on the context insensitive object. // This ensures that the all values flowing into a context-sensitive field flow // are also visible from the context-insensitive field flow. 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 2c971bf08106..04e43cbba1bc 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 @@ -26,6 +26,7 @@ import java.lang.reflect.Field; import java.util.Collection; +import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; @@ -143,7 +144,7 @@ public void onFieldRead(AnalysisField field) { if (fieldType.isArray() || (fieldType.isInstanceClass() && !fieldType.isAbstract())) { fieldType.registerAsInstantiated(field); } - bb.injectFieldTypes(field, fieldType); + bb.injectFieldTypes(field, List.of(fieldType), true); } return; } @@ -151,8 +152,6 @@ public void onFieldRead(AnalysisField field) { JavaConstant fieldValue = readStaticFieldValue(field); markReachable(fieldValue, reason); notifyAnalysis(field, null, fieldValue, reason); - } else if (field.canBeNull()) { - notifyAnalysis(field, null, JavaConstant.NULL_POINTER, reason); } } else { /* Trigger field scanning for the already processed objects. */ @@ -616,8 +615,6 @@ private void updateInstanceField(AnalysisField field, ImageHeapInstance imageHea JavaConstant fieldValue = imageHeapInstance.readFieldValue(field); markReachable(fieldValue, reason, onAnalysisModified); notifyAnalysis(field, imageHeapInstance, fieldValue, reason, onAnalysisModified); - } else if (field.canBeNull()) { - notifyAnalysis(field, imageHeapInstance, JavaConstant.NULL_POINTER, reason, onAnalysisModified); } } 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 2335a6268696..c8486dff0990 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 @@ -31,14 +31,12 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; -import com.oracle.graal.pointsto.api.HostVM; import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.flow.ContextInsensitiveFieldTypeFlow; import com.oracle.graal.pointsto.flow.FieldTypeFlow; import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider; import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider; import com.oracle.graal.pointsto.infrastructure.WrappedJavaField; -import com.oracle.graal.pointsto.typestate.TypeState; import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.graal.pointsto.util.AnalysisFuture; import com.oracle.graal.pointsto.util.AtomicUtils; @@ -73,17 +71,17 @@ public abstract class AnalysisField extends AnalysisElement implements WrappedJa public final ResolvedJavaField wrapped; - /** Field type flow for the static fields. */ - protected FieldTypeFlow staticFieldFlow; - - /** Initial field type flow, i.e., as specified by the analysis client. */ - protected FieldTypeFlow initialInstanceFieldFlow; - + /** + * Initial field type flow, i.e., as specified by the analysis client. It can be used to inject + * specific types into a field that the analysis would not see on its own, and to inject the + * null value into a field. + */ + protected FieldTypeFlow initialFlow; /** * Field type flow that reflects all the types flowing in this field on its declaring type and - * all the sub-types. It doesn't track any context-sensitive information. + * all the sub-types. It does not track any context-sensitive information. */ - protected ContextInsensitiveFieldTypeFlow instanceFieldFlow; + protected FieldTypeFlow sinkFlow; /** The reason flags contain a {@link BytecodePosition} or a reason object. */ @SuppressWarnings("unused") private volatile Object isRead; @@ -92,12 +90,6 @@ public abstract class AnalysisField extends AnalysisElement implements WrappedJa @SuppressWarnings("unused") private volatile Object isFolded; @SuppressWarnings("unused") private volatile Object isUnsafeAccessed; - /** - * By default all instance fields are null before are initialized. It can be specified by - * {@link HostVM} that certain fields will not be null. - */ - private boolean canBeNull; - private ConcurrentMap readBy; private ConcurrentMap writtenBy; @@ -129,14 +121,16 @@ public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField) declaringClass = universe.lookup(wrappedField.getDeclaringClass()); fieldType = getDeclaredType(universe, wrappedField); + initialFlow = new FieldTypeFlow(this, getType()); if (this.isStatic()) { - this.canBeNull = false; - this.staticFieldFlow = new FieldTypeFlow(this, getType()); - this.initialInstanceFieldFlow = null; + /* There is never any context-sensitivity for static fields. */ + sinkFlow = initialFlow; } else { - this.canBeNull = !getStorageKind().isPrimitive(); - this.instanceFieldFlow = new ContextInsensitiveFieldTypeFlow(this, getType()); - this.initialInstanceFieldFlow = new FieldTypeFlow(this, getType()); + /* + * Regardless of the context-sensitivity policy, there is always this single type flow + * that accumulates all types. + */ + sinkFlow = new ContextInsensitiveFieldTypeFlow(this, getType()); } if (universe.hostVM().useBaseLayer()) { @@ -202,45 +196,22 @@ public JavaKind getStorageKind() { } - /** - * Returns all possible types that this field can have. The result is not context sensitive, - * i.e., it is a union of all types found in all contexts. - */ - public TypeState getTypeState() { - if (getType().getStorageKind() != JavaKind.Object) { - return null; - } else if (isStatic()) { - return staticFieldFlow.getState(); - } else { - return getInstanceFieldTypeState(); - } - } - - public TypeState getInstanceFieldTypeState() { - return instanceFieldFlow.getState(); + public FieldTypeFlow getInitialFlow() { + return initialFlow; } - public FieldTypeFlow getInitialInstanceFieldFlow() { - return initialInstanceFieldFlow; + public FieldTypeFlow getSinkFlow() { + return sinkFlow; } public FieldTypeFlow getStaticFieldFlow() { assert Modifier.isStatic(this.getModifiers()) : this; - - return staticFieldFlow; - } - - /** Get the field type flow, stripped of any context. */ - public ContextInsensitiveFieldTypeFlow getInstanceFieldFlow() { - assert !Modifier.isStatic(this.getModifiers()) : this; - - return instanceFieldFlow; + return sinkFlow; } public void cleanupAfterAnalysis() { - staticFieldFlow = null; - instanceFieldFlow = null; - initialInstanceFieldFlow = null; + initialFlow = null; + sinkFlow = null; readBy = null; writtenBy = null; } @@ -420,14 +391,6 @@ public void setFieldValueInterceptor(Object fieldValueInterceptor) { this.fieldValueInterceptor = fieldValueInterceptor; } - public void setCanBeNull(boolean canBeNull) { - this.canBeNull = canBeNull; - } - - public boolean canBeNull() { - return canBeNull; - } - @Override public String getName() { return wrapped.getName(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisField.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisField.java index eed43a1e5e06..e351e6da663e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisField.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisField.java @@ -99,11 +99,7 @@ public boolean registerAsUnsafeAccessed(Object reason) { public void saturatePrimitiveField() { assert fieldType.isPrimitive() || fieldType.isWordType() : this; var bb = ((PointsToAnalysis) getUniverse().getBigbang()); - if (isStatic()) { - staticFieldFlow.addState(bb, TypeState.anyPrimitiveState()); - } else { - initialInstanceFieldFlow.addState(bb, TypeState.anyPrimitiveState()); - instanceFieldFlow.addState(bb, TypeState.anyPrimitiveState()); - } + initialFlow.addState(bb, TypeState.anyPrimitiveState()); + sinkFlow.addState(bb, TypeState.anyPrimitiveState()); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisType.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisType.java index 78e53212c156..6f035f0cb98c 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisType.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisType.java @@ -55,6 +55,19 @@ public class PointsToAnalysisType extends AnalysisType { super(universe, javaType, storageKind, objectType, cloneableType); } + @Override + public boolean registerAsUnsafeAllocated(Object reason) { + boolean result = super.registerAsUnsafeAllocated(reason); + if (result) { + var bb = (PointsToAnalysis) universe.getBigbang(); + for (var f : getInstanceFields(true)) { + var field = (AnalysisField) f; + field.getInitialFlow().addState(bb, TypeState.defaultValueForKind(field.getStorageKind())); + } + } + return result; + } + /** * @see AnalysisType#registerAsAssignable(BigBang) */ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysisGraphDecoder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysisGraphDecoder.java index 4c33c88bb6c0..e1fb9ca1b7b7 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysisGraphDecoder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysisGraphDecoder.java @@ -93,7 +93,7 @@ public class InlineBeforeAnalysisMethodScope extends PEMethodScope { for (int i = 0; i < arguments.length; i++) { constArgsWithReceiver[i] = arguments[i].isConstant(); } - policyScope = policy.openCalleeScope(cast(caller).policyScope, method); + policyScope = policy.openCalleeScope(cast(caller).policyScope, (AnalysisMethod) caller.method, method); if (graph.getDebug().isLogEnabled()) { graph.getDebug().logv(" ".repeat(inliningDepth) + "openCalleeScope for " + method.format("%H.%n(%p)") + ": " + policyScope); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysisPolicy.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysisPolicy.java index fed6b6483af9..633342c121c1 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysisPolicy.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/phases/InlineBeforeAnalysisPolicy.java @@ -99,7 +99,7 @@ protected InlineBeforeAnalysisPolicy(NodePlugin[] nodePlugins) { protected abstract AbstractPolicyScope createRootScope(); - protected abstract AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, AnalysisMethod method); + protected abstract AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, AnalysisMethod caller, AnalysisMethod method); /** @see InlineBeforeAnalysisGraphDecoder#shouldOmitIntermediateMethodInStates */ protected abstract boolean shouldOmitIntermediateMethodInState(AnalysisMethod method); @@ -142,7 +142,7 @@ protected AbstractPolicyScope createRootScope() { } @Override - protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, AnalysisMethod method) { + protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, AnalysisMethod caller, AnalysisMethod method) { throw AnalysisError.shouldNotReachHere("NO_INLINING policy should not try to inline"); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java index 7bd998a886e8..96191859aba3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java @@ -172,12 +172,30 @@ public StrengthenGraphs(BigBang bb, Universe converter) { if (ImageBuildStatistics.Options.CollectImageBuildStatistics.getValue(bb.getOptions())) { beforeCounters = new StrengthenGraphsCounters(ImageBuildStatistics.CheckCountLocation.BEFORE_STRENGTHEN_GRAPHS); afterCounters = new StrengthenGraphsCounters(ImageBuildStatistics.CheckCountLocation.AFTER_STRENGTHEN_GRAPHS); + reportNeverNullInstanceFields(bb); } else { beforeCounters = null; afterCounters = null; } } + private static void reportNeverNullInstanceFields(BigBang bb) { + int neverNull = 0; + int canBeNull = 0; + for (var field : bb.getUniverse().getFields()) { + if (!field.isStatic() && field.isReachable() && field.getType().getStorageKind() == JavaKind.Object) { + if (field.getSinkFlow().getState().canBeNull()) { + canBeNull++; + } else { + neverNull++; + } + } + } + ImageBuildStatistics imageBuildStats = ImageBuildStatistics.counters(); + imageBuildStats.insert("instancefield_neverNull").addAndGet(neverNull); + imageBuildStats.insert("instancefield_canBeNull").addAndGet(canBeNull); + } + @SuppressWarnings("try") public final void applyResults(AnalysisMethod method) { var nodeReferences = method instanceof PointsToAnalysisMethod ptaMethod && ptaMethod.getTypeFlow().flowsGraphCreated() @@ -369,9 +387,7 @@ public void simplify(Node n, SimplifierTool tool) { * update the stamp directly to the stamp that is correct for the whole method and * all inlined methods. */ - var field = (AnalysisField) node.field(); - var fieldTypeFlow = field.isStatic() ? field.getStaticFieldFlow() : field.getInstanceFieldFlow(); - Object fieldNewStampOrConstant = strengthenStampFromTypeFlow(node, fieldTypeFlow, node, tool); + Object fieldNewStampOrConstant = strengthenStampFromTypeFlow(node, ((AnalysisField) node.field()).getSinkFlow(), node, tool); if (fieldNewStampOrConstant instanceof JavaConstant) { ConstantNode replacement = ConstantNode.forConstant((JavaConstant) fieldNewStampOrConstant, bb.getMetaAccess(), graph); graph.replaceFixedWithFloating(node, replacement); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java index 0e9a2b10ed84..4fc5278b4dd3 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java @@ -36,6 +36,7 @@ import com.oracle.graal.pointsto.meta.AnalysisType; import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.JavaKind; public abstract class TypeState { @@ -144,6 +145,14 @@ public static TypeState forNull() { return NullTypeState.SINGLETON; } + public static TypeState defaultValueForKind(JavaKind javaKind) { + if (javaKind.isPrimitive()) { + return TypeState.forPrimitiveConstant(0); + } else { + return TypeState.forNull(); + } + } + public static TypeState forPrimitiveConstant(long value) { return PrimitiveConstantTypeState.forValue(value); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/allocationprofile/AllocationCounter.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/allocationprofile/AllocationCounter.java index 51d29f646222..92efb139c24f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/allocationprofile/AllocationCounter.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/allocationprofile/AllocationCounter.java @@ -41,7 +41,8 @@ public class AllocationCounter { /** Name of the method that increments this counter. */ private final String name; /** Next allocation counter - this value is only written during compilation. */ - @UnknownObjectField(availability = AfterCompilation.class) private AllocationCounter next; + @UnknownObjectField(canBeNull = true, availability = AfterCompilation.class)// + private AllocationCounter next; /** Number of allocations. */ private long count; /** Size of allocations in bytes. */ diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index cb125bbdfe9a..f39b6526498c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -372,7 +372,7 @@ public final class DynamicHub implements AnnotatedElement, java.lang.reflect.Typ * Back link to the SubstrateType used by the substrate meta access. Only used for the subset of * types for which a SubstrateType exists. */ - @UnknownObjectField(fullyQualifiedTypes = "com.oracle.svm.graal.meta.SubstrateType")// + @UnknownObjectField(fullyQualifiedTypes = "com.oracle.svm.graal.meta.SubstrateType", canBeNull = true)// private SharedType metaType; /** diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java index 9636986e868a..df2cbb097472 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/hosted/runtimecompilation/RuntimeCompilationFeature.java @@ -910,13 +910,13 @@ protected AbstractPolicyScope createRootScope() { } @Override - protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, AnalysisMethod method) { + protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, AnalysisMethod caller, AnalysisMethod method) { if (outer instanceof InlineBeforeAnalysisPolicyUtils.AccumulativeInlineScope accOuter) { /* * Once the accumulative policy is activated, we cannot return to the trivial * policy. */ - return inliningUtils.createAccumulativeInlineScope(accOuter, method, DeoptimizationUtils.RUNTIME_COMPILATION_INVALID_NODES); + return inliningUtils.createAccumulativeInlineScope(accOuter, caller, method, DeoptimizationUtils.RUNTIME_COMPILATION_INVALID_NODES); } assert outer == null || outer instanceof RuntimeCompilationAlwaysInlineScope : "unexpected outer scope: " + outer; @@ -936,7 +936,7 @@ protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, Analysi return new RuntimeCompilationAlwaysInlineScope(inliningDepth); } else { // start with a new accumulative inline scope - return inliningUtils.createAccumulativeInlineScope(null, method, DeoptimizationUtils.RUNTIME_COMPILATION_INVALID_NODES); + return inliningUtils.createAccumulativeInlineScope(null, caller, method, DeoptimizationUtils.RUNTIME_COMPILATION_INVALID_NODES); } } } diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java index e2885e2a62ad..64ac2c312209 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateField.java @@ -59,7 +59,8 @@ public class SubstrateField implements SharedField { @UnknownPrimitiveField(availability = AfterCompilation.class) int location; @UnknownPrimitiveField(availability = AfterCompilation.class) private boolean isAccessed; @UnknownPrimitiveField(availability = AfterCompilation.class) private boolean isWritten; - @UnknownObjectField(types = {DirectSubstrateObjectConstant.class, PrimitiveConstant.class}, fullyQualifiedTypes = "jdk.vm.ci.meta.NullConstant", availability = AfterCompilation.class)// + @UnknownObjectField(types = {DirectSubstrateObjectConstant.class, PrimitiveConstant.class}, fullyQualifiedTypes = "jdk.vm.ci.meta.NullConstant", // + canBeNull = true, availability = AfterCompilation.class)// JavaConstant constantValue; @Platforms(Platform.HOSTED_ONLY.class) diff --git a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateType.java b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateType.java index 9973c932bad2..44c6e88c3849 100644 --- a/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateType.java +++ b/substratevm/src/com.oracle.svm.graal/src/com/oracle/svm/graal/meta/SubstrateType.java @@ -60,7 +60,8 @@ public class SubstrateType implements SharedType { @UnknownObjectField(canBeNull = true)// SubstrateField[] rawAllInstanceFields; - @UnknownObjectField protected DynamicHub uniqueConcreteImplementation; + @UnknownObjectField(canBeNull = true)// + protected DynamicHub uniqueConcreteImplementation; public SubstrateType(JavaKind kind, DynamicHub hub) { this.kind = kind; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/CustomTypeFieldHandler.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/CustomTypeFieldHandler.java index 86c731bd5f9c..0242086c3892 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/CustomTypeFieldHandler.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ameta/CustomTypeFieldHandler.java @@ -60,32 +60,25 @@ public void handleField(AnalysisField field) { assert field.isAccessed(); if (fieldValueInterceptionSupport.hasFieldValueTransformer(field)) { if (field.getJavaKind().isObject() && !fieldValueInterceptionSupport.isValueAvailable(field)) { - injectFieldTypes(field, field.getType()); + injectFieldTypes(field, List.of(field.getType()), true); } else if (bb.trackPrimitiveValues() && field.getStorageKind().isPrimitive() && field instanceof PointsToAnalysisField ptaField) { ptaField.saturatePrimitiveField(); } } else if (fieldValueInterceptionSupport.lookupFieldValueInterceptor(field) instanceof FieldValueComputer fieldValueComputer) { if (field.getStorageKind().isObject()) { - field.setCanBeNull(fieldValueComputer.canBeNull()); - injectFieldTypes(field, transformTypes(field, fieldValueComputer.types())); + List types = transformTypes(field, fieldValueComputer.types()); + for (AnalysisType type : types) { + assert !type.isPrimitive() : type + " for " + field; + type.registerAsInstantiated("Is declared as the type of an unknown object field."); + } + injectFieldTypes(field, types, fieldValueComputer.canBeNull()); } else if (bb.trackPrimitiveValues() && field.getStorageKind().isPrimitive() && field instanceof PointsToAnalysisField ptaField) { ptaField.saturatePrimitiveField(); } } } - private void injectFieldTypes(AnalysisField field, List customTypes) { - for (AnalysisType type : customTypes) { - if (!type.isPrimitive()) { - type.registerAsInstantiated("Is declared as the type of an unknown object field."); - } - } - - /* Use the annotation types, instead of the declared type, in the field initialization. */ - injectFieldTypes(field, customTypes.toArray(new AnalysisType[0])); - } - - public abstract void injectFieldTypes(AnalysisField aField, AnalysisType... customTypes); + public abstract void injectFieldTypes(AnalysisField aField, List customTypes, boolean canBeNull); private List transformTypes(AnalysisField field, List> types) { List customTypes = new ArrayList<>(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImagePointsToAnalysis.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImagePointsToAnalysis.java index 29d9edbb3146..4b855824f34e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImagePointsToAnalysis.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImagePointsToAnalysis.java @@ -28,6 +28,7 @@ import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; +import java.util.List; import java.util.stream.Stream; import com.oracle.graal.pointsto.ClassInclusionPolicy; @@ -119,8 +120,8 @@ public void onFieldAccessed(AnalysisField field) { } @Override - public void injectFieldTypes(AnalysisField aField, AnalysisType... customTypes) { - customTypeFieldHandler.injectFieldTypes(aField, customTypes); + public void injectFieldTypes(AnalysisField aField, List customTypes, boolean canBeNull) { + customTypeFieldHandler.injectFieldTypes(aField, customTypes, canBeNull); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImageReachabilityAnalysisEngine.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImageReachabilityAnalysisEngine.java index 12b8d1430d8b..558e46592890 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImageReachabilityAnalysisEngine.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/NativeImageReachabilityAnalysisEngine.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.hosted.analysis; +import java.util.List; + import com.oracle.graal.pointsto.ClassInclusionPolicy; import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; @@ -58,7 +60,7 @@ public NativeImageReachabilityAnalysisEngine(OptionValues options, AnalysisUnive this.dynamicHubInitializer = new DynamicHubInitializer(this); this.unknownFieldHandler = new CustomTypeFieldHandler(this, metaAccess) { @Override - public void injectFieldTypes(AnalysisField aField, AnalysisType... declaredTypes) { + public void injectFieldTypes(AnalysisField aField, List declaredTypes, boolean canBeNull) { assert aField.getJavaKind().isObject(); aField.registerAsAccessed("@UnknownObjectField annotated field."); for (AnalysisType declaredType : declaredTypes) { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/PointsToCustomTypeFieldHandler.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/PointsToCustomTypeFieldHandler.java index 25e369316edd..df472fe169a2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/PointsToCustomTypeFieldHandler.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/analysis/PointsToCustomTypeFieldHandler.java @@ -24,6 +24,8 @@ */ package com.oracle.svm.hosted.analysis; +import java.util.List; + import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.flow.TypeFlow; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -41,7 +43,7 @@ public PointsToCustomTypeFieldHandler(BigBang bb, AnalysisMetaAccess metaAccess) * which are not available during analysis. */ @Override - public void injectFieldTypes(AnalysisField aField, AnalysisType... customTypes) { + public void injectFieldTypes(AnalysisField aField, List customTypes, boolean canBeNull) { NativeImagePointsToAnalysis analysis = (NativeImagePointsToAnalysis) bb; assert aField.getJavaKind().isObject(); @@ -51,34 +53,30 @@ public void injectFieldTypes(AnalysisField aField, AnalysisType... customTypes) if (type.isPrimitive() || type.isWordType()) { continue; } - TypeFlow typeFlow = type.getTypeFlow(analysis, true); - if (aField.isStatic()) { - typeFlow.addUse(analysis, aField.getStaticFieldFlow()); - } else { - typeFlow.addUse(analysis, aField.getInitialInstanceFieldFlow()); - if (type.isArray()) { - AnalysisType fieldComponentType = type.getComponentType(); - aField.getInitialInstanceFieldFlow().addUse(analysis, aField.getInstanceFieldFlow()); - if (!(fieldComponentType.isPrimitive() || fieldComponentType.isWordType())) { - /* - * Write the component type abstract object into the field array elements - * type flow, i.e., the array elements type flow of the abstract object of - * the field declared type. - * - * This is required so that the index loads from this array return all the - * possible objects that can be stored in the array. - */ - TypeFlow elementsFlow = type.getContextInsensitiveAnalysisObject().getArrayElementsFlow(analysis, true); - fieldComponentType.getTypeFlow(analysis, false).addUse(analysis, elementsFlow); + type.getTypeFlow(analysis, canBeNull).addUse(analysis, aField.getInitialFlow()); + + if (type.isArray()) { + AnalysisType fieldComponentType = type.getComponentType(); + aField.getInitialFlow().addUse(analysis, aField.getSinkFlow()); + if (!(fieldComponentType.isPrimitive() || fieldComponentType.isWordType())) { + /* + * Write the component type abstract object into the field array elements type + * flow, i.e., the array elements type flow of the abstract object of the field + * declared type. + * + * This is required so that the index loads from this array return all the + * possible objects that can be stored in the array. + */ + TypeFlow elementsFlow = type.getContextInsensitiveAnalysisObject().getArrayElementsFlow(analysis, true); + fieldComponentType.getTypeFlow(analysis, false).addUse(analysis, elementsFlow); - /* - * In the current implementation it is not necessary to do it it recursively - * for multidimensional arrays since we don't model individual array - * elements, so from the point of view of the static analysis the field's - * array elements value is non null (in the case of a n-dimensional array - * that value is another array, n-1 dimensional). - */ - } + /* + * In the current implementation it is not necessary to do it it recursively for + * multidimensional arrays since we don't model individual array elements, so + * from the point of view of the static analysis the field's array elements + * value is non null (in the case of a n-dimensional array that value is another + * array, n-1 dimensional). + */ } } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerPolicy.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerPolicy.java index 3112feafbd88..9afb8f6617d2 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerPolicy.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/SimulateClassInitializerPolicy.java @@ -156,7 +156,7 @@ protected AbstractPolicyScope createRootScope() { } @Override - protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope o, AnalysisMethod method) { + protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope o, AnalysisMethod caller, AnalysisMethod method) { var outer = (SimulateClassInitializerInlineScope) o; return new SimulateClassInitializerInlineScope(outer.accumulativeCounters, outer.inliningDepth + 1); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethodSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethodSupport.java index e56cc3e54846..86d1b3c0a2f5 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethodSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/FactoryMethodSupport.java @@ -56,6 +56,11 @@ public static FactoryMethodSupport singleton() { private final Map factoryMethods = new ConcurrentHashMap<>(); private final Map factoryThrowMethods = new ConcurrentHashMap<>(); + public static boolean isFactoryMethod(AnalysisMethod method) { + var javaClass = method.getDeclaringClass().getJavaClass(); + return javaClass == FactoryMethodHolder.class || javaClass == FactoryThrowMethodHolder.class; + } + public AnalysisMethod lookup(AnalysisMetaAccess aMetaAccess, AnalysisMethod aConstructor, boolean throwAllocatedObject) { VMError.guarantee(aConstructor.getDeclaringClass().isInstanceClass() && !aConstructor.getDeclaringClass().isAbstract(), "Must be a non-abstract instance class"); Map methods = throwAllocatedObject ? factoryThrowMethods : factoryMethods; diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyImpl.java index 8823bf9746ad..e8585328a059 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyImpl.java @@ -79,8 +79,8 @@ protected AbstractPolicyScope createRootScope() { } @Override - protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, AnalysisMethod method) { - return inliningUtils.createAccumulativeInlineScope((InlineBeforeAnalysisPolicyUtils.AccumulativeInlineScope) outer, method, (ignore) -> false); + protected AbstractPolicyScope openCalleeScope(AbstractPolicyScope outer, AnalysisMethod caller, AnalysisMethod method) { + return inliningUtils.createAccumulativeInlineScope((InlineBeforeAnalysisPolicyUtils.AccumulativeInlineScope) outer, caller, method, (ignore) -> false); } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java index 6e394f7c6b33..f1eb3374c105 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/InlineBeforeAnalysisPolicyUtils.java @@ -30,15 +30,18 @@ import org.graalvm.nativeimage.AnnotationAccess; +import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.phases.InlineBeforeAnalysisPolicy; import com.oracle.svm.core.Uninterruptible; import com.oracle.svm.core.heap.RestrictHeapAccess; import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.util.VMError; import com.oracle.svm.hosted.ReachabilityRegistrationNode; import com.oracle.svm.hosted.SVMHost; +import com.oracle.svm.hosted.code.FactoryMethodSupport; import com.oracle.svm.hosted.methodhandles.MethodHandleInvokerRenamingSubstitutionProcessor; import com.oracle.svm.util.ReflectionUtil; @@ -114,6 +117,12 @@ public static class Options { @Option(help = "Maximum number of methods inlined for method handle internals before static analysis")// public static final HostedOptionKey InlineBeforeAnalysisMethodHandleAllowedInlinings = new HostedOptionKey<>(10_000); + + @Option(help = "Maximum number of computation nodes for constructors inlined into factory methods before static analysis")// + public static final HostedOptionKey InlineBeforeAnalysisConstructorAllowedNodes = new HostedOptionKey<>(200); + + @Option(help = "Maximum number of invokes for constructors inlined into factory methods before static analysis")// + public static final HostedOptionKey InlineBeforeAnalysisConstructorAllowedInvokes = new HostedOptionKey<>(50); } /* Cached values of options, to avoid repeated option lookup. */ @@ -126,6 +135,10 @@ public static class Options { public final int optionMethodHandleAllowedDepth = Options.InlineBeforeAnalysisMethodHandleAllowedDepth.getValue(); public final int optionMethodHandleAllowedInlinings = Options.InlineBeforeAnalysisMethodHandleAllowedInlinings.getValue(); + public final boolean optionTrackNeverNullInstanceFields = PointstoOptions.TrackNeverNullInstanceFields.getValue(HostedOptionValues.singleton()); + public final int optionConstructorAllowedNodes = Options.InlineBeforeAnalysisConstructorAllowedNodes.getValue(); + public final int optionConstructorAllowedInvokes = Options.InlineBeforeAnalysisConstructorAllowedInvokes.getValue(); + @SuppressWarnings("unchecked") // private static final Class COMPILED_LAMBDA_FORM_ANNOTATION = // (Class) ReflectionUtil.lookupClass(false, "java.lang.invoke.LambdaForm$Compiled"); @@ -241,15 +254,17 @@ static final class AccumulativeCounters { int maxNodes; int maxInvokes; final boolean inMethodHandleIntrinsification; + final boolean inConstructorInlining; int numNodes; int numInvokes; int totalInlinedMethods; - private AccumulativeCounters(int maxNodes, int maxInvokes, boolean inMethodHandleIntrinsification) { + private AccumulativeCounters(int maxNodes, int maxInvokes, boolean inMethodHandleIntrinsification, boolean inConstructorInlining) { this.maxNodes = maxNodes; this.maxInvokes = maxInvokes; this.inMethodHandleIntrinsification = inMethodHandleIntrinsification; + this.inConstructorInlining = inConstructorInlining; } } @@ -258,7 +273,7 @@ private AccumulativeCounters(int maxNodes, int maxInvokes, boolean inMethodHandl * has exceeded a specified count, or an illegal node is inlined, then the process will be * aborted. */ - public AccumulativeInlineScope createAccumulativeInlineScope(AccumulativeInlineScope outer, AnalysisMethod method, NodePredicate invalidNodePredicate) { + public AccumulativeInlineScope createAccumulativeInlineScope(AccumulativeInlineScope outer, AnalysisMethod caller, AnalysisMethod method, NodePredicate invalidNodePredicate) { AccumulativeCounters accumulativeCounters; int depth; if (outer == null) { @@ -274,9 +289,13 @@ public AccumulativeInlineScope createAccumulativeInlineScope(AccumulativeInlineS * permit more types of nodes, but not recursively, i.e., not if we are already in a * method handle intrinsification context. */ - accumulativeCounters = new AccumulativeCounters(optionMethodHandleAllowedNodes, optionMethodHandleAllowedInvokes, true); + accumulativeCounters = new AccumulativeCounters(optionMethodHandleAllowedNodes, optionMethodHandleAllowedInvokes, true, false); + + } else if (optionTrackNeverNullInstanceFields && FactoryMethodSupport.isFactoryMethod(caller)) { + accumulativeCounters = new AccumulativeCounters(optionConstructorAllowedNodes, optionConstructorAllowedInvokes, false, true); + } else { - accumulativeCounters = new AccumulativeCounters(optionAllowedNodes, optionAllowedInvokes, false); + accumulativeCounters = new AccumulativeCounters(optionAllowedNodes, optionAllowedInvokes, false, false); } } else if (outer.accumulativeCounters.inMethodHandleIntrinsification && !inlineForMethodHandleIntrinsification(method)) { @@ -294,7 +313,11 @@ public AccumulativeInlineScope createAccumulativeInlineScope(AccumulativeInlineS * inlining root. */ depth = 1; - accumulativeCounters = new AccumulativeCounters(optionAllowedNodes, optionAllowedInvokes, false); + accumulativeCounters = new AccumulativeCounters(optionAllowedNodes, optionAllowedInvokes, false, false); + + } else if (outer.accumulativeCounters.inConstructorInlining && !method.isConstructor()) { + depth = 1; + accumulativeCounters = new AccumulativeCounters(optionAllowedNodes, optionAllowedInvokes, false, false); } else { /* Nested inlining (potentially during method handle intrinsification). */ @@ -326,8 +349,6 @@ public final class AccumulativeInlineScope extends InlineBeforeAnalysisPolicy.Ab public void commitCalleeScope(InlineBeforeAnalysisPolicy.AbstractPolicyScope callee) { AccumulativeInlineScope calleeScope = (AccumulativeInlineScope) callee; if (accumulativeCounters != calleeScope.accumulativeCounters) { - assert accumulativeCounters.inMethodHandleIntrinsification != calleeScope.accumulativeCounters.inMethodHandleIntrinsification; - // Expand limits to hold the method handle intrinsification, but not more. accumulativeCounters.maxNodes += calleeScope.numNodes; accumulativeCounters.maxInvokes += calleeScope.numInvokes; @@ -345,8 +366,6 @@ public void abortCalleeScope(InlineBeforeAnalysisPolicy.AbstractPolicyScope call if (accumulativeCounters == calleeScope.accumulativeCounters) { accumulativeCounters.numNodes -= calleeScope.numNodes; accumulativeCounters.numInvokes -= calleeScope.numInvokes; - } else { - assert accumulativeCounters.inMethodHandleIntrinsification != calleeScope.accumulativeCounters.inMethodHandleIntrinsification; } } @@ -457,7 +476,7 @@ public boolean processNode(AnalysisMetaAccess metaAccess, AnalysisMethod method, accumulativeCounters.numNodes++; // With method handle intrinsification we permit all node types to become more effective - return allow || accumulativeCounters.inMethodHandleIntrinsification; + return allow || accumulativeCounters.inMethodHandleIntrinsification || accumulativeCounters.inConstructorInlining; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java index 34208b81ebb4..cb6743c28dc7 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java @@ -975,7 +975,15 @@ private void registerTypes(Collection> types) { * Exception proxies are stored as-is in the image heap */ if (ExceptionProxy.class.isAssignableFrom(type)) { + /* + * The image heap scanning does not see the actual instances, so we need to be + * conservative and assume fields can have any value. + */ analysisType.registerAsInstantiated("Is used by annotation of element registered for reflection."); + for (var f : analysisType.getInstanceFields(true)) { + var aField = (AnalysisField) f; + universe.getBigbang().injectFieldTypes(aField, List.of(aField.getType()), true); + } } } }