From c7c4bf5b1b5244d396a4c49b520f5a22cb68e5ce Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Fri, 9 Dec 2022 15:45:49 -0800 Subject: [PATCH] Strengthen graphs with constants propagated through static analysis --- .../ContextSensitiveSingleTypeState.java | 14 +++- .../flow/context/object/AnalysisObject.java | 8 +- .../ConstantContextSensitiveObject.java | 5 +- .../pointsto/results/StrengthenGraphs.java | 76 ++++++++++++++----- .../pointsto/typestate/ConstantTypeState.java | 4 +- .../pointsto/typestate/PointsToStats.java | 2 +- .../graal/pointsto/typestate/TypeState.java | 12 ++- 7 files changed, 87 insertions(+), 34 deletions(-) diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/bytecode/ContextSensitiveSingleTypeState.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/bytecode/ContextSensitiveSingleTypeState.java index d6addaafbe95..fff16555d125 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/bytecode/ContextSensitiveSingleTypeState.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/bytecode/ContextSensitiveSingleTypeState.java @@ -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; @@ -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 diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java index 0d97c8456785..3caef40f1e5b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java @@ -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() { diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ConstantContextSensitiveObject.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ConstantContextSensitiveObject.java index d4a5e93abc2c..89f6b8d9e820 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ConstantContextSensitiveObject.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ConstantContextSensitiveObject.java @@ -79,7 +79,8 @@ public ConstantContextSensitiveObject(PointsToAnalysis bb, AnalysisType type, Ja bb.profileConstantObject(type); } - public JavaConstant getConstant() { + @Override + public JavaConstant asConstant() { return constant; } @@ -128,7 +129,7 @@ public boolean isEmptyObjectArrayConstant(PointsToAnalysis bb) { return false; } - return AnalysisObject.isEmptyObjectArrayConstant(bb, getConstant()); + return AnalysisObject.isEmptyObjectArrayConstant(bb, asConstant()); } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java index 315daf8a4ef4..50fb336c9cf0 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/results/StrengthenGraphs.java @@ -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; @@ -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; @@ -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. @@ -246,12 +248,12 @@ 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 @@ -259,7 +261,13 @@ public void simplify(Node n, SimplifierTool tool) { * 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; @@ -385,13 +393,13 @@ private void handleInvoke(Invoke invoke, SimplifierTool tool) { NodeInputList 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); } } @@ -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); } /** @@ -519,9 +527,9 @@ 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 @@ -529,9 +537,12 @@ private void updateStampUsingPiNode(ValueNode node, Stamp newStamp, FixedWithNex */ 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()); } } @@ -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)) { @@ -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; @@ -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(); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/ConstantTypeState.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/ConstantTypeState.java index ec634493bc31..953820221765 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/ConstantTypeState.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/ConstantTypeState.java @@ -100,8 +100,8 @@ public void noteMerge(PointsToAnalysis bb) { } @Override - public boolean isConstant() { - return true; + public JavaConstant asConstant() { + return constant; } @Override diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/PointsToStats.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/PointsToStats.java index 8c0cda1e01d8..bcb3bfa195cf 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/PointsToStats.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/PointsToStats.java @@ -632,7 +632,7 @@ public static String asString(TypeState s) { return ""; } - 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"; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java index fb89b57f405a..1358b96cab1b 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/typestate/TypeState.java @@ -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() {