Skip to content

Commit 7a033b9

Browse files
author
Christian Wimmer
committed
[GR-51974] Track never-null instance fields in the static analysis.
PullRequest: graal/16922
2 parents c10d824 + e2e2569 commit 7a033b9

28 files changed

+186
-166
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ default void onFieldAccessed(AnalysisField field) {
108108
}
109109

110110
@SuppressWarnings("unused")
111-
default void injectFieldTypes(AnalysisField aField, AnalysisType... customTypes) {
111+
default void injectFieldTypes(AnalysisField aField, List<AnalysisType> customTypes, boolean canBeNull) {
112112
}
113113

114114
@SuppressWarnings("unused")

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/api/PointstoOptions.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean o
149149
@Option(help = "Run conditional elimination before static analysis.", type = Expert)//
150150
public static final OptionKey<Boolean> ConditionalEliminationBeforeAnalysis = new OptionKey<>(true);
151151

152+
@Option(help = "Track in the static analysis whether an instance field is never null.")//
153+
public static final OptionKey<Boolean> TrackNeverNullInstanceFields = new OptionKey<>(true);
154+
152155
/**
153156
* Controls the static analysis context sensitivity. Available values:
154157
* <p/>

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

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,21 +39,6 @@ public class FieldTypeFlow extends TypeFlow<AnalysisField> {
3939
private static final AtomicReferenceFieldUpdater<FieldTypeFlow, FieldFilterTypeFlow> FILTER_FLOW_UPDATER = AtomicReferenceFieldUpdater.newUpdater(FieldTypeFlow.class, FieldFilterTypeFlow.class,
4040
"filterFlow");
4141

42-
private static TypeState initialFieldState(AnalysisField field) {
43-
if (field.getStorageKind().isPrimitive()) {
44-
return TypeState.forPrimitiveConstant(0);
45-
} else if (field.canBeNull()) {
46-
/*
47-
* All object type instance fields of a new object can be null. Instance fields are null
48-
* in the time between the new-instance and the first write to a field. This is even
49-
* true for non-null final fields because even final fields are null until they are
50-
* initialized in a constructor.
51-
*/
52-
return TypeState.forNull();
53-
}
54-
return TypeState.forEmpty();
55-
}
56-
5742
/** The holder of the field flow (null for static fields). */
5843
private final AnalysisObject object;
5944

@@ -65,7 +50,7 @@ public FieldTypeFlow(AnalysisField field, AnalysisType type) {
6550
}
6651

6752
public FieldTypeFlow(AnalysisField field, AnalysisType type, AnalysisObject object) {
68-
super(field, filterUncheckedInterface(type), initialFieldState(field));
53+
super(field, filterUncheckedInterface(type), TypeState.forEmpty());
6954
this.object = object;
7055
}
7156

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,10 @@ public static void registerUsedElements(PointsToAnalysis bb, StructuredGraph gra
252252
NewInstanceNode node = (NewInstanceNode) n;
253253
AnalysisType type = (AnalysisType) node.instanceClass();
254254
type.registerAsInstantiated(AbstractAnalysisEngine.sourcePosition(node));
255+
for (var f : type.getInstanceFields(true)) {
256+
var field = (AnalysisField) f;
257+
field.getInitialFlow().addState(bb, TypeState.defaultValueForKind(field.getStorageKind()));
258+
}
255259

256260
} else if (n instanceof NewInstanceWithExceptionNode) {
257261
NewInstanceWithExceptionNode node = (NewInstanceWithExceptionNode) n;
@@ -1441,6 +1445,11 @@ protected void processCommitAllocation(CommitAllocationNode commitAllocationNode
14411445
AnalysisField field = (AnalysisField) ((VirtualInstanceNode) virtualObject).field(i);
14421446
processStoreField(commitAllocationNode, field, object, value, value.getStackKind(), state);
14431447
}
1448+
} else {
1449+
if (!type.isArray()) {
1450+
AnalysisField field = (AnalysisField) ((VirtualInstanceNode) virtualObject).field(i);
1451+
field.getInitialFlow().addState(bb, TypeState.defaultValueForKind(field.getStorageKind()));
1452+
}
14441453
}
14451454
}
14461455
objectStartIndex += virtualObject.entryCount();

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,9 @@ private void checkField(PointsToAnalysis bb, TypeFlow<?> objectFlow, BytecodePos
262262
@SuppressWarnings("unused")
263263
protected void linkFieldFlows(PointsToAnalysis bb, AnalysisField field, FieldTypeStore fieldStore) {
264264
// link the initial instance field flow to the field write flow
265-
field.getInitialInstanceFieldFlow().addUse(bb, fieldStore.writeFlow());
266-
// link the field read flow to the context insensitive instance field flow
267-
fieldStore.readFlow().addUse(bb, field.getInstanceFieldFlow());
265+
field.getInitialFlow().addUse(bb, fieldStore.writeFlow());
266+
// link the field read flow to the sink flow that accumulates all field types
267+
fieldStore.readFlow().addUse(bb, field.getSinkFlow());
268268
}
269269

270270
/**

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ContextSensitiveAnalysisObject.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,9 @@ public FieldTypeFlow getInstanceFieldFlow(PointsToAnalysis bb, TypeFlow<?> objec
163163
@Override
164164
protected void linkFieldFlows(PointsToAnalysis bb, AnalysisField field, FieldTypeStore fieldStore) {
165165
// link the initial instance field flow to the field write flow
166-
field.getInitialInstanceFieldFlow().addUse(bb, fieldStore.writeFlow());
167-
// link the field read flow to the instance field flow
168-
fieldStore.readFlow().addUse(bb, field.getInstanceFieldFlow());
166+
field.getInitialFlow().addUse(bb, fieldStore.writeFlow());
167+
// link the field read flow to the sink flow that accumulates all field types
168+
fieldStore.readFlow().addUse(bb, field.getSinkFlow());
169169
// Also link the field read flow the field flow on the context insensitive object.
170170
// This ensures that the all values flowing into a context-sensitive field flow
171171
// are also visible from the context-insensitive field flow.

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/heap/ImageHeapScanner.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import java.lang.reflect.Field;
2828
import java.util.Collection;
29+
import java.util.List;
2930
import java.util.Map;
3031
import java.util.Objects;
3132
import java.util.Optional;
@@ -143,16 +144,14 @@ public void onFieldRead(AnalysisField field) {
143144
if (fieldType.isArray() || (fieldType.isInstanceClass() && !fieldType.isAbstract())) {
144145
fieldType.registerAsInstantiated(field);
145146
}
146-
bb.injectFieldTypes(field, fieldType);
147+
bb.injectFieldTypes(field, List.of(fieldType), true);
147148
}
148149
return;
149150
}
150151
if (isValueAvailable(field)) {
151152
JavaConstant fieldValue = readStaticFieldValue(field);
152153
markReachable(fieldValue, reason);
153154
notifyAnalysis(field, null, fieldValue, reason);
154-
} else if (field.canBeNull()) {
155-
notifyAnalysis(field, null, JavaConstant.NULL_POINTER, reason);
156155
}
157156
} else {
158157
/* Trigger field scanning for the already processed objects. */
@@ -616,8 +615,6 @@ private void updateInstanceField(AnalysisField field, ImageHeapInstance imageHea
616615
JavaConstant fieldValue = imageHeapInstance.readFieldValue(field);
617616
markReachable(fieldValue, reason, onAnalysisModified);
618617
notifyAnalysis(field, imageHeapInstance, fieldValue, reason, onAnalysisModified);
619-
} else if (field.canBeNull()) {
620-
notifyAnalysis(field, imageHeapInstance, JavaConstant.NULL_POINTER, reason, onAnalysisModified);
621618
}
622619
}
623620

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisField.java

Lines changed: 23 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,12 @@
3131
import java.util.concurrent.ConcurrentMap;
3232
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
3333

34-
import com.oracle.graal.pointsto.api.HostVM;
3534
import com.oracle.graal.pointsto.api.PointstoOptions;
3635
import com.oracle.graal.pointsto.flow.ContextInsensitiveFieldTypeFlow;
3736
import com.oracle.graal.pointsto.flow.FieldTypeFlow;
3837
import com.oracle.graal.pointsto.infrastructure.OriginalClassProvider;
3938
import com.oracle.graal.pointsto.infrastructure.OriginalFieldProvider;
4039
import com.oracle.graal.pointsto.infrastructure.WrappedJavaField;
41-
import com.oracle.graal.pointsto.typestate.TypeState;
4240
import com.oracle.graal.pointsto.util.AnalysisError;
4341
import com.oracle.graal.pointsto.util.AnalysisFuture;
4442
import com.oracle.graal.pointsto.util.AtomicUtils;
@@ -73,17 +71,17 @@ public abstract class AnalysisField extends AnalysisElement implements WrappedJa
7371

7472
public final ResolvedJavaField wrapped;
7573

76-
/** Field type flow for the static fields. */
77-
protected FieldTypeFlow staticFieldFlow;
78-
79-
/** Initial field type flow, i.e., as specified by the analysis client. */
80-
protected FieldTypeFlow initialInstanceFieldFlow;
81-
74+
/**
75+
* Initial field type flow, i.e., as specified by the analysis client. It can be used to inject
76+
* specific types into a field that the analysis would not see on its own, and to inject the
77+
* null value into a field.
78+
*/
79+
protected FieldTypeFlow initialFlow;
8280
/**
8381
* Field type flow that reflects all the types flowing in this field on its declaring type and
84-
* all the sub-types. It doesn't track any context-sensitive information.
82+
* all the sub-types. It does not track any context-sensitive information.
8583
*/
86-
protected ContextInsensitiveFieldTypeFlow instanceFieldFlow;
84+
protected FieldTypeFlow sinkFlow;
8785

8886
/** The reason flags contain a {@link BytecodePosition} or a reason object. */
8987
@SuppressWarnings("unused") private volatile Object isRead;
@@ -92,12 +90,6 @@ public abstract class AnalysisField extends AnalysisElement implements WrappedJa
9290
@SuppressWarnings("unused") private volatile Object isFolded;
9391
@SuppressWarnings("unused") private volatile Object isUnsafeAccessed;
9492

95-
/**
96-
* By default all instance fields are null before are initialized. It can be specified by
97-
* {@link HostVM} that certain fields will not be null.
98-
*/
99-
private boolean canBeNull;
100-
10193
private ConcurrentMap<Object, Boolean> readBy;
10294
private ConcurrentMap<Object, Boolean> writtenBy;
10395

@@ -129,14 +121,16 @@ public AnalysisField(AnalysisUniverse universe, ResolvedJavaField wrappedField)
129121
declaringClass = universe.lookup(wrappedField.getDeclaringClass());
130122
fieldType = getDeclaredType(universe, wrappedField);
131123

124+
initialFlow = new FieldTypeFlow(this, getType());
132125
if (this.isStatic()) {
133-
this.canBeNull = false;
134-
this.staticFieldFlow = new FieldTypeFlow(this, getType());
135-
this.initialInstanceFieldFlow = null;
126+
/* There is never any context-sensitivity for static fields. */
127+
sinkFlow = initialFlow;
136128
} else {
137-
this.canBeNull = !getStorageKind().isPrimitive();
138-
this.instanceFieldFlow = new ContextInsensitiveFieldTypeFlow(this, getType());
139-
this.initialInstanceFieldFlow = new FieldTypeFlow(this, getType());
129+
/*
130+
* Regardless of the context-sensitivity policy, there is always this single type flow
131+
* that accumulates all types.
132+
*/
133+
sinkFlow = new ContextInsensitiveFieldTypeFlow(this, getType());
140134
}
141135

142136
if (universe.hostVM().useBaseLayer()) {
@@ -202,45 +196,22 @@ public JavaKind getStorageKind() {
202196

203197
}
204198

205-
/**
206-
* Returns all possible types that this field can have. The result is not context sensitive,
207-
* i.e., it is a union of all types found in all contexts.
208-
*/
209-
public TypeState getTypeState() {
210-
if (getType().getStorageKind() != JavaKind.Object) {
211-
return null;
212-
} else if (isStatic()) {
213-
return staticFieldFlow.getState();
214-
} else {
215-
return getInstanceFieldTypeState();
216-
}
217-
}
218-
219-
public TypeState getInstanceFieldTypeState() {
220-
return instanceFieldFlow.getState();
199+
public FieldTypeFlow getInitialFlow() {
200+
return initialFlow;
221201
}
222202

223-
public FieldTypeFlow getInitialInstanceFieldFlow() {
224-
return initialInstanceFieldFlow;
203+
public FieldTypeFlow getSinkFlow() {
204+
return sinkFlow;
225205
}
226206

227207
public FieldTypeFlow getStaticFieldFlow() {
228208
assert Modifier.isStatic(this.getModifiers()) : this;
229-
230-
return staticFieldFlow;
231-
}
232-
233-
/** Get the field type flow, stripped of any context. */
234-
public ContextInsensitiveFieldTypeFlow getInstanceFieldFlow() {
235-
assert !Modifier.isStatic(this.getModifiers()) : this;
236-
237-
return instanceFieldFlow;
209+
return sinkFlow;
238210
}
239211

240212
public void cleanupAfterAnalysis() {
241-
staticFieldFlow = null;
242-
instanceFieldFlow = null;
243-
initialInstanceFieldFlow = null;
213+
initialFlow = null;
214+
sinkFlow = null;
244215
readBy = null;
245216
writtenBy = null;
246217
}
@@ -420,14 +391,6 @@ public void setFieldValueInterceptor(Object fieldValueInterceptor) {
420391
this.fieldValueInterceptor = fieldValueInterceptor;
421392
}
422393

423-
public void setCanBeNull(boolean canBeNull) {
424-
this.canBeNull = canBeNull;
425-
}
426-
427-
public boolean canBeNull() {
428-
return canBeNull;
429-
}
430-
431394
@Override
432395
public String getName() {
433396
return wrapped.getName();

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisField.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,7 @@ public boolean registerAsUnsafeAccessed(Object reason) {
9999
public void saturatePrimitiveField() {
100100
assert fieldType.isPrimitive() || fieldType.isWordType() : this;
101101
var bb = ((PointsToAnalysis) getUniverse().getBigbang());
102-
if (isStatic()) {
103-
staticFieldFlow.addState(bb, TypeState.anyPrimitiveState());
104-
} else {
105-
initialInstanceFieldFlow.addState(bb, TypeState.anyPrimitiveState());
106-
instanceFieldFlow.addState(bb, TypeState.anyPrimitiveState());
107-
}
102+
initialFlow.addState(bb, TypeState.anyPrimitiveState());
103+
sinkFlow.addState(bb, TypeState.anyPrimitiveState());
108104
}
109105
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/PointsToAnalysisType.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,19 @@ public class PointsToAnalysisType extends AnalysisType {
5555
super(universe, javaType, storageKind, objectType, cloneableType);
5656
}
5757

58+
@Override
59+
public boolean registerAsUnsafeAllocated(Object reason) {
60+
boolean result = super.registerAsUnsafeAllocated(reason);
61+
if (result) {
62+
var bb = (PointsToAnalysis) universe.getBigbang();
63+
for (var f : getInstanceFields(true)) {
64+
var field = (AnalysisField) f;
65+
field.getInitialFlow().addState(bb, TypeState.defaultValueForKind(field.getStorageKind()));
66+
}
67+
}
68+
return result;
69+
}
70+
5871
/**
5972
* @see AnalysisType#registerAsAssignable(BigBang)
6073
*/

0 commit comments

Comments
 (0)