Skip to content

Commit b93973b

Browse files
committed
[GR-29145] Points-to analysis constant propagation in default policy.
PullRequest: graal/11594
2 parents 04cde4b + d323511 commit b93973b

15 files changed

+278
-68
lines changed

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public abstract class AbstractAnalysisEngine implements BigBang {
7575
private final HeapScanningPolicy heapScanningPolicy;
7676

7777
protected final Boolean extendedAsserts;
78+
protected final int maxConstantObjectsPerType;
79+
protected final boolean profileConstantObjects;
7880

7981
protected final OptionValues options;
8082
protected final DebugContext debug;
@@ -115,6 +117,8 @@ public AbstractAnalysisEngine(OptionValues options, AnalysisUniverse universe, H
115117
this.analysisTimer = timerCollection.get(TimerCollection.Registry.ANALYSIS);
116118

117119
this.extendedAsserts = PointstoOptions.ExtendedAsserts.getValue(options);
120+
maxConstantObjectsPerType = PointstoOptions.MaxConstantObjectsPerType.getValue(options);
121+
profileConstantObjects = PointstoOptions.ProfileConstantObjects.getValue(options);
118122

119123
this.heapScanningPolicy = PointstoOptions.ExhaustiveHeapScan.getValue(options)
120124
? HeapScanningPolicy.scanAll()
@@ -230,6 +234,17 @@ public boolean extendedAsserts() {
230234
return extendedAsserts;
231235
}
232236

237+
public int maxConstantObjectsPerType() {
238+
return maxConstantObjectsPerType;
239+
}
240+
241+
public void profileConstantObject(AnalysisType type) {
242+
if (profileConstantObjects) {
243+
PointsToAnalysis.ConstantObjectsProfiler.registerConstant(type);
244+
PointsToAnalysis.ConstantObjectsProfiler.maybeDumpConstantHistogram();
245+
}
246+
}
247+
233248
@Override
234249
public OptionValues getOptions() {
235250
return options;

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,8 @@ public boolean forNonNullFieldValue(JavaConstant receiver, AnalysisField field,
6767

6868
/* Add the constant value object to the field's type flow. */
6969
FieldTypeFlow fieldTypeFlow = getFieldTypeFlow(field, receiver);
70-
AnalysisObject constantObject = bb.analysisPolicy().createConstantObject(analysis, fieldValue, fieldType);
7170
/* Add the new constant to the field's flow state. */
72-
TypeState constantTypeState = TypeState.forNonNullObject(analysis, constantObject);
73-
return fieldTypeFlow.addState(analysis, constantTypeState);
71+
return fieldTypeFlow.addState(analysis, bb.analysisPolicy().constantTypeState(analysis, fieldValue, fieldType));
7472
}
7573

7674
/**
@@ -107,10 +105,8 @@ public boolean forNullArrayElement(JavaConstant array, AnalysisType arrayType, i
107105
public boolean forNonNullArrayElement(JavaConstant array, AnalysisType arrayType, JavaConstant elementConstant, AnalysisType elementType, int elementIndex, ScanReason reason) {
108106
ArrayElementsTypeFlow arrayObjElementsFlow = getArrayElementsFlow(array, arrayType);
109107
PointsToAnalysis analysis = getAnalysis();
110-
AnalysisObject constantObject = bb.analysisPolicy().createConstantObject(analysis, elementConstant, elementType);
111108
/* Add the constant element to the constant's array type flow. */
112-
TypeState elementTypeState = TypeState.forNonNullObject(analysis, constantObject);
113-
return arrayObjElementsFlow.addState(analysis, elementTypeState);
109+
return arrayObjElementsFlow.addState(analysis, bb.analysisPolicy().constantTypeState(analysis, elementConstant, elementType));
114110
}
115111

116112
/**

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ public int typeFlowSaturationCutoff() {
123123
/** Create a constant object abstraction. */
124124
public abstract AnalysisObject createConstantObject(PointsToAnalysis bb, JavaConstant constant, AnalysisType exactType);
125125

126+
/** Wrap a constant into a type state abstraction. */
127+
public abstract TypeState constantTypeState(PointsToAnalysis bb, JavaConstant constant, AnalysisType exactType);
128+
126129
/** Create type state for dynamic new instance. */
127130
public abstract TypeState dynamicNewInstanceState(PointsToAnalysis bb, TypeState currentState, TypeState newState, BytecodePosition allocationSite, AnalysisContext allocationContext);
128131

@@ -135,7 +138,7 @@ public int typeFlowSaturationCutoff() {
135138
*/
136139
public abstract void linkClonedObjects(PointsToAnalysis bb, TypeFlow<?> inputFlow, CloneTypeFlow cloneFlow, BytecodePosition source);
137140

138-
public abstract FieldTypeStore createFieldTypeStore(AnalysisObject object, AnalysisField field, AnalysisUniverse universe);
141+
public abstract FieldTypeStore createFieldTypeStore(PointsToAnalysis bb, AnalysisObject object, AnalysisField field, AnalysisUniverse universe);
139142

140143
public abstract ArrayElementsTypeStore createArrayElementsTypeStore(AnalysisObject object, AnalysisUniverse universe);
141144

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,9 @@ public class PointstoOptions {
6868
@Option(help = "The maximum number of objects recorded for each type of a type state before disabling heap sensitivity for that type. The analysis must be heap sensitive. It has a minimum value of 1.")//
6969
public static final OptionKey<Integer> MaxObjectSetSize = new OptionKey<>(100);
7070

71-
@Option(help = "The maximum number of constant objects recorded for each type before merging the constants into one unique constant object per type. The analysis must be heap sensitive. It has a minimum value of 1.")//
72-
public static final OptionKey<Integer> MaxConstantObjectsPerType = new OptionKey<>(100);
71+
@Option(help = "The maximum number of constant objects recorded for each type before merging the constants into one unique constant object per type. " +
72+
"If the value is 0 there is no limit.")//
73+
public static final OptionKey<Integer> MaxConstantObjectsPerType = new OptionKey<>(0);
7374

7475
@Option(help = "Track the progress of the static analysis.")//
7576
public static final OptionKey<Boolean> ProfileAnalysisOperations = new OptionKey<>(false);

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,10 @@ public EconomicMap<Object, InvokeTypeFlow> getInvokes() {
143143
return flowsGraph == null ? EconomicMap.emptyMap() : flowsGraph.getInvokes();
144144
}
145145

146+
public TypeFlow<?> getParameter(int idx) {
147+
return flowsGraph == null ? null : flowsGraph.getParameter(idx);
148+
}
149+
146150
public Iterable<TypeFlow<?>> getParameters() {
147151
return flowsGraph == null ? Collections.emptyList() : Arrays.asList(flowsGraph.getParameters());
148152
}

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/bytecode/BytecodeSensitiveAnalysisPolicy.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import com.oracle.graal.pointsto.flow.context.AnalysisContext;
5555
import com.oracle.graal.pointsto.flow.context.object.AllocationContextSensitiveObject;
5656
import com.oracle.graal.pointsto.flow.context.object.AnalysisObject;
57+
import com.oracle.graal.pointsto.flow.context.object.ConstantContextSensitiveObject;
5758
import com.oracle.graal.pointsto.meta.AnalysisField;
5859
import com.oracle.graal.pointsto.meta.AnalysisType;
5960
import com.oracle.graal.pointsto.meta.AnalysisUniverse;
@@ -152,12 +153,18 @@ public AnalysisObject createHeapObject(PointsToAnalysis bb, AnalysisType type, B
152153
public AnalysisObject createConstantObject(PointsToAnalysis bb, JavaConstant constant, AnalysisType exactType) {
153154
/* Get the analysis object wrapping the JavaConstant. */
154155
if (bb.trackConcreteAnalysisObjects(exactType)) {
155-
return exactType.getCachedConstantObject(bb, constant);
156+
return exactType.getCachedConstantObject(bb, constant, (c) -> new ConstantContextSensitiveObject(bb, exactType, c));
156157
} else {
157158
return exactType.getContextInsensitiveAnalysisObject();
158159
}
159160
}
160161

162+
@Override
163+
public TypeState constantTypeState(PointsToAnalysis bb, JavaConstant constant, AnalysisType exactType) {
164+
AnalysisObject constantObject = bb.analysisPolicy().createConstantObject(bb, constant, exactType);
165+
return TypeState.forNonNullObject(bb, constantObject);
166+
}
167+
161168
@Override
162169
public TypeState dynamicNewInstanceState(PointsToAnalysis bb, TypeState currentState, TypeState newState, BytecodePosition allocationSite, AnalysisContext allocationContext) {
163170
/* Generate a heap object for every new incoming type. */
@@ -258,7 +265,7 @@ public void linkClonedObjects(PointsToAnalysis bb, TypeFlow<?> inputFlow, CloneT
258265
}
259266

260267
@Override
261-
public FieldTypeStore createFieldTypeStore(AnalysisObject object, AnalysisField field, AnalysisUniverse universe) {
268+
public FieldTypeStore createFieldTypeStore(PointsToAnalysis bb, AnalysisObject object, AnalysisField field, AnalysisUniverse universe) {
262269
assert PointstoOptions.AllocationSiteSensitiveHeap.getValue(options);
263270
if (object.isContextInsensitiveObject()) {
264271
/*

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

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public class AnalysisObject implements Comparable<AnalysisObject> {
6060
protected enum AnalysisObjectKind {
6161
/** The types of runtime objects that the analysis models. */
6262
ContextInsensitive("!S"),
63+
ConstantObject("C"),
6364
AllocationContextSensitive("AS"),
6465
ConstantContextSensitive("CS");
6566

@@ -82,8 +83,19 @@ protected enum AnalysisObjectKind {
8283
protected final AnalysisObjectKind kind;
8384

8485
/**
85-
* Is this a context sensitive object that was merged with a context insensitive object, or a
86-
* context insensitive object that has merged some context sensitive objects?
86+
* The merging of analysis objects is used to erase the identity of more concrete analysis
87+
* objects (i.e., objects that wrap a constant, or retain information about their allocation
88+
* location) and to replace them with the per-type context-insensitive analysis object.
89+
*
90+
* This distinction between merged and un-merged objects is essential to correctly track field
91+
* and array flows reads and writes. For example when a concrete analysis object is the receiver
92+
* of a write operation then only the field flow associated with that receiver will get the
93+
* state of the stored value, such that any read from the same receiver and field will return
94+
* the written values, but no others. However, if there is a union between the concrete receiver
95+
* and a context-insensitive object of the same type then the receiver needs to be marked as
96+
* `merged` to signal that all reads from a field of this object must include all the state
97+
* written to the corresponding field flow of the context-insensitive object and all writes must
98+
* also flow in the corresponding field flow of the context-insensitive object.
8799
*/
88100
protected volatile boolean merged;
89101

@@ -171,8 +183,8 @@ public final boolean isConstantContextSensitiveObject() {
171183
return this.kind == AnalysisObjectKind.ConstantContextSensitive;
172184
}
173185

174-
public final boolean isContextSensitiveObject() {
175-
return this.isAllocationContextSensitiveObject() || this.isConstantContextSensitiveObject();
186+
public final boolean isConstantObject() {
187+
return this.kind == AnalysisObjectKind.ConstantObject;
176188
}
177189

178190
public ArrayElementsTypeStore getArrayElementsTypeStore() {
@@ -203,6 +215,10 @@ public UnsafeWriteSinkTypeFlow getUnsafeWriteSinkFrozenFilterFlow(PointsToAnalys
203215
return fieldTypeStore.unsafeWriteSinkFlow(bb);
204216
}
205217

218+
public FieldTypeStore getInstanceFieldTypeStore(PointsToAnalysis bb, AnalysisField field) {
219+
return getInstanceFieldTypeStore(bb, null, null, field);
220+
}
221+
206222
/** Returns the instance field flow corresponding to a filed of the object's type. */
207223
public FieldTypeFlow getInstanceFieldFlow(PointsToAnalysis bb, AnalysisField field, boolean isStore) {
208224
return getInstanceFieldFlow(bb, null, null, field, isStore);
@@ -231,7 +247,7 @@ final FieldTypeStore getInstanceFieldTypeStore(PointsToAnalysis bb, TypeFlow<?>
231247

232248
FieldTypeStore fieldStore = instanceFieldsTypeStore.get(field.getPosition());
233249
if (fieldStore == null) {
234-
fieldStore = bb.analysisPolicy().createFieldTypeStore(this, field, bb.getUniverse());
250+
fieldStore = bb.analysisPolicy().createFieldTypeStore(bb, this, field, bb.getUniverse());
235251
boolean result = instanceFieldsTypeStore.compareAndSet(field.getPosition(), null, fieldStore);
236252
if (result) {
237253
fieldStore.init(bb);

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
import jdk.vm.ci.meta.JavaConstant;
3434

3535
/**
36-
* A context sensitive analysis object that represents a constant. The context for this analysis
37-
* object is the constant it wraps.
36+
* A context-sensitive analysis object that represents a constant. The implicit context for this
37+
* analysis object is the constant that it wraps.
3838
*/
3939
public class ConstantContextSensitiveObject extends ContextSensitiveAnalysisObject {
4040

@@ -76,6 +76,7 @@ public ConstantContextSensitiveObject(PointsToAnalysis bb, AnalysisType type, Ja
7676
super(bb.getUniverse(), type, AnalysisObjectKind.ConstantContextSensitive);
7777
assert bb.trackConcreteAnalysisObjects(type);
7878
this.constant = constant;
79+
bb.profileConstantObject(type);
7980
}
8081

8182
public JavaConstant getConstant() {
@@ -137,7 +138,7 @@ public String toString() {
137138
if (constant == null) {
138139
result.append("MERGED CONSTANT");
139140
} else {
140-
// result.append(constant);
141+
result.append(constant);
141142
}
142143
return result.toString();
143144
}

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@
4141

4242
import jdk.vm.ci.code.BytecodePosition;
4343

44+
/**
45+
* This class models analysis objects that retain some information about their allocation. So here
46+
* context doesn't refer to calling context, but rather to the allocation context of this object
47+
* (which could contain information about the calling context of its allocator). More importantly,
48+
* this object abstraction is responsible four correct routing of field and array read and write
49+
* flows.
50+
*/
4451
public class ContextSensitiveAnalysisObject extends AnalysisObject {
4552

4653
public ContextSensitiveAnalysisObject(AnalysisUniverse universe, AnalysisType type, AnalysisObjectKind kind) {

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

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
4040
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
4141
import java.util.function.Consumer;
42+
import java.util.function.Function;
4243

4344
import org.graalvm.compiler.debug.GraalError;
4445
import org.graalvm.compiler.graph.Node;
@@ -47,9 +48,7 @@
4748

4849
import com.oracle.graal.pointsto.BigBang;
4950
import com.oracle.graal.pointsto.PointsToAnalysis;
50-
import com.oracle.graal.pointsto.PointsToAnalysis.ConstantObjectsProfiler;
5151
import com.oracle.graal.pointsto.api.DefaultUnsafePartition;
52-
import com.oracle.graal.pointsto.api.PointstoOptions;
5352
import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException;
5453
import com.oracle.graal.pointsto.flow.AllInstantiatedTypeFlow;
5554
import com.oracle.graal.pointsto.flow.context.object.AnalysisObject;
@@ -66,7 +65,6 @@
6665
import com.oracle.svm.util.UnsafePartitionKind;
6766

6867
import jdk.vm.ci.meta.Assumptions.AssumptionResult;
69-
import jdk.vm.ci.meta.Constant;
7068
import jdk.vm.ci.meta.JavaConstant;
7169
import jdk.vm.ci.meta.JavaKind;
7270
import jdk.vm.ci.meta.PrimitiveConstant;
@@ -81,8 +79,8 @@ public abstract class AnalysisType extends AnalysisElement implements WrappedJav
8179
private static final AtomicReferenceFieldUpdater<AnalysisType, ConcurrentHashMap> UNSAFE_ACCESS_FIELDS_UPDATER = //
8280
AtomicReferenceFieldUpdater.newUpdater(AnalysisType.class, ConcurrentHashMap.class, "unsafeAccessedFields");
8381

84-
private static final AtomicReferenceFieldUpdater<AnalysisType, ConstantContextSensitiveObject> UNIQUE_CONSTANT_UPDATER = //
85-
AtomicReferenceFieldUpdater.newUpdater(AnalysisType.class, ConstantContextSensitiveObject.class, "uniqueConstant");
82+
private static final AtomicReferenceFieldUpdater<AnalysisType, AnalysisObject> UNIQUE_CONSTANT_UPDATER = //
83+
AtomicReferenceFieldUpdater.newUpdater(AnalysisType.class, AnalysisObject.class, "uniqueConstant");
8684

8785
@SuppressWarnings("rawtypes")//
8886
private static final AtomicReferenceFieldUpdater<AnalysisType, Object> INTERCEPTORS_UPDATER = //
@@ -140,13 +138,13 @@ public abstract class AnalysisType extends AnalysisElement implements WrappedJav
140138
/** The unique context insensitive analysis object for this type. */
141139
private AnalysisObject contextInsensitiveAnalysisObject;
142140
/** Mapping from JavaConstant to the analysis ConstantObject. */
143-
private ConcurrentMap<Constant, ConstantContextSensitiveObject> constantObjectsCache;
141+
private ConcurrentMap<JavaConstant, AnalysisObject> constantObjectsCache;
144142
/**
145143
* A unique ConstantObject per analysis type. When the size of {@link #constantObjectsCache} is
146144
* above a threshold all the ConstantObject recorded until that moment are merged in the
147145
* {@link #uniqueConstant}.
148146
*/
149-
private volatile ConstantContextSensitiveObject uniqueConstant;
147+
private volatile AnalysisObject uniqueConstant;
150148

151149
/**
152150
* Cache for the resolved methods.
@@ -341,10 +339,10 @@ public AnalysisObject getUniqueConstantObject() {
341339
return uniqueConstant;
342340
}
343341

344-
public AnalysisObject getCachedConstantObject(PointsToAnalysis bb, JavaConstant constant) {
342+
public AnalysisObject getCachedConstantObject(PointsToAnalysis bb, JavaConstant constant, Function<JavaConstant, AnalysisObject> constantTransformer) {
345343

346344
/*
347-
* Constant caching is only used we certain analysis policies. Ideally we would store the
345+
* Constant caching is only used with certain analysis policies. Ideally we would store the
348346
* cache in the policy, but it is simpler to store the cache for each type.
349347
*/
350348
assert bb.analysisPolicy().needsConstantCache() : "The analysis policy doesn't specify the need for a constants cache.";
@@ -356,41 +354,32 @@ public AnalysisObject getCachedConstantObject(PointsToAnalysis bb, JavaConstant
356354
return uniqueConstant;
357355
}
358356

359-
if (constantObjectsCache.size() >= PointstoOptions.MaxConstantObjectsPerType.getValue(bb.getOptions())) {
357+
/* If maxConstantObjectsPerType is 0 there is no limit, i.e., we track all constants. */
358+
if (bb.maxConstantObjectsPerType() > 0 && constantObjectsCache.size() >= bb.maxConstantObjectsPerType()) {
360359
// The number of constant objects has increased above the limit,
361360
// merge the constants in the uniqueConstant and return it
362361
mergeConstantObjects(bb);
363362
return uniqueConstant;
364363
}
365364

366-
// Get the analysis ConstantObject modeling the JavaConstant
367-
AnalysisObject result = constantObjectsCache.get(constant);
368-
if (result == null) {
369-
// Create a ConstantObject to model each JavaConstant
370-
ConstantContextSensitiveObject newValue = new ConstantContextSensitiveObject(bb, this, constant);
371-
ConstantContextSensitiveObject oldValue = constantObjectsCache.putIfAbsent(constant, newValue);
372-
result = oldValue != null ? oldValue : newValue;
373-
374-
if (PointstoOptions.ProfileConstantObjects.getValue(bb.getOptions())) {
375-
ConstantObjectsProfiler.registerConstant(this);
376-
ConstantObjectsProfiler.maybeDumpConstantHistogram();
377-
}
378-
}
379-
380-
return result;
365+
/* Get the analysis ConstantObject modeling the JavaConstant. */
366+
return constantObjectsCache.computeIfAbsent(constant, constantTransformer);
381367
}
382368

383369
private void mergeConstantObjects(PointsToAnalysis bb) {
384-
ConstantContextSensitiveObject uConstant = new ConstantContextSensitiveObject(bb, this, null);
370+
ConstantContextSensitiveObject uConstant = new ConstantContextSensitiveObject(bb, this);
385371
if (UNIQUE_CONSTANT_UPDATER.compareAndSet(this, null, uConstant)) {
386-
constantObjectsCache.values().stream().forEach(constantObject -> {
372+
constantObjectsCache.values().forEach(constantObject -> {
387373
/*
388374
* The order of the two lines below matters: setting the merged flag first, before
389375
* doing the actual merging, ensures that concurrent updates to the flow are still
390376
* merged correctly.
391377
*/
392-
constantObject.setMergedWithUniqueConstantObject();
393-
constantObject.mergeInstanceFieldsFlows(bb, uniqueConstant);
378+
if (constantObject instanceof ConstantContextSensitiveObject) {
379+
ConstantContextSensitiveObject ct = (ConstantContextSensitiveObject) constantObject;
380+
ct.setMergedWithUniqueConstantObject();
381+
ct.mergeInstanceFieldsFlows(bb, uniqueConstant);
382+
}
394383
});
395384
}
396385
}

0 commit comments

Comments
 (0)