From 74fea34169cf32bf705771edd6a3f72066a4f209 Mon Sep 17 00:00:00 2001 From: Codrut Stancu Date: Thu, 24 Oct 2024 15:50:41 +0200 Subject: [PATCH] Adapt saturation to open world. --- .../graal/pointsto/PointsToAnalysis.java | 21 +++++++++ .../flow/AllInstantiatedTypeFlow.java | 2 +- .../flow/AnyPrimitiveSourceTypeFlow.java | 2 +- .../pointsto/flow/ArrayElementsTypeFlow.java | 22 ++++++---- .../graal/pointsto/flow/CloneTypeFlow.java | 17 +++++--- .../flow/DynamicNewInstanceTypeFlow.java | 15 ++++--- .../pointsto/flow/FieldFilterTypeFlow.java | 30 ++++++++++--- .../graal/pointsto/flow/FieldTypeFlow.java | 20 ++++++--- .../graal/pointsto/flow/FilterTypeFlow.java | 43 ++++++++++++++----- .../pointsto/flow/FormalReceiverTypeFlow.java | 17 ++++++-- .../graal/pointsto/flow/InvokeTypeFlow.java | 21 ++++++++- .../pointsto/flow/LoadFieldTypeFlow.java | 4 ++ .../graal/pointsto/flow/MethodFlowsGraph.java | 4 +- .../pointsto/flow/OffsetLoadTypeFlow.java | 4 ++ .../pointsto/flow/OffsetStoreTypeFlow.java | 10 ++++- .../graal/pointsto/flow/SourceTypeFlow.java | 2 +- .../pointsto/flow/StoreFieldTypeFlow.java | 4 ++ .../oracle/graal/pointsto/flow/TypeFlow.java | 12 +++--- .../pointsto/results/StrengthenGraphs.java | 13 +++--- 19 files changed, 198 insertions(+), 65 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java index 938a2d448413..2db1fbf5e1ae 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java @@ -152,6 +152,27 @@ public PointsToAnalysis(OptionValues options, AnalysisUniverse universe, HostVM executor.init(timing); } + /** + * Returns true if the type's hierarchy is complete in the observable universe. + * + * + * This method is conservative, it returns false in cases where we are not sure, and further + * refining when a type is closed will improve analysis. For example GR-59311 will also define + * when a sealed type can be treated as a closed type. + */ + public boolean isClosed(AnalysisType type) { + if (hostVM.isClosedTypeWorld()) { + /* In a closed type world all subtypes known. */ + return true; + } + /* Array and leaf types are by definition closed. */ + return type.isArray() || type.isLeaf(); + } + @Override protected CompletionExecutor.Timing getTiming() { return timing; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AllInstantiatedTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AllInstantiatedTypeFlow.java index c46288794c73..07ddb0bdf169 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AllInstantiatedTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AllInstantiatedTypeFlow.java @@ -48,7 +48,7 @@ public TypeFlow copy(PointsToAnalysis bb, MethodFlowsGraph methodF } @Override - public boolean canSaturate() { + public boolean canSaturate(PointsToAnalysis bb) { return false; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnyPrimitiveSourceTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnyPrimitiveSourceTypeFlow.java index 61b0af69e134..bfb65cc9a9bb 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnyPrimitiveSourceTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnyPrimitiveSourceTypeFlow.java @@ -57,7 +57,7 @@ private boolean isLocal() { } @Override - public boolean canSaturate() { + public boolean canSaturate(PointsToAnalysis bb) { /* * AnyPrimitiveSourceTypeFlow can be used as a global flow that should always propagate * values. The global version can be identified be having source == null, and it should diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/ArrayElementsTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/ArrayElementsTypeFlow.java index 1ab883d65253..0b8e3e918fa1 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/ArrayElementsTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/ArrayElementsTypeFlow.java @@ -50,17 +50,23 @@ public TypeFlow copy(PointsToAnalysis bb, MethodFlowsGraph methodF } @Override - public boolean canSaturate() { - return false; + public boolean canSaturate(PointsToAnalysis bb) { + /* Arrays flows with a closed component type don't saturate, they track all input types. */ + return !bb.isClosed(declaredType); } @Override protected void onInputSaturated(PointsToAnalysis bb, TypeFlow input) { - /* - * When an array store is saturated conservatively assume that the array can contain any - * subtype of its declared type. - */ - getDeclaredType().getTypeFlow(bb, true).addUse(bb, this); + if (bb.isClosed(declaredType)) { + /* + * When an array store is saturated conservatively assume that the array can contain any + * subtype of its declared type, i.e., of its component type. + */ + declaredType.getTypeFlow(bb, true).addUse(bb, this); + } else { + /* Propagate the saturation stamp through the array flow. */ + super.onInputSaturated(bb, input); + } } @Override @@ -71,7 +77,7 @@ public TypeState filter(PointsToAnalysis bb, TypeState update) { } else { /* * Filter out the objects not compatible with the declared type, i.e., those objects - * whose type cannot be converted to the component type of the this array by assignment + * whose type cannot be converted to the component type of this array by assignment * conversion. At runtime that will throw an ArrayStoreException but during the analysis * we can detect such cases and filter out the incompatible types. */ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/CloneTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/CloneTypeFlow.java index 2384a11c7c6c..848ea978d5d5 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/CloneTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/CloneTypeFlow.java @@ -90,12 +90,17 @@ public void update(PointsToAnalysis bb) { @Override public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { - if (!isSaturated()) { - /* - * When the input flow saturates start observing the flow of the declared type, unless - * the clone is already saturated. - */ - replaceObservedWith(bb, declaredType); + if (bb.isClosed(declaredType)) { + if (!isSaturated()) { + /* + * When the input flow saturates start observing the flow of the declared type, + * unless the clone is already saturated. + */ + replaceObservedWith(bb, declaredType); + } + } else { + /* Propagate the saturation stamp through the clone flow. */ + onSaturated(bb); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/DynamicNewInstanceTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/DynamicNewInstanceTypeFlow.java index 63df237c007b..319a8c6549fe 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/DynamicNewInstanceTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/DynamicNewInstanceTypeFlow.java @@ -96,14 +96,19 @@ public void setObserved(TypeFlow declaredTypeFlow) { @Override public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { - /* When the new-type flow saturates start observing the flow of the declared type. */ - replaceObservedWith(bb, declaredType); + if (bb.isClosed(declaredType)) { + /* When the new-type flow saturates start observing the flow of the declared type. */ + replaceObservedWith(bb, declaredType); + } else { + /* Propagate the saturation stamp through the dynamic new instance flow. */ + onSaturated(bb); + } } @Override - public boolean canSaturate() { - /* The dynamic new instance tracks all of its input types. */ - return false; + public boolean canSaturate(PointsToAnalysis bb) { + /* Dynamic new instance of closed types doesn't saturate, it tracks all input types. */ + return !bb.isClosed(declaredType); } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldFilterTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldFilterTypeFlow.java index 1b9b73008098..60c34f601a43 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldFilterTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldFilterTypeFlow.java @@ -75,21 +75,39 @@ public void addPredicated(PointsToAnalysis bb, TypeFlow predicatedFlow) { @Override protected void onInputSaturated(PointsToAnalysis bb, TypeFlow input) { - if (!setSaturated()) { - return; + if (bb.isClosed(declaredType)) { + /* + * We stop saturation propagation to the field flow and instead use the upper limit + * type, i.e., the field declared type, as a safe approximation, but only if the type is + * closed. For open types we simply propagate the saturation. + */ + if (!setSaturated()) { + return; + } + /* Swap out this flow with its declared type flow. */ + swapOut(bb, declaredType.getTypeFlow(bb, true)); + } else { + /* Propagate the saturation stamp through the filter flow. */ + super.onInputSaturated(bb, input); } - /* Swap out this flow with its declared type flow. */ - swapOut(bb, declaredType.getTypeFlow(bb, true)); } @Override protected void notifyUseOfSaturation(PointsToAnalysis bb, TypeFlow use) { - swapAtUse(bb, declaredType.getTypeFlow(bb, true), use); + if (bb.isClosed(declaredType)) { + swapAtUse(bb, declaredType.getTypeFlow(bb, true), use); + } else { + super.notifyUseOfSaturation(bb, use); + } } @Override protected void notifyObserverOfSaturation(PointsToAnalysis bb, TypeFlow observer) { - swapAtObserver(bb, declaredType.getTypeFlow(bb, true), observer); + if (bb.isClosed(declaredType)) { + swapAtObserver(bb, declaredType.getTypeFlow(bb, true), observer); + } else { + super.notifyObserverOfSaturation(bb, observer); + } } @Override 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 f56aaa40790a..387ed2754ad6 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 @@ -65,17 +65,23 @@ public TypeFlow copy(PointsToAnalysis bb, MethodFlowsGraph method } @Override - public boolean canSaturate() { - return false; + public boolean canSaturate(PointsToAnalysis bb) { + /* Fields declared with a closed type don't saturate, they track all input types. */ + return !bb.isClosed(declaredType); } @Override protected void onInputSaturated(PointsToAnalysis bb, TypeFlow input) { - /* - * When a field store is saturated conservatively assume that the field state can contain - * any subtype of its declared type or any primitive value for primitive fields. - */ - getDeclaredType().getTypeFlow(bb, true).addUse(bb, this); + if (bb.isClosed(declaredType)) { + /* + * When a field store is saturated conservatively assume that the field state can + * contain any subtype of its declared type or any primitive value for primitive fields. + */ + declaredType.getTypeFlow(bb, true).addUse(bb, this); + } else { + /* Propagate saturation stamp through the field flow. */ + super.onInputSaturated(bb, input); + } } /** The filter flow is used for unsafe writes and initialized on demand. */ diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FilterTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FilterTypeFlow.java index 76ae822a23dc..0517788e0e90 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FilterTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FilterTypeFlow.java @@ -111,23 +111,42 @@ public void addPredicated(PointsToAnalysis bb, TypeFlow predicatedFlow) { @Override protected void onInputSaturated(PointsToAnalysis bb, TypeFlow input) { - if (isAssignable) { - if (!setSaturated()) { - return; + if (bb.isClosed(filterType)) { + if (isAssignable) { + /* + * If the filter type is closed stop saturation propagation to dependent flows and + * instead use the upper limit type, i.e., the filter type, as a safe approximation. + */ + if (!setSaturated()) { + return; + } + /* Swap this flow out at uses/observers/predicated flows with its filter type. */ + swapOut(bb, filterType.getTypeFlow(bb, includeNull)); + } else { + /* + * For the non-assignable branch simply propagate the saturation stamp through the + * filter flow. + */ + super.onInputSaturated(bb, input); } + } else { /* - * Swap this flow out at its uses/observers/predicated flows with its filter type flow. + * /* For open types simply propagate the saturation stamp through the filter flow, just + * like for the non-assignable branch. GR-59312 will preserve and propagate the upper + * limit type also for open types. */ - swapOut(bb, filterType.getTypeFlow(bb, includeNull)); - } else { super.onInputSaturated(bb, input); } } @Override protected void notifyUseOfSaturation(PointsToAnalysis bb, TypeFlow use) { - if (isAssignable) { - swapAtUse(bb, filterType.getTypeFlow(bb, includeNull), use); + if (bb.isClosed(filterType)) { + if (isAssignable) { + swapAtUse(bb, filterType.getTypeFlow(bb, includeNull), use); + } else { + super.notifyUseOfSaturation(bb, use); + } } else { super.notifyUseOfSaturation(bb, use); } @@ -135,8 +154,12 @@ protected void notifyUseOfSaturation(PointsToAnalysis bb, TypeFlow use) { @Override protected void notifyObserverOfSaturation(PointsToAnalysis bb, TypeFlow observer) { - if (isAssignable) { - swapAtObserver(bb, filterType.getTypeFlow(bb, includeNull), observer); + if (bb.isClosed(filterType)) { + if (isAssignable) { + swapAtObserver(bb, filterType.getTypeFlow(bb, includeNull), observer); + } else { + super.notifyObserverOfSaturation(bb, observer); + } } else { super.notifyObserverOfSaturation(bb, observer); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalReceiverTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalReceiverTypeFlow.java index 252a841f8f62..aa2e8550a54d 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalReceiverTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalReceiverTypeFlow.java @@ -75,10 +75,21 @@ public boolean addState(PointsToAnalysis bb, TypeState add) { @Override protected void onInputSaturated(PointsToAnalysis bb, TypeFlow input) { /* - * The saturation of the actual receiver doesn't result in the saturation of the formal - * receiver; some callees, depending how low in the type hierarchies they are, may only see - * a number of types smaller than the saturation cut-off limit. + * Note that in open world analysis the formal receiver of all callees linked to a context + * insensitive invoke will be notified of saturation. + * + * For a formal receiver with a closed declared type (which corresponds to the declaring + * class of its method) the saturation of the actual receiver doesn't result in the + * saturation of the formal receiver; some callees, depending on how low in the type + * hierarchies they are, may only see a number of types smaller than the saturation cut-off + * limit. + * + * If the declared type is open we cannot make any assumptions and simply propagate the + * saturation stamp. */ + if (!bb.isClosed(declaredType)) { + super.onInputSaturated(bb, input); + } } public boolean addReceiverState(PointsToAnalysis bb, TypeState add) { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/InvokeTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/InvokeTypeFlow.java index ece8f7c168fe..eb20ac2eb757 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/InvokeTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/InvokeTypeFlow.java @@ -35,6 +35,7 @@ import com.oracle.graal.pointsto.meta.InvokeInfo; import com.oracle.graal.pointsto.meta.PointsToAnalysisMethod; import com.oracle.graal.pointsto.typestate.TypeState; +import com.oracle.graal.pointsto.util.AnalysisError; import com.oracle.svm.common.meta.MultiMethod; import com.oracle.svm.common.meta.MultiMethod.MultiMethodKey; @@ -259,7 +260,11 @@ protected void linkCallee(PointsToAnalysis bb, boolean isStatic, MethodFlowsGrap // (formalParam, callerContext) -> (actualParam, calleeContext) // Note: the callerContext is an implicit property of the current InvokeTypeFlow // clone - actualParam.addUse(bb, formalParam); + if (actualParam.addUse(bb, formalParam)) { + if (i == 0 && !isStatic) { + maybeSaturateFormalReceiver(bb, actualParam, formalParam); + } + } } } } @@ -275,6 +280,20 @@ protected void linkCallee(PointsToAnalysis bb, boolean isStatic, MethodFlowsGrap } } + /* + * In the open type world we always propagate saturation of the actual receiver to the formal + * receiver, even after the saturated invoke was replaced by the context insensitive variant. + * So, if we are linking the receiver of a context insensitive invoke we immediately notify the + * formal receiver of saturation. + */ + private void maybeSaturateFormalReceiver(PointsToAnalysis bb, TypeFlow actualParam, TypeFlow formalParam) { + if (!bb.getHostVM().isClosedTypeWorld() && this.isContextInsensitive()) { + AnalysisError.guarantee(actualParam instanceof AllInstantiatedTypeFlow && formalParam instanceof FormalReceiverTypeFlow); + /* The actualParam is not technically saturated, but we treat it as such. */ + actualParam.notifyUseOfSaturation(bb, formalParam); + } + } + public void linkReturn(PointsToAnalysis bb, boolean isStatic, MethodFlowsGraphInfo calleeFlows) { /* * If actualReturn is null, then there is no linking necessary. Later, if a typeflow is diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java index c5dd681f3236..f08ffa1a42c5 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java @@ -146,6 +146,10 @@ public void onObservedUpdate(PointsToAnalysis bb) { @Override public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { + /* + * Nothing needs to change for open world analysis: we want to link all field flows when + * the receiver saturates. + */ if (!isSaturated()) { /* * When the receiver flow saturates start observing the flow of the field declaring diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodFlowsGraph.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodFlowsGraph.java index e3e3819e3f2f..9d98ded258ad 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodFlowsGraph.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodFlowsGraph.java @@ -449,7 +449,7 @@ void updateInternalState(GraphKind newGraphKind) { public void saturateAllParameters(PointsToAnalysis bb) { AnalysisError.guarantee(bb.isBaseLayerAnalysisEnabled()); for (TypeFlow parameter : getParameters()) { - if (parameter != null && parameter.canSaturate()) { + if (parameter != null && parameter.canSaturate(bb)) { parameter.enableFlow(bb); parameter.onSaturated(bb); } @@ -466,7 +466,7 @@ public void saturateAllParameters(PointsToAnalysis bb) { */ if (miscEntryFlows != null) { for (TypeFlow miscEntryFlow : miscEntryFlows) { - if (miscEntryFlow != null && miscEntryFlow.canSaturate()) { + if (miscEntryFlow != null && miscEntryFlow.canSaturate(bb)) { miscEntryFlow.enableFlow(bb); miscEntryFlow.onSaturated(bb); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetLoadTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetLoadTypeFlow.java index ca3c6d4713c6..59f637426402 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetLoadTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetLoadTypeFlow.java @@ -69,6 +69,10 @@ public void setObserved(TypeFlow newObjectFlow) { @Override public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { + /* + * Nothing needs to change for open world analysis: we want to link all indexed/unsafe flows + * when the receiver saturates. + */ if (!isSaturated()) { /* * When the receiver flow saturates start observing the flow of the object type, unless diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetStoreTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetStoreTypeFlow.java index b01f8da156ec..08d5197ba33b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetStoreTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetStoreTypeFlow.java @@ -145,11 +145,15 @@ public void onObservedUpdate(PointsToAnalysis bb) { @Override public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { + /* + * Nothing needs to change for open world analysis: we want to link all indexed store + * flows when the receiver saturates. + */ /* * When receiver flow saturates swap in the saturated indexed store type flow. When the * store itself saturates it propagates the saturation state to the uses/observers and * unlinks them, but it still observes the receiver state to notify no-yet-reachable - * field flows of saturation. + * indexed store flows of saturation. */ /* Deregister the store as an observer of the receiver. */ @@ -270,6 +274,10 @@ public void onObservedUpdate(PointsToAnalysis bb) { @Override public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { + /* + * Nothing needs to change for open world analysis: we want to link all unsafe store + * flows when the receiver saturates. + */ /* * When receiver flow saturates swap in the saturated unsafe store type flow. When the * store itself saturates it propagates the saturation state to the uses/observers and diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/SourceTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/SourceTypeFlow.java index 8a20c867cd8b..f700b1856ad0 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/SourceTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/SourceTypeFlow.java @@ -94,7 +94,7 @@ protected void onSaturated(PointsToAnalysis bb) { } @Override - public boolean canSaturate() { + public boolean canSaturate(PointsToAnalysis bb) { return false; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java index ed4a895517e6..dbd68be19dde 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java @@ -175,6 +175,10 @@ public void onObservedUpdate(PointsToAnalysis bb) { @Override public void onObservedSaturated(PointsToAnalysis bb, TypeFlow observed) { + /* + * Nothing needs to change for open world analysis: we want to link all field flows when + * the receiver saturates. + */ /* * When receiver flow saturates swap in the saturated store type flow. When the store * itself saturates it propagates the saturation state to the uses/observers and unlinks diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/TypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/TypeFlow.java index 41981c8bcee8..28efc11db52b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/TypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/TypeFlow.java @@ -459,7 +459,7 @@ public boolean isSaturated() { * Can this type flow saturate? By default all type flows can saturate, with the exception of a * few ones that need to track all their types, e.g., AllInstantiated, AllSynchronized, etc. */ - public boolean canSaturate() { + public boolean canSaturate(@SuppressWarnings("unused") PointsToAnalysis bb) { return true; } @@ -840,7 +840,7 @@ boolean checkSaturated(PointsToAnalysis bb, TypeState typeState) { /* If the type flow saturation optimization is disabled just return false. */ return false; } - if (!canSaturate()) { + if (!canSaturate(bb)) { /* This type flow needs to track all its individual types. */ return false; } @@ -853,7 +853,7 @@ boolean checkSaturated(PointsToAnalysis bb, TypeState typeState) { /** Called when this type flow becomes saturated. */ protected void onSaturated(PointsToAnalysis bb) { assert bb.analysisPolicy().removeSaturatedTypeFlows() : "The type flow saturation optimization is disabled."; - assert canSaturate() : "This type flow cannot saturate."; + assert canSaturate(bb) : "This type flow cannot saturate."; assert isFlowEnabled() : "A flow cannot saturate before it is enabled."; /* * Array type flow aliasing needs to be enabled for the type flow saturation optimization to @@ -946,14 +946,14 @@ private void markInputSaturated(PointsToAnalysis bb, TypeFlow input) { */ protected void onInputSaturated(PointsToAnalysis bb, @SuppressWarnings("unused") TypeFlow input) { assert bb.analysisPolicy().removeSaturatedTypeFlows() : "The type flow saturation optimization is disabled."; - if (!canSaturate()) { + if (!canSaturate(bb)) { /* This type flow needs to track all its individual types. */ return; } /* - * By default when a type flow is notified that one of its inputs is saturated it will just - * pass this information to its uses and observers and unlink them. Subclases should + * By default, when a type flow is notified that one of its inputs is saturated it will just + * pass this information to its uses and observers and unlink them. Subclasses should * override this method and provide custom behavior. */ onSaturated(bb); 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 8fa695ec560a..54a42d47a1f2 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 @@ -215,7 +215,8 @@ private static void reportNeverNullInstanceFields(BigBang bb) { int canBeNull = 0; for (var field : bb.getUniverse().getFields()) { if (!field.isStatic() && field.isReachable() && field.getType().getStorageKind() == JavaKind.Object) { - if (field.getSinkFlow().getState().canBeNull()) { + /* If the field flow is saturated we must assume it can be null. */ + if (field.getSinkFlow().isSaturated() || field.getSinkFlow().getState().canBeNull()) { canBeNull++; } else { neverNull++; @@ -416,8 +417,7 @@ public void simplify(Node n, SimplifierTool tool) { if (simplifyDelegate(n, tool)) { // handled elsewhere - } else if (n instanceof ParameterNode && parameterFlows != null) { - ParameterNode node = (ParameterNode) n; + } else if (n instanceof ParameterNode node && parameterFlows != null) { StartNode anchorPoint = graph.start(); Object newStampOrConstant = strengthenStampFromTypeFlow(node, parameterFlows[node.index()], anchorPoint, tool); updateStampUsingPiNode(node, newStampOrConstant, anchorPoint, tool); @@ -883,7 +883,7 @@ private Object strengthenStampFromTypeFlow(ValueNode node, TypeFlow nodeFlow, return null; } if (unreachableValues.contains(node)) { - // This node has already been made unreachable - no further action is needed + /* This node has already been made unreachable - no further action is needed. */ return null; } /* @@ -914,9 +914,8 @@ private Object strengthenStampFromTypeFlow(ValueNode node, TypeFlow nodeFlow, /* * Find all types of the TypeState that are compatible with the current stamp. Since - * stamps are propagated around immediately by the Canonicalizer, and the static - * analysis does not track primitive types at all, it is possible and allowed that the - * stamp is already more precise than the static analysis results. + * stamps are propagated around immediately by the Canonicalizer it is possible and + * allowed that the stamp is already more precise than the static analysis results. */ List typeStateTypes = new ArrayList<>(nodeTypeState.typesCount()); for (AnalysisType typeStateType : nodeTypeState.types(bb)) {