src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java
changeset 55509 d58442b8abc1
parent 54724 62f373a53296
child 57537 ecc6e394475f
--- a/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java	Thu Jun 27 03:10:52 2019 +0200
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/ConditionalEliminationPhase.java	Thu Jun 27 03:33:44 2019 +0200
@@ -56,6 +56,7 @@
 import org.graalvm.compiler.nodes.AbstractMergeNode;
 import org.graalvm.compiler.nodes.BinaryOpLogicNode;
 import org.graalvm.compiler.nodes.ConditionAnchorNode;
+import org.graalvm.compiler.nodes.DeoptimizeNode;
 import org.graalvm.compiler.nodes.DeoptimizingGuard;
 import org.graalvm.compiler.nodes.EndNode;
 import org.graalvm.compiler.nodes.FixedGuardNode;
@@ -90,20 +91,20 @@
 import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
 import org.graalvm.compiler.nodes.java.InstanceOfNode;
 import org.graalvm.compiler.nodes.java.TypeSwitchNode;
+import org.graalvm.compiler.nodes.spi.CoreProviders;
 import org.graalvm.compiler.nodes.spi.NodeWithState;
 import org.graalvm.compiler.nodes.spi.StampInverter;
 import org.graalvm.compiler.nodes.util.GraphUtil;
 import org.graalvm.compiler.phases.BasePhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase;
 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
-import org.graalvm.compiler.phases.tiers.PhaseContext;
 
 import jdk.vm.ci.meta.DeoptimizationAction;
 import jdk.vm.ci.meta.JavaConstant;
 import jdk.vm.ci.meta.SpeculationLog.Speculation;
 import jdk.vm.ci.meta.TriState;
 
-public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
+public class ConditionalEliminationPhase extends BasePhase<CoreProviders> {
 
     private static final CounterKey counterStampsRegistered = DebugContext.counter("StampsRegistered");
     private static final CounterKey counterStampsFound = DebugContext.counter("StampsFound");
@@ -123,7 +124,7 @@
 
     @Override
     @SuppressWarnings("try")
-    protected void run(StructuredGraph graph, PhaseContext context) {
+    protected void run(StructuredGraph graph, CoreProviders context) {
         try (DebugContext.Scope s = graph.getDebug().scope("DominatorConditionalElimination")) {
             BlockMap<List<Node>> blockToNodes = null;
             NodeMap<Block> nodeToBlock = null;
@@ -154,7 +155,7 @@
     }
 
     protected ControlFlowGraph.RecursiveVisitor<?> createVisitor(StructuredGraph graph, @SuppressWarnings("unused") ControlFlowGraph cfg, BlockMap<List<Node>> blockToNodes,
-                    NodeMap<Block> nodeToBlock, PhaseContext context) {
+                    NodeMap<Block> nodeToBlock, CoreProviders context) {
         return new Instance(graph, blockToNodes, nodeToBlock, context);
     }
 
@@ -302,7 +303,7 @@
          */
         private Deque<DeoptimizingGuard> pendingTests;
 
-        public Instance(StructuredGraph graph, BlockMap<List<Node>> blockToNodes, NodeMap<Block> nodeToBlock, PhaseContext context) {
+        public Instance(StructuredGraph graph, BlockMap<List<Node>> blockToNodes, NodeMap<Block> nodeToBlock, CoreProviders context) {
             this.graph = graph;
             this.debug = graph.getDebug();
             this.blockToNodes = blockToNodes;
@@ -335,19 +336,26 @@
             if (!tryProveGuardCondition(node, node.getCondition(), (guard, result, guardedValueStamp, newInput) -> {
                 if (result != node.isNegated()) {
                     node.replaceAndDelete(guard.asNode());
+                    if (guard instanceof DeoptimizingGuard && !((DeoptimizingGuard) guard).isNegated()) {
+                        rebuildPiNodes((DeoptimizingGuard) guard);
+                    }
                 } else {
-                    /*
-                     * Don't kill this branch immediately because `killCFG` can have complex
-                     * implications in the presence of loops: it might replace or delete nodes in
-                     * other branches or even above the kill point. Instead of killing immediately,
-                     * just leave the graph in a state that is easy to simplify by a subsequent
-                     * canonicalizer phase.
-                     */
-                    FixedGuardNode deopt = new FixedGuardNode(LogicConstantNode.forBoolean(result, node.graph()), node.getReason(), node.getAction(), node.getSpeculation(), node.isNegated(),
-                                    node.getNodeSourcePosition());
                     AbstractBeginNode beginNode = (AbstractBeginNode) node.getAnchor();
-                    graph.addAfterFixed(beginNode, node.graph().add(deopt));
 
+                    if (beginNode.next() instanceof DeoptimizeNode) {
+                        // This branch is already dead.
+                    } else {
+                        /*
+                         * Don't kill this branch immediately because `killCFG` can have complex
+                         * implications in the presence of loops: it might replace or delete nodes
+                         * in other branches or even above the kill point. Instead of killing
+                         * immediately, just leave the graph in a state that is easy to simplify by
+                         * a subsequent canonicalizer phase.
+                         */
+                        FixedGuardNode deopt = new FixedGuardNode(LogicConstantNode.forBoolean(result, node.graph()), node.getReason(), node.getAction(), node.getSpeculation(), node.isNegated(),
+                                        node.getNodeSourcePosition());
+                        graph.addAfterFixed(beginNode, node.graph().add(deopt));
+                    }
                 }
                 return true;
             })) {
@@ -361,41 +369,14 @@
                     node.replaceAtUsages(guard.asNode());
                     GraphUtil.unlinkFixedNode(node);
                     GraphUtil.killWithUnusedFloatingInputs(node);
+                    if (guard instanceof DeoptimizingGuard && !((DeoptimizingGuard) guard).isNegated()) {
+                        rebuildPiNodes((DeoptimizingGuard) guard);
+                    }
                 } else {
                     node.setCondition(LogicConstantNode.forBoolean(result, node.graph()), node.isNegated());
                     // Don't kill this branch immediately, see `processGuard`.
                 }
 
-                if (guard instanceof DeoptimizingGuard && !node.isNegated() && !((DeoptimizingGuard) guard).isNegated()) {
-                    LogicNode newCondition = ((DeoptimizingGuard) guard.asNode()).getCondition();
-                    if (newCondition instanceof InstanceOfNode) {
-                        InstanceOfNode inst = (InstanceOfNode) newCondition;
-                        ValueNode originalValue = GraphUtil.skipPi(inst.getValue());
-                        PiNode pi = null;
-                        // Ensure that any Pi that's weaker than what the instanceof proves is
-                        // replaced by one derived from the instanceof itself.
-                        for (PiNode existing : guard.asNode().usages().filter(PiNode.class).snapshot()) {
-                            if (!existing.isAlive()) {
-                                continue;
-                            }
-                            if (originalValue != GraphUtil.skipPi(existing.object())) {
-                                // Somehow these are unrelated values so leave it alone
-                                continue;
-                            }
-                            // If the pi has a weaker stamp or the same stamp but a different input
-                            // then replace it.
-                            boolean strongerStamp = !existing.piStamp().join(inst.getCheckedStamp()).equals(inst.getCheckedStamp());
-                            boolean differentStamp = !existing.piStamp().equals(inst.getCheckedStamp());
-                            boolean differentObject = existing.object() != inst.getValue();
-                            if (!strongerStamp && (differentStamp || differentObject)) {
-                                if (pi == null) {
-                                    pi = graph.unique(new PiNode(inst.getValue(), inst.getCheckedStamp(), (ValueNode) guard));
-                                }
-                                existing.replaceAndDelete(pi);
-                            }
-                        }
-                    }
-                }
                 debug.log("Kill fixed guard %s", node);
                 return true;
             })) {
@@ -403,6 +384,59 @@
             }
         }
 
+        private void rebuildPiNodes(DeoptimizingGuard guard) {
+            LogicNode newCondition = guard.getCondition();
+            if (newCondition instanceof InstanceOfNode) {
+                InstanceOfNode inst = (InstanceOfNode) newCondition;
+                ValueNode originalValue = GraphUtil.skipPi(inst.getValue());
+                PiNode pi = null;
+                // Ensure that any Pi that's weaker than what the instanceof proves is
+                // replaced by one derived from the instanceof itself.
+                for (PiNode existing : guard.asNode().usages().filter(PiNode.class).snapshot()) {
+                    if (!existing.isAlive()) {
+                        continue;
+                    }
+                    if (originalValue != GraphUtil.skipPi(existing.object())) {
+                        // Somehow these are unrelated values so leave it alone
+                        continue;
+                    }
+                    // If the pi has a weaker stamp or the same stamp but a different input
+                    // then replace it.
+                    boolean strongerStamp = !existing.piStamp().join(inst.getCheckedStamp()).equals(inst.getCheckedStamp());
+                    boolean differentCheckedStamp = !existing.piStamp().equals(inst.getCheckedStamp());
+                    boolean differentObject = existing.object() != inst.getValue();
+                    if (!strongerStamp && (differentCheckedStamp || differentObject)) {
+                        if (pi == null) {
+                            pi = graph.unique(new PiNode(inst.getValue(), inst.getCheckedStamp(), (ValueNode) guard));
+                        }
+                        if (!pi.stamp(NodeView.DEFAULT).join(existing.stamp(NodeView.DEFAULT)).equals(pi.stamp(NodeView.DEFAULT))) {
+                            /*
+                             * With a code sequence like null check, type check, null check of type
+                             * checked value, CE will use the first null check to prove the second
+                             * null check so the graph ends up a Pi guarded by the first null check
+                             * but consuming the output Pi from the type check check. In this case
+                             * we should still canonicalize the checked stamp for consistency.
+                             */
+                            if (differentCheckedStamp) {
+                                PiNode alternatePi = graph.unique(new PiNode(existing.object(), inst.getCheckedStamp(), (ValueNode) guard));
+                                /*
+                                 * If the resulting stamp is as good or better then do the
+                                 * replacement. However when interface types are involved it's
+                                 * possible that improving the checked stamp merges types which
+                                 * appear unrelated so there's we must skip the replacement.
+                                 */
+                                if (alternatePi.stamp(NodeView.DEFAULT).join(existing.stamp(NodeView.DEFAULT)).equals(alternatePi.stamp(NodeView.DEFAULT))) {
+                                    existing.replaceAndDelete(alternatePi);
+                                }
+                            }
+                            continue;
+                        }
+                        existing.replaceAndDelete(pi);
+                    }
+                }
+            }
+        }
+
         protected void processIf(IfNode node) {
             tryProveCondition(node.condition(), (guard, result, guardedValueStamp, newInput) -> {
                 node.setCondition(LogicConstantNode.forBoolean(result, node.graph()));