Skip to content

Commit

Permalink
Strengthen graphs with constants propagated through static analysis
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Wimmer committed Dec 9, 2022
1 parent 971dd13 commit c7c4bf5
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
import com.oracle.graal.pointsto.typestate.SingleTypeState;
import com.oracle.graal.pointsto.typestate.TypeState;

import jdk.vm.ci.meta.JavaConstant;

public class ContextSensitiveSingleTypeState extends SingleTypeState {
/** The objects of this type state. */
protected final AnalysisObject[] objects;
Expand Down Expand Up @@ -145,8 +147,16 @@ public boolean isAllocation() {
}

@Override
public boolean isConstant() {
return objects[0].isConstantContextSensitiveObject();
public JavaConstant asConstant() {
JavaConstant result = null;
for (AnalysisObject object : objects) {
JavaConstant objectConstant = object.asConstant();
if (objectConstant == null || (result != null && !result.equals(objectConstant))) {
return null;
}
result = objectConstant;
}
return result;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,12 +179,8 @@ public final boolean isAllocationContextSensitiveObject() {
return this.kind == AnalysisObjectKind.AllocationContextSensitive;
}

public final boolean isConstantContextSensitiveObject() {
return this.kind == AnalysisObjectKind.ConstantContextSensitive;
}

public final boolean isConstantObject() {
return this.kind == AnalysisObjectKind.ConstantObject;
public JavaConstant asConstant() {
return null;
}

public ArrayElementsTypeStore getArrayElementsTypeStore() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ public ConstantContextSensitiveObject(PointsToAnalysis bb, AnalysisType type, Ja
bb.profileConstantObject(type);
}

public JavaConstant getConstant() {
@Override
public JavaConstant asConstant() {
return constant;
}

Expand Down Expand Up @@ -128,7 +129,7 @@ public boolean isEmptyObjectArrayConstant(PointsToAnalysis bb) {
return false;
}

return AnalysisObject.isEmptyObjectArrayConstant(bb, getConstant());
return AnalysisObject.isEmptyObjectArrayConstant(bb, asConstant());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
import com.oracle.graal.pointsto.flow.MethodFlowsGraph;
import com.oracle.graal.pointsto.flow.MethodTypeFlow;
import com.oracle.graal.pointsto.flow.TypeFlow;
import com.oracle.graal.pointsto.heap.ImageHeapConstant;
import com.oracle.graal.pointsto.infrastructure.Universe;
import com.oracle.graal.pointsto.meta.AnalysisField;
import com.oracle.graal.pointsto.meta.AnalysisMethod;
Expand All @@ -91,6 +92,7 @@
import com.oracle.graal.pointsto.typestate.TypeState;
import com.oracle.svm.util.ImageBuildStatistics;

import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.JavaMethodProfile;
import jdk.vm.ci.meta.JavaTypeProfile;
Expand All @@ -105,7 +107,7 @@
* processing the graph.
*
* From the single-method view that the compiler has when later compiling the graph, static analysis
* results appear "out of thin air": At some some random point in the graph, we suddenly have a more
* results appear "out of thin air": At some random point in the graph, we suddenly have a more
* precise type (= stamp) for a value. Since many nodes are floating, and even currently fixed nodes
* might float later, we need to be careful that all information coming from the type flow graph
* remains properly anchored to the point where the static analysis actually proved the information.
Expand Down Expand Up @@ -246,20 +248,26 @@ public void simplify(Node n, SimplifierTool tool) {
if (n instanceof ParameterNode) {
ParameterNode node = (ParameterNode) n;
StartNode anchorPoint = graph.start();
Stamp newStamp = strengthenStampFromTypeFlow(node, parameterFlows[node.index()], anchorPoint, tool);
updateStampUsingPiNode(node, newStamp, anchorPoint, tool);
Object newStampOrConstant = strengthenStampFromTypeFlow(node, parameterFlows[node.index()], anchorPoint, tool);
updateStampUsingPiNode(node, newStampOrConstant, anchorPoint, tool);

} else if (n instanceof LoadFieldNode || n instanceof LoadIndexedNode) {
FixedWithNextNode node = (FixedWithNextNode) n;
Stamp newStamp = strengthenStampFromTypeFlow(node, getNodeFlow(node), node, tool);
Object newStampOrConstant = strengthenStampFromTypeFlow(node, getNodeFlow(node), node, tool);
/*
* Even though the memory load will be a floating node later, we can update the
* stamp directly because the type information maintained by the static analysis
* about memory is not flow-sensitive and not context-sensitive. If we ever revive a
* context-sensitive analysis, we will need to change this. But for now, we are
* fine.
*/
updateStampInPlace(node, newStamp, tool);
if (newStampOrConstant instanceof JavaConstant) {
ConstantNode replacement = ConstantNode.forConstant((JavaConstant) newStampOrConstant, bb.getMetaAccess(), graph);
graph.replaceFixedWithFloating(node, replacement);
tool.addToWorkList(replacement);
} else {
updateStampInPlace(node, (Stamp) newStampOrConstant, tool);
}

} else if (n instanceof Invoke) {
Invoke invoke = (Invoke) n;
Expand Down Expand Up @@ -385,13 +393,13 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) {
NodeInputList<ValueNode> arguments = callTarget.arguments();
for (int i = 0; i < arguments.size(); i++) {
ValueNode argument = arguments.get(i);
Stamp newStamp = strengthenStampFromTypeFlow(argument, invokeFlow.getActualParameters()[i], beforeInvoke, tool);
Object newStampOrConstant = strengthenStampFromTypeFlow(argument, invokeFlow.getActualParameters()[i], beforeInvoke, tool);
if (node.isDeleted()) {
/* Parameter stamp was empty, so invoke is unreachable. */
return;
} else if (newStamp != null) {
PiNode pi = insertPi(argument, newStamp, beforeInvoke);
if (pi != null) {
} else if (newStampOrConstant != null) {
ValueNode pi = insertPi(argument, newStampOrConstant, beforeInvoke);
if (pi != null && pi != argument) {
callTarget.replaceAllInputs(argument, pi);
}
}
Expand Down Expand Up @@ -423,8 +431,8 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) {
optimizeReturnedParameter(callees, arguments, node, tool);

FixedWithNextNode anchorPointAfterInvoke = (FixedWithNextNode) (invoke instanceof InvokeWithExceptionNode ? invoke.next() : invoke);
Stamp newStamp = strengthenStampFromTypeFlow(node, invokeFlow.getResult(), anchorPointAfterInvoke, tool);
updateStampUsingPiNode(node, newStamp, anchorPointAfterInvoke, tool);
Object newStampOrConstant = strengthenStampFromTypeFlow(node, invokeFlow.getResult(), anchorPointAfterInvoke, tool);
updateStampUsingPiNode(node, newStampOrConstant, anchorPointAfterInvoke, tool);
}

/**
Expand Down Expand Up @@ -519,19 +527,22 @@ private void updateStampInPlace(ValueNode node, Stamp newStamp, SimplifierTool t
}
}

private void updateStampUsingPiNode(ValueNode node, Stamp newStamp, FixedWithNextNode anchorPoint, SimplifierTool tool) {
if (newStamp != null && node.hasUsages() && !createdPiNodes.isMarked(node)) {
PiNode pi = insertPi(node, newStamp, anchorPoint);
private void updateStampUsingPiNode(ValueNode node, Object newStampOrConstant, FixedWithNextNode anchorPoint, SimplifierTool tool) {
if (newStampOrConstant != null && node.hasUsages() && !createdPiNodes.isMarked(node)) {
ValueNode pi = insertPi(node, newStampOrConstant, anchorPoint);
if (pi != null) {
/*
* The Canonicalizer that drives all of our node processing is iterative. We
* only want to insert the PiNode the first time we handle a node.
*/
createdPiNodes.mark(node);

FrameState anchorState = node instanceof StateSplit ? ((StateSplit) node).stateAfter() : graph.start().stateAfter();
node.replaceAtUsages(pi, usage -> usage != pi && usage != anchorState);

if (pi.isConstant()) {
node.replaceAtUsages(pi);
} else {
FrameState anchorState = node instanceof StateSplit ? ((StateSplit) node).stateAfter() : graph.start().stateAfter();
node.replaceAtUsages(pi, usage -> usage != pi && usage != anchorState);
}
tool.addToWorkList(pi.usages());
}
}
Expand All @@ -540,7 +551,17 @@ private void updateStampUsingPiNode(ValueNode node, Stamp newStamp, FixedWithNex
/*
* See comment on {@link StrengthenGraphs} on why anchoring is necessary.
*/
private PiNode insertPi(ValueNode input, Stamp piStamp, FixedWithNextNode anchorPoint) {
private ValueNode insertPi(ValueNode input, Object newStampOrConstant, FixedWithNextNode anchorPoint) {
if (newStampOrConstant instanceof JavaConstant) {
JavaConstant constant = (JavaConstant) newStampOrConstant;
if (input.isConstant()) {
assert input.asConstant().equals(constant);
return null;
}
return ConstantNode.forConstant(constant, bb.getMetaAccess(), graph);
}

Stamp piStamp = (Stamp) newStampOrConstant;
Stamp oldStamp = input.stamp(NodeView.DEFAULT);
Stamp computedStamp = oldStamp.improveWith(piStamp);
if (oldStamp.equals(computedStamp)) {
Expand All @@ -553,7 +574,7 @@ private PiNode insertPi(ValueNode input, Stamp piStamp, FixedWithNextNode anchor
return graph.unique(new PiNode(input, piStamp, anchor));
}

private Stamp strengthenStampFromTypeFlow(ValueNode node, TypeFlow<?> nodeFlow, FixedWithNextNode anchorPoint, SimplifierTool tool) {
private Object strengthenStampFromTypeFlow(ValueNode node, TypeFlow<?> nodeFlow, FixedWithNextNode anchorPoint, SimplifierTool tool) {
PointsToAnalysis pta = getAnalysis();
if (node.getStackKind() != JavaKind.Object) {
return null;
Expand All @@ -571,6 +592,23 @@ private Stamp strengthenStampFromTypeFlow(ValueNode node, TypeFlow<?> nodeFlow,
}

TypeState nodeTypeState = methodFlow.foldTypeFlow(pta, nodeFlow);

if (!nodeTypeState.canBeNull()) {
JavaConstant constantValue = nodeTypeState.asConstant();
if (constantValue instanceof ImageHeapConstant) {
/*
* GR-42996: until the AOT compilation can properly constant fold also
* ImageHeapConstant, we unwrap the ImageHeapConstant to the hosted object. This
* also means we do not constant fold yet when the constant does not wrap a
* hosted object.
*/
constantValue = ((ImageHeapConstant) constantValue).getHostedObject();
}
if (constantValue != null) {
return constantValue;
}
}

node.inferStamp();
ObjectStamp oldStamp = (ObjectStamp) node.stamp(NodeView.DEFAULT);
AnalysisType oldType = (AnalysisType) oldStamp.type();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,8 @@ public void noteMerge(PointsToAnalysis bb) {
}

@Override
public boolean isConstant() {
return true;
public JavaConstant asConstant() {
return constant;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -632,7 +632,7 @@ public static String asString(TypeState s) {
return "<Null>";
}

String sKind = s.isAllocation() ? "Alloc" : s.isConstant() ? "Const" : s instanceof SingleTypeState ? "Single" : s instanceof MultiTypeState ? "Multi" : "";
String sKind = s.isAllocation() ? "Alloc" : s.asConstant() != null ? "Const" : s instanceof SingleTypeState ? "Single" : s instanceof MultiTypeState ? "Multi" : "";
String sSizeOrType = s instanceof MultiTypeState ? s.typesCount() + "" : s.exactType().toJavaName(false);
int objectsNumber = s.objectsCount();
String canBeNull = s.canBeNull() ? "null" : "!null";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,16 @@ public boolean isAllocation() {
return false;
}

public boolean isConstant() {
return false;
/**
* Returns a non-null value when this type state represents a single constant value, or null if
* this type state is not a single constant.
*
* Note that the {@link #canBeNull()} flag still applies when a constant is returned. A type
* state that is a "constant or null" both returns a non-null result for {@link #asConstant()}}
* and true for {@link #canBeNull()}.
*/
public JavaConstant asConstant() {
return null;
}

public boolean isEmpty() {
Expand Down

0 comments on commit c7c4bf5

Please sign in to comment.