Skip to content

Commit

Permalink
[GR-19721] Improve points-to analysis performance.
Browse files Browse the repository at this point in the history
PullRequest: graal/4998
  • Loading branch information
cstancu committed Apr 11, 2020
2 parents ed1e408 + 25896f0 commit 92d44c1
Show file tree
Hide file tree
Showing 41 changed files with 1,030 additions and 90 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.graalvm.compiler.nodes.Invoke;
import org.graalvm.compiler.options.OptionValues;

import com.oracle.graal.pointsto.api.PointstoOptions;
import com.oracle.graal.pointsto.flow.AbstractSpecialInvokeTypeFlow;
import com.oracle.graal.pointsto.flow.AbstractVirtualInvokeTypeFlow;
import com.oracle.graal.pointsto.flow.ActualReturnTypeFlow;
Expand All @@ -49,8 +50,34 @@ public abstract class AnalysisPolicy {

protected final OptionValues options;

protected final boolean aliasArrayTypeFlows;
protected final boolean relaxTypeFlowConstraints;
protected final boolean removeSaturatedTypeFlows;
protected final int typeFlowSaturationCutoff;

public AnalysisPolicy(OptionValues options) {
this.options = options;

aliasArrayTypeFlows = PointstoOptions.AliasArrayTypeFlows.getValue(options);
relaxTypeFlowConstraints = PointstoOptions.RelaxTypeFlowStateConstraints.getValue(options);
removeSaturatedTypeFlows = PointstoOptions.RemoveSaturatedTypeFlows.getValue(options);
typeFlowSaturationCutoff = PointstoOptions.TypeFlowSaturationCutoff.getValue(options);
}

public boolean aliasArrayTypeFlows() {
return aliasArrayTypeFlows;
}

public boolean relaxTypeFlowConstraints() {
return relaxTypeFlowConstraints;
}

public boolean removeSaturatedTypeFlows() {
return removeSaturatedTypeFlows;
}

public int typeFlowSaturationCutoff() {
return typeFlowSaturationCutoff;
}

/** Provide an analysis context policy. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,14 @@ public TypeFlow<?> getAllSynchronizedTypeFlow() {
}

public TypeState getAllSynchronizedTypeState() {
/*
* If all-synchrnonized type flow, i.e., the type flow that keeps track of the types of all
* monitor objects, is saturated then we need to assume that any type can be used for
* monitors.
*/
if (allSynchronizedTypeFlow.isSaturated()) {
return getAllInstantiatedTypeFlow().getState();
}
return allSynchronizedTypeFlow.getState();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,16 +153,28 @@ public FieldTypeStore createFieldTypeStore(AnalysisObject object, AnalysisField
public ArrayElementsTypeStore createArrayElementsTypeStore(AnalysisObject object, AnalysisUniverse universe) {
assert PointstoOptions.AllocationSiteSensitiveHeap.getValue(options);
if (object.type().isArray()) {
if (object.isContextInsensitiveObject()) {
return new SplitArrayElementsTypeStore(object);
} else {
return new UnifiedArrayElementsTypeStore(object);
if (aliasArrayTypeFlows) {
/* Alias all array type flows using the elements type flow model of Object type. */
if (object.type().getElementalType().isJavaLangObject() && object.isContextInsensitiveObject()) {
// return getArrayElementsTypeStore(object);
return new UnifiedArrayElementsTypeStore(object);
}
return universe.objectType().getArrayClass().getContextInsensitiveAnalysisObject().getArrayElementsTypeStore();
}
return getArrayElementsTypeStore(object);
} else {
return null;
}
}

private static ArrayElementsTypeStore getArrayElementsTypeStore(AnalysisObject object) {
if (object.isContextInsensitiveObject()) {
return new SplitArrayElementsTypeStore(object);
} else {
return new UnifiedArrayElementsTypeStore(object);
}
}

@Override
public AbstractVirtualInvokeTypeFlow createVirtualInvokeTypeFlow(Invoke invoke, AnalysisType receiverType, AnalysisMethod targetMethod,
TypeFlow<?>[] actualParameters, ActualReturnTypeFlow actualReturn, BytecodeLocation location) {
Expand Down Expand Up @@ -223,6 +235,7 @@ public void onObservedUpdate(BigBang bb) {
bb.reportIllegalUnknownUse(graphRef.getMethod(), source, "Illegal: Invoke on UnknownTypeState objects. Invoke: " + this);
return;
}
receiverState = filterReceiverState(bb, receiverState);

/* Use the tandem types - objects iterator. */
TypesObjectsIterator toi = receiverState.getTypesObjectsIterator();
Expand Down Expand Up @@ -298,7 +311,7 @@ public void onObservedUpdate(BigBang bb) {

initCallee();

TypeState invokeState = getReceiver().getState();
TypeState invokeState = filterReceiverState(bb, getReceiver().getState());
for (AnalysisObject receiverObject : invokeState.objects()) {
AnalysisContext calleeContext = bb.contextPolicy().calleeContext(bb, receiverObject, callerContext, callee);
MethodFlowsGraph calleeFlows = callee.addContext(bb, calleeContext, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,13 @@ public FieldTypeStore createFieldTypeStore(AnalysisObject object, AnalysisField
@Override
public ArrayElementsTypeStore createArrayElementsTypeStore(AnalysisObject object, AnalysisUniverse universe) {
if (object.type().isArray()) {
if (aliasArrayTypeFlows) {
/* Alias all array type flows using the elements type flow model of Object type. */
if (object.type().getComponentType().isJavaLangObject()) {
return new UnifiedArrayElementsTypeStore(object);
}
return universe.objectType().getArrayClass().getContextInsensitiveAnalysisObject().getArrayElementsTypeStore();
}
return new UnifiedArrayElementsTypeStore(object);
} else {
return null;
Expand Down Expand Up @@ -169,16 +176,38 @@ public TypeFlow<BytecodePosition> copy(BigBang bb, MethodFlowsGraph methodFlows)

@Override
public void onObservedUpdate(BigBang bb) {
assert this.isClone();

assert this.isClone() || this.isContextInsensitive();
if (isSaturated()) {
/* The receiver can saturate while the invoke update was waiting to be scheduled. */
return;
}
TypeState receiverState = getReceiver().getState();

if (receiverState.isUnknown()) {
bb.reportIllegalUnknownUse(graphRef.getMethod(), source, "Illegal: Invoke on UnknownTypeState objects. Invoke: " + this);
return;
}
if (!isContextInsensitive()) {
/*
* The context insensitive invoke receiver doesn't need any filtering, the invoke is
* directly linked to its receiver type.
*/
receiverState = filterReceiverState(bb, receiverState);
}

for (AnalysisType type : receiverState.types()) {
if (isSaturated()) {
/*-
* The receiver can become saturated during the callees linking, which saturates
* the invoke, when linking the return flow of callees for code patterns like:
*
* Object cur = ...
* while {
* cur = cur.next();
* }
*/
return;
}
if (seenReceiverTypes.containsType(type)) {
/* Already resolved this type and linked the callee in a previous update. */
continue;
Expand Down Expand Up @@ -217,13 +246,93 @@ public void onObservedUpdate(BigBang bb) {
seenReceiverTypes = receiverState;
}

@Override
public void onObservedSaturated(BigBang bb, TypeFlow<?> observed) {
assert this.isClone() && !this.isContextInsensitive();

setSaturated();

/*
* The receiver object flow of the invoke operation is saturated; it will stop sending
* notificatons. Swap the invoke flow with the unique, context-insensitive invoke flow
* corresponding to the target method, which is already registered as an observer for
* the type flow of the receiver type and therefore saturated. This is a conservative
* approximation and this invoke will reach all possible callees.
*/

/* Deregister the invoke as an observer of the receiver. */
getReceiver().removeObserver(this);

/* Unlink all callees. */
for (AnalysisMethod callee : super.getCallees()) {
MethodFlowsGraph calleeFlows = callee.getTypeFlow().getFlows(bb.contextPolicy().emptyContext());
/* Iterate over the actual parameters in caller context. */
for (int i = 0; i < actualParameters.length; i++) {
/* Get the formal parameter from the callee. */
TypeFlow<?> formalParam = calleeFlows.getParameter(i);
/* Remove the link between the actual and the formal parameters. */
if (actualParameters[i] != null && formalParam != null) {
actualParameters[i].removeUse(formalParam);
}
}
/* Remove the link between the formal and the actual return, if present. */
if (actualReturn != null && calleeFlows.getResult() != null) {
calleeFlows.getResult().removeUse(actualReturn);
}
}

/* Link the saturated invoke. */
AbstractVirtualInvokeTypeFlow contextInsensitiveInvoke = (AbstractVirtualInvokeTypeFlow) targetMethod.initAndGetContextInsensitiveInvoke(bb);
contextInsensitiveInvoke.addInvokeLocation(getSource());

/*
* Link the call site actual parameters to the saturated invoke actual parameters. The
* receiver is already set in the saturated invoke.
*/
for (int i = 1; i < actualParameters.length; i++) {
/* Primitive type parameters are not modeled, hence null. */
if (actualParameters[i] != null) {
actualParameters[i].addUse(bb, contextInsensitiveInvoke.getActualParameter(i));
}
}
if (actualReturn != null) {
/* Link the actual return. */
contextInsensitiveInvoke.getActualReturn().addUse(bb, actualReturn);
}

/* Cleanup this type flow's state. */
callees = null;
seenReceiverTypes = null;
}

@Override
public void setSaturated() {
super.setSaturated();
if (this.isClone()) {
/*
* If this is a clone, mark the original as saturated too such that
* originalInvoke.getCallees() is redirected to the context-insensitive invoke.
*/
originalInvoke.setSaturated();
}
}

@Override
public final Collection<AnalysisMethod> getCallees() {
if (isSaturated()) {
return targetMethod.getContextInsensitiveInvoke().getCallees();
} else {
return super.getCallees();
}
}

@Override
public Collection<MethodFlowsGraph> getCalleesFlows(BigBang bb) {
// collect the flow graphs, one for each analysis method, since it is context
// insensitive
Collection<AnalysisMethod> callees = getCallees();
List<MethodFlowsGraph> methodFlowsGraphs = new ArrayList<>(callees.size());
for (AnalysisMethod method : callees) {
Collection<AnalysisMethod> calleesList = getCallees();
List<MethodFlowsGraph> methodFlowsGraphs = new ArrayList<>(calleesList.size());
for (AnalysisMethod method : calleesList) {
methodFlowsGraphs.add(method.getTypeFlow().getFlows(bb.contextPolicy().emptyContext()));
}
return methodFlowsGraphs;
Expand Down Expand Up @@ -270,7 +379,8 @@ public void onObservedUpdate(BigBang bb) {
* needs to be updated as there is no direct update link between actual and formal
* receivers.
*/
updateReceiver(bb, calleeFlows, getReceiver().getState());
TypeState invokeState = filterReceiverState(bb, getReceiver().getState());
updateReceiver(bb, calleeFlows, invokeState);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,36 @@ public class PointstoOptions {
@Option(help = "The maximum size of type and method profiles returned by the static analysis. -1 indicates no limitation.")//
public static final OptionKey<Integer> AnalysisSizeCutoff = new OptionKey<>(8);

@Option(help = "The maximum number of types recorded in a type flow. -1 indicates no limitation.")//
public static final OptionKey<Integer> TypeFlowSaturationCutoff = new OptionKey<Integer>(20) {
@Override
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Integer oldValue, Integer newValue) {
assert newValue.intValue() >= AnalysisSizeCutoff.getValueOrDefault(values);
super.onValueUpdate(values, oldValue, newValue);
}
};

@Option(help = "Enable the type flow saturation analysis performance optimization.")//
public static final OptionKey<Boolean> RemoveSaturatedTypeFlows = new OptionKey<Boolean>(false) {
@Override
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
/* Removing saturated type flows needs array type flows aliasing. */
AliasArrayTypeFlows.update(values, newValue);
}
};

@Option(help = "Model all array type flows using a unique elements type flow abstraction.")//
public static final OptionKey<Boolean> AliasArrayTypeFlows = new OptionKey<Boolean>(false) {
@Override
protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean oldValue, Boolean newValue) {
/* Aliasing array type flows implies relaxation of type flow constraints. */
RelaxTypeFlowStateConstraints.update(values, newValue);
}
};

@Option(help = "Allow a type flow state to contain types not compatible with its declared type.")//
public static final OptionKey<Boolean> RelaxTypeFlowStateConstraints = new OptionKey<>(false);

@Option(help = "Report unresolved elements as errors.")//
public static final OptionKey<Boolean> UnresolvedIsError = new OptionKey<>(true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ protected void initCallee() {
@Override
public abstract void onObservedUpdate(BigBang bb);

@Override
public void onObservedSaturated(BigBang bb, TypeFlow<?> observed) {
assert this.isClone();
/* When the receiver flow saturates start observing the flow of the receiver type. */
replaceObservedWith(bb, receiverType);
}

@Override
public abstract Collection<MethodFlowsGraph> getCalleesFlows(BigBang bb);

Expand Down
Loading

0 comments on commit 92d44c1

Please sign in to comment.