Skip to content

Commit 73e8faa

Browse files
committed
[GR-58118] Adapt saturation to open world.
PullRequest: graal/19082
2 parents 9060dc3 + 74fea34 commit 73e8faa

19 files changed

+198
-65
lines changed

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/PointsToAnalysis.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,27 @@ public PointsToAnalysis(OptionValues options, AnalysisUniverse universe, HostVM
152152
executor.init(timing);
153153
}
154154

155+
/**
156+
* Returns true if the type's hierarchy is complete in the observable universe.
157+
* <ul>
158+
* <li>In <b>open type world</b> this means that all the subtypes of this type are known and
159+
* this type cannot be extended outside the observable universe.</li>
160+
* <li>In <b>closed type world</b> all types are considered closed.</li>
161+
* </ul>
162+
*
163+
* This method is conservative, it returns false in cases where we are not sure, and further
164+
* refining when a type is closed will improve analysis. For example GR-59311 will also define
165+
* when a sealed type can be treated as a closed type.
166+
*/
167+
public boolean isClosed(AnalysisType type) {
168+
if (hostVM.isClosedTypeWorld()) {
169+
/* In a closed type world all subtypes known. */
170+
return true;
171+
}
172+
/* Array and leaf types are by definition closed. */
173+
return type.isArray() || type.isLeaf();
174+
}
175+
155176
@Override
156177
protected CompletionExecutor.Timing getTiming() {
157178
return timing;

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AllInstantiatedTypeFlow.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public TypeFlow<AnalysisType> copy(PointsToAnalysis bb, MethodFlowsGraph methodF
4848
}
4949

5050
@Override
51-
public boolean canSaturate() {
51+
public boolean canSaturate(PointsToAnalysis bb) {
5252
return false;
5353
}
5454

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/AnyPrimitiveSourceTypeFlow.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ private boolean isLocal() {
5757
}
5858

5959
@Override
60-
public boolean canSaturate() {
60+
public boolean canSaturate(PointsToAnalysis bb) {
6161
/*
6262
* AnyPrimitiveSourceTypeFlow can be used as a global flow that should always propagate
6363
* values. The global version can be identified be having source == null, and it should

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/ArrayElementsTypeFlow.java

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,23 @@ public TypeFlow<AnalysisType> copy(PointsToAnalysis bb, MethodFlowsGraph methodF
5050
}
5151

5252
@Override
53-
public boolean canSaturate() {
54-
return false;
53+
public boolean canSaturate(PointsToAnalysis bb) {
54+
/* Arrays flows with a closed component type don't saturate, they track all input types. */
55+
return !bb.isClosed(declaredType);
5556
}
5657

5758
@Override
5859
protected void onInputSaturated(PointsToAnalysis bb, TypeFlow<?> input) {
59-
/*
60-
* When an array store is saturated conservatively assume that the array can contain any
61-
* subtype of its declared type.
62-
*/
63-
getDeclaredType().getTypeFlow(bb, true).addUse(bb, this);
60+
if (bb.isClosed(declaredType)) {
61+
/*
62+
* When an array store is saturated conservatively assume that the array can contain any
63+
* subtype of its declared type, i.e., of its component type.
64+
*/
65+
declaredType.getTypeFlow(bb, true).addUse(bb, this);
66+
} else {
67+
/* Propagate the saturation stamp through the array flow. */
68+
super.onInputSaturated(bb, input);
69+
}
6470
}
6571

6672
@Override
@@ -71,7 +77,7 @@ public TypeState filter(PointsToAnalysis bb, TypeState update) {
7177
} else {
7278
/*
7379
* Filter out the objects not compatible with the declared type, i.e., those objects
74-
* whose type cannot be converted to the component type of the this array by assignment
80+
* whose type cannot be converted to the component type of this array by assignment
7581
* conversion. At runtime that will throw an ArrayStoreException but during the analysis
7682
* we can detect such cases and filter out the incompatible types.
7783
*/

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/CloneTypeFlow.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,17 @@ public void update(PointsToAnalysis bb) {
100100

101101
@Override
102102
public void onObservedSaturated(PointsToAnalysis bb, TypeFlow<?> observed) {
103-
if (!isSaturated()) {
104-
/*
105-
* When the input flow saturates start observing the flow of the declared type, unless
106-
* the clone is already saturated.
107-
*/
108-
replaceObservedWith(bb, declaredType);
103+
if (bb.isClosed(declaredType)) {
104+
if (!isSaturated()) {
105+
/*
106+
* When the input flow saturates start observing the flow of the declared type,
107+
* unless the clone is already saturated.
108+
*/
109+
replaceObservedWith(bb, declaredType);
110+
}
111+
} else {
112+
/* Propagate the saturation stamp through the clone flow. */
113+
onSaturated(bb);
109114
}
110115
}
111116

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/DynamicNewInstanceTypeFlow.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,19 @@ public void setObserved(TypeFlow<?> declaredTypeFlow) {
106106

107107
@Override
108108
public void onObservedSaturated(PointsToAnalysis bb, TypeFlow<?> observed) {
109-
/* When the new-type flow saturates start observing the flow of the declared type. */
110-
replaceObservedWith(bb, declaredType);
109+
if (bb.isClosed(declaredType)) {
110+
/* When the new-type flow saturates start observing the flow of the declared type. */
111+
replaceObservedWith(bb, declaredType);
112+
} else {
113+
/* Propagate the saturation stamp through the dynamic new instance flow. */
114+
onSaturated(bb);
115+
}
111116
}
112117

113118
@Override
114-
public boolean canSaturate() {
115-
/* The dynamic new instance tracks all of its input types. */
116-
return false;
119+
public boolean canSaturate(PointsToAnalysis bb) {
120+
/* Dynamic new instance of closed types doesn't saturate, it tracks all input types. */
121+
return !bb.isClosed(declaredType);
117122
}
118123

119124
@Override

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldFilterTypeFlow.java

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,21 +75,39 @@ public void addPredicated(PointsToAnalysis bb, TypeFlow<?> predicatedFlow) {
7575

7676
@Override
7777
protected void onInputSaturated(PointsToAnalysis bb, TypeFlow<?> input) {
78-
if (!setSaturated()) {
79-
return;
78+
if (bb.isClosed(declaredType)) {
79+
/*
80+
* We stop saturation propagation to the field flow and instead use the upper limit
81+
* type, i.e., the field declared type, as a safe approximation, but only if the type is
82+
* closed. For open types we simply propagate the saturation.
83+
*/
84+
if (!setSaturated()) {
85+
return;
86+
}
87+
/* Swap out this flow with its declared type flow. */
88+
swapOut(bb, declaredType.getTypeFlow(bb, true));
89+
} else {
90+
/* Propagate the saturation stamp through the filter flow. */
91+
super.onInputSaturated(bb, input);
8092
}
81-
/* Swap out this flow with its declared type flow. */
82-
swapOut(bb, declaredType.getTypeFlow(bb, true));
8393
}
8494

8595
@Override
8696
protected void notifyUseOfSaturation(PointsToAnalysis bb, TypeFlow<?> use) {
87-
swapAtUse(bb, declaredType.getTypeFlow(bb, true), use);
97+
if (bb.isClosed(declaredType)) {
98+
swapAtUse(bb, declaredType.getTypeFlow(bb, true), use);
99+
} else {
100+
super.notifyUseOfSaturation(bb, use);
101+
}
88102
}
89103

90104
@Override
91105
protected void notifyObserverOfSaturation(PointsToAnalysis bb, TypeFlow<?> observer) {
92-
swapAtObserver(bb, declaredType.getTypeFlow(bb, true), observer);
106+
if (bb.isClosed(declaredType)) {
107+
swapAtObserver(bb, declaredType.getTypeFlow(bb, true), observer);
108+
} else {
109+
super.notifyObserverOfSaturation(bb, observer);
110+
}
93111
}
94112

95113
@Override

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FieldTypeFlow.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,23 @@ public TypeFlow<AnalysisField> copy(PointsToAnalysis bb, MethodFlowsGraph method
6565
}
6666

6767
@Override
68-
public boolean canSaturate() {
69-
return false;
68+
public boolean canSaturate(PointsToAnalysis bb) {
69+
/* Fields declared with a closed type don't saturate, they track all input types. */
70+
return !bb.isClosed(declaredType);
7071
}
7172

7273
@Override
7374
protected void onInputSaturated(PointsToAnalysis bb, TypeFlow<?> input) {
74-
/*
75-
* When a field store is saturated conservatively assume that the field state can contain
76-
* any subtype of its declared type or any primitive value for primitive fields.
77-
*/
78-
getDeclaredType().getTypeFlow(bb, true).addUse(bb, this);
75+
if (bb.isClosed(declaredType)) {
76+
/*
77+
* When a field store is saturated conservatively assume that the field state can
78+
* contain any subtype of its declared type or any primitive value for primitive fields.
79+
*/
80+
declaredType.getTypeFlow(bb, true).addUse(bb, this);
81+
} else {
82+
/* Propagate saturation stamp through the field flow. */
83+
super.onInputSaturated(bb, input);
84+
}
7985
}
8086

8187
/** The filter flow is used for unsafe writes and initialized on demand. */

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FilterTypeFlow.java

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -111,32 +111,55 @@ public void addPredicated(PointsToAnalysis bb, TypeFlow<?> predicatedFlow) {
111111

112112
@Override
113113
protected void onInputSaturated(PointsToAnalysis bb, TypeFlow<?> input) {
114-
if (isAssignable) {
115-
if (!setSaturated()) {
116-
return;
114+
if (bb.isClosed(filterType)) {
115+
if (isAssignable) {
116+
/*
117+
* If the filter type is closed stop saturation propagation to dependent flows and
118+
* instead use the upper limit type, i.e., the filter type, as a safe approximation.
119+
*/
120+
if (!setSaturated()) {
121+
return;
122+
}
123+
/* Swap this flow out at uses/observers/predicated flows with its filter type. */
124+
swapOut(bb, filterType.getTypeFlow(bb, includeNull));
125+
} else {
126+
/*
127+
* For the non-assignable branch simply propagate the saturation stamp through the
128+
* filter flow.
129+
*/
130+
super.onInputSaturated(bb, input);
117131
}
132+
} else {
118133
/*
119-
* Swap this flow out at its uses/observers/predicated flows with its filter type flow.
134+
* /* For open types simply propagate the saturation stamp through the filter flow, just
135+
* like for the non-assignable branch. GR-59312 will preserve and propagate the upper
136+
* limit type also for open types.
120137
*/
121-
swapOut(bb, filterType.getTypeFlow(bb, includeNull));
122-
} else {
123138
super.onInputSaturated(bb, input);
124139
}
125140
}
126141

127142
@Override
128143
protected void notifyUseOfSaturation(PointsToAnalysis bb, TypeFlow<?> use) {
129-
if (isAssignable) {
130-
swapAtUse(bb, filterType.getTypeFlow(bb, includeNull), use);
144+
if (bb.isClosed(filterType)) {
145+
if (isAssignable) {
146+
swapAtUse(bb, filterType.getTypeFlow(bb, includeNull), use);
147+
} else {
148+
super.notifyUseOfSaturation(bb, use);
149+
}
131150
} else {
132151
super.notifyUseOfSaturation(bb, use);
133152
}
134153
}
135154

136155
@Override
137156
protected void notifyObserverOfSaturation(PointsToAnalysis bb, TypeFlow<?> observer) {
138-
if (isAssignable) {
139-
swapAtObserver(bb, filterType.getTypeFlow(bb, includeNull), observer);
157+
if (bb.isClosed(filterType)) {
158+
if (isAssignable) {
159+
swapAtObserver(bb, filterType.getTypeFlow(bb, includeNull), observer);
160+
} else {
161+
super.notifyObserverOfSaturation(bb, observer);
162+
}
140163
} else {
141164
super.notifyObserverOfSaturation(bb, observer);
142165
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalReceiverTypeFlow.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,21 @@ public boolean addState(PointsToAnalysis bb, TypeState add) {
7575
@Override
7676
protected void onInputSaturated(PointsToAnalysis bb, TypeFlow<?> input) {
7777
/*
78-
* The saturation of the actual receiver doesn't result in the saturation of the formal
79-
* receiver; some callees, depending how low in the type hierarchies they are, may only see
80-
* a number of types smaller than the saturation cut-off limit.
78+
* Note that in open world analysis the formal receiver of all callees linked to a context
79+
* insensitive invoke will be notified of saturation.
80+
*
81+
* For a formal receiver with a closed declared type (which corresponds to the declaring
82+
* class of its method) the saturation of the actual receiver doesn't result in the
83+
* saturation of the formal receiver; some callees, depending on how low in the type
84+
* hierarchies they are, may only see a number of types smaller than the saturation cut-off
85+
* limit.
86+
*
87+
* If the declared type is open we cannot make any assumptions and simply propagate the
88+
* saturation stamp.
8189
*/
90+
if (!bb.isClosed(declaredType)) {
91+
super.onInputSaturated(bb, input);
92+
}
8293
}
8394

8495
public boolean addReceiverState(PointsToAnalysis bb, TypeState add) {

0 commit comments

Comments
 (0)