Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* <ul>
* <li>In <b>open type world</b> this means that all the subtypes of this type are known and
* this type cannot be extended outside the observable universe.</li>
* <li>In <b>closed type world</b> all types are considered closed.</li>
* </ul>
*
* 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public TypeFlow<AnalysisType> copy(PointsToAnalysis bb, MethodFlowsGraph methodF
}

@Override
public boolean canSaturate() {
public boolean canSaturate(PointsToAnalysis bb) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,23 @@ public TypeFlow<AnalysisType> 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
Expand All @@ -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.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,17 +65,23 @@ public TypeFlow<AnalysisField> 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. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,32 +111,55 @@ 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);
}
}

@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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
}
}
}
}
}
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading