Skip to content

Commit c7c4bf5

Browse files
author
Christian Wimmer
committed
Strengthen graphs with constants propagated through static analysis
1 parent 971dd13 commit c7c4bf5

File tree

7 files changed

+87
-34
lines changed

7 files changed

+87
-34
lines changed

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@
3636
import com.oracle.graal.pointsto.typestate.SingleTypeState;
3737
import com.oracle.graal.pointsto.typestate.TypeState;
3838

39+
import jdk.vm.ci.meta.JavaConstant;
40+
3941
public class ContextSensitiveSingleTypeState extends SingleTypeState {
4042
/** The objects of this type state. */
4143
protected final AnalysisObject[] objects;
@@ -145,8 +147,16 @@ public boolean isAllocation() {
145147
}
146148

147149
@Override
148-
public boolean isConstant() {
149-
return objects[0].isConstantContextSensitiveObject();
150+
public JavaConstant asConstant() {
151+
JavaConstant result = null;
152+
for (AnalysisObject object : objects) {
153+
JavaConstant objectConstant = object.asConstant();
154+
if (objectConstant == null || (result != null && !result.equals(objectConstant))) {
155+
return null;
156+
}
157+
result = objectConstant;
158+
}
159+
return result;
150160
}
151161

152162
@Override

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -179,12 +179,8 @@ public final boolean isAllocationContextSensitiveObject() {
179179
return this.kind == AnalysisObjectKind.AllocationContextSensitive;
180180
}
181181

182-
public final boolean isConstantContextSensitiveObject() {
183-
return this.kind == AnalysisObjectKind.ConstantContextSensitive;
184-
}
185-
186-
public final boolean isConstantObject() {
187-
return this.kind == AnalysisObjectKind.ConstantObject;
182+
public JavaConstant asConstant() {
183+
return null;
188184
}
189185

190186
public ArrayElementsTypeStore getArrayElementsTypeStore() {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ public ConstantContextSensitiveObject(PointsToAnalysis bb, AnalysisType type, Ja
7979
bb.profileConstantObject(type);
8080
}
8181

82-
public JavaConstant getConstant() {
82+
@Override
83+
public JavaConstant asConstant() {
8384
return constant;
8485
}
8586

@@ -128,7 +129,7 @@ public boolean isEmptyObjectArrayConstant(PointsToAnalysis bb) {
128129
return false;
129130
}
130131

131-
return AnalysisObject.isEmptyObjectArrayConstant(bb, getConstant());
132+
return AnalysisObject.isEmptyObjectArrayConstant(bb, asConstant());
132133
}
133134

134135
@Override

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java

Lines changed: 57 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
import com.oracle.graal.pointsto.flow.MethodFlowsGraph;
8484
import com.oracle.graal.pointsto.flow.MethodTypeFlow;
8585
import com.oracle.graal.pointsto.flow.TypeFlow;
86+
import com.oracle.graal.pointsto.heap.ImageHeapConstant;
8687
import com.oracle.graal.pointsto.infrastructure.Universe;
8788
import com.oracle.graal.pointsto.meta.AnalysisField;
8889
import com.oracle.graal.pointsto.meta.AnalysisMethod;
@@ -91,6 +92,7 @@
9192
import com.oracle.graal.pointsto.typestate.TypeState;
9293
import com.oracle.svm.util.ImageBuildStatistics;
9394

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

252254
} else if (n instanceof LoadFieldNode || n instanceof LoadIndexedNode) {
253255
FixedWithNextNode node = (FixedWithNextNode) n;
254-
Stamp newStamp = strengthenStampFromTypeFlow(node, getNodeFlow(node), node, tool);
256+
Object newStampOrConstant = strengthenStampFromTypeFlow(node, getNodeFlow(node), node, tool);
255257
/*
256258
* Even though the memory load will be a floating node later, we can update the
257259
* stamp directly because the type information maintained by the static analysis
258260
* about memory is not flow-sensitive and not context-sensitive. If we ever revive a
259261
* context-sensitive analysis, we will need to change this. But for now, we are
260262
* fine.
261263
*/
262-
updateStampInPlace(node, newStamp, tool);
264+
if (newStampOrConstant instanceof JavaConstant) {
265+
ConstantNode replacement = ConstantNode.forConstant((JavaConstant) newStampOrConstant, bb.getMetaAccess(), graph);
266+
graph.replaceFixedWithFloating(node, replacement);
267+
tool.addToWorkList(replacement);
268+
} else {
269+
updateStampInPlace(node, (Stamp) newStampOrConstant, tool);
270+
}
263271

264272
} else if (n instanceof Invoke) {
265273
Invoke invoke = (Invoke) n;
@@ -385,13 +393,13 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) {
385393
NodeInputList<ValueNode> arguments = callTarget.arguments();
386394
for (int i = 0; i < arguments.size(); i++) {
387395
ValueNode argument = arguments.get(i);
388-
Stamp newStamp = strengthenStampFromTypeFlow(argument, invokeFlow.getActualParameters()[i], beforeInvoke, tool);
396+
Object newStampOrConstant = strengthenStampFromTypeFlow(argument, invokeFlow.getActualParameters()[i], beforeInvoke, tool);
389397
if (node.isDeleted()) {
390398
/* Parameter stamp was empty, so invoke is unreachable. */
391399
return;
392-
} else if (newStamp != null) {
393-
PiNode pi = insertPi(argument, newStamp, beforeInvoke);
394-
if (pi != null) {
400+
} else if (newStampOrConstant != null) {
401+
ValueNode pi = insertPi(argument, newStampOrConstant, beforeInvoke);
402+
if (pi != null && pi != argument) {
395403
callTarget.replaceAllInputs(argument, pi);
396404
}
397405
}
@@ -423,8 +431,8 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) {
423431
optimizeReturnedParameter(callees, arguments, node, tool);
424432

425433
FixedWithNextNode anchorPointAfterInvoke = (FixedWithNextNode) (invoke instanceof InvokeWithExceptionNode ? invoke.next() : invoke);
426-
Stamp newStamp = strengthenStampFromTypeFlow(node, invokeFlow.getResult(), anchorPointAfterInvoke, tool);
427-
updateStampUsingPiNode(node, newStamp, anchorPointAfterInvoke, tool);
434+
Object newStampOrConstant = strengthenStampFromTypeFlow(node, invokeFlow.getResult(), anchorPointAfterInvoke, tool);
435+
updateStampUsingPiNode(node, newStampOrConstant, anchorPointAfterInvoke, tool);
428436
}
429437

430438
/**
@@ -519,19 +527,22 @@ private void updateStampInPlace(ValueNode node, Stamp newStamp, SimplifierTool t
519527
}
520528
}
521529

522-
private void updateStampUsingPiNode(ValueNode node, Stamp newStamp, FixedWithNextNode anchorPoint, SimplifierTool tool) {
523-
if (newStamp != null && node.hasUsages() && !createdPiNodes.isMarked(node)) {
524-
PiNode pi = insertPi(node, newStamp, anchorPoint);
530+
private void updateStampUsingPiNode(ValueNode node, Object newStampOrConstant, FixedWithNextNode anchorPoint, SimplifierTool tool) {
531+
if (newStampOrConstant != null && node.hasUsages() && !createdPiNodes.isMarked(node)) {
532+
ValueNode pi = insertPi(node, newStampOrConstant, anchorPoint);
525533
if (pi != null) {
526534
/*
527535
* The Canonicalizer that drives all of our node processing is iterative. We
528536
* only want to insert the PiNode the first time we handle a node.
529537
*/
530538
createdPiNodes.mark(node);
531539

532-
FrameState anchorState = node instanceof StateSplit ? ((StateSplit) node).stateAfter() : graph.start().stateAfter();
533-
node.replaceAtUsages(pi, usage -> usage != pi && usage != anchorState);
534-
540+
if (pi.isConstant()) {
541+
node.replaceAtUsages(pi);
542+
} else {
543+
FrameState anchorState = node instanceof StateSplit ? ((StateSplit) node).stateAfter() : graph.start().stateAfter();
544+
node.replaceAtUsages(pi, usage -> usage != pi && usage != anchorState);
545+
}
535546
tool.addToWorkList(pi.usages());
536547
}
537548
}
@@ -540,7 +551,17 @@ private void updateStampUsingPiNode(ValueNode node, Stamp newStamp, FixedWithNex
540551
/*
541552
* See comment on {@link StrengthenGraphs} on why anchoring is necessary.
542553
*/
543-
private PiNode insertPi(ValueNode input, Stamp piStamp, FixedWithNextNode anchorPoint) {
554+
private ValueNode insertPi(ValueNode input, Object newStampOrConstant, FixedWithNextNode anchorPoint) {
555+
if (newStampOrConstant instanceof JavaConstant) {
556+
JavaConstant constant = (JavaConstant) newStampOrConstant;
557+
if (input.isConstant()) {
558+
assert input.asConstant().equals(constant);
559+
return null;
560+
}
561+
return ConstantNode.forConstant(constant, bb.getMetaAccess(), graph);
562+
}
563+
564+
Stamp piStamp = (Stamp) newStampOrConstant;
544565
Stamp oldStamp = input.stamp(NodeView.DEFAULT);
545566
Stamp computedStamp = oldStamp.improveWith(piStamp);
546567
if (oldStamp.equals(computedStamp)) {
@@ -553,7 +574,7 @@ private PiNode insertPi(ValueNode input, Stamp piStamp, FixedWithNextNode anchor
553574
return graph.unique(new PiNode(input, piStamp, anchor));
554575
}
555576

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

573594
TypeState nodeTypeState = methodFlow.foldTypeFlow(pta, nodeFlow);
595+
596+
if (!nodeTypeState.canBeNull()) {
597+
JavaConstant constantValue = nodeTypeState.asConstant();
598+
if (constantValue instanceof ImageHeapConstant) {
599+
/*
600+
* GR-42996: until the AOT compilation can properly constant fold also
601+
* ImageHeapConstant, we unwrap the ImageHeapConstant to the hosted object. This
602+
* also means we do not constant fold yet when the constant does not wrap a
603+
* hosted object.
604+
*/
605+
constantValue = ((ImageHeapConstant) constantValue).getHostedObject();
606+
}
607+
if (constantValue != null) {
608+
return constantValue;
609+
}
610+
}
611+
574612
node.inferStamp();
575613
ObjectStamp oldStamp = (ObjectStamp) node.stamp(NodeView.DEFAULT);
576614
AnalysisType oldType = (AnalysisType) oldStamp.type();

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/ConstantTypeState.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ public void noteMerge(PointsToAnalysis bb) {
100100
}
101101

102102
@Override
103-
public boolean isConstant() {
104-
return true;
103+
public JavaConstant asConstant() {
104+
return constant;
105105
}
106106

107107
@Override

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/PointsToStats.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ public static String asString(TypeState s) {
632632
return "<Null>";
633633
}
634634

635-
String sKind = s.isAllocation() ? "Alloc" : s.isConstant() ? "Const" : s instanceof SingleTypeState ? "Single" : s instanceof MultiTypeState ? "Multi" : "";
635+
String sKind = s.isAllocation() ? "Alloc" : s.asConstant() != null ? "Const" : s instanceof SingleTypeState ? "Single" : s instanceof MultiTypeState ? "Multi" : "";
636636
String sSizeOrType = s instanceof MultiTypeState ? s.typesCount() + "" : s.exactType().toJavaName(false);
637637
int objectsNumber = s.objectsCount();
638638
String canBeNull = s.canBeNull() ? "null" : "!null";

substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,16 @@ public boolean isAllocation() {
115115
return false;
116116
}
117117

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

122130
public boolean isEmpty() {

0 commit comments

Comments
 (0)