changeset 15698:eaeba148bb15

more aggressively fold implicit nulls into memory operations
author Tom Rodriguez <tom.rodriguez@oracle.com>
date Thu, 15 May 2014 20:11:16 -0700
parents e563b7668db5
children 423bf61c9c32
files graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java
diffstat 2 files changed, 80 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java	Thu May 15 23:12:48 2014 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java	Thu May 15 20:11:16 2014 -0700
@@ -23,10 +23,12 @@
 package com.oracle.graal.nodes.extended;
 
 import com.oracle.graal.compiler.common.type.*;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 
-public class NullCheckNode extends DeoptimizingFixedWithNextNode implements LIRLowerable {
+@NodeInfo(allowedUsageTypes = {InputType.Guard})
+public class NullCheckNode extends DeoptimizingFixedWithNextNode implements LIRLowerable, GuardingNode {
 
     @Input private ValueNode object;
 
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java	Thu May 15 23:12:48 2014 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java	Thu May 15 20:11:16 2014 -0700
@@ -22,7 +22,10 @@
  */
 package com.oracle.graal.phases.common;
 
+import java.util.*;
+
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.GuardsStage;
@@ -34,6 +37,10 @@
 
 public class UseTrappingNullChecksPhase extends BasePhase<LowTierContext> {
 
+    private static final DebugMetric metricTrappingNullCheck = Debug.metric("TrappingNullCheck");
+    private static final DebugMetric metricTrappingNullCheckUnreached = Debug.metric("TrappingNullCheckUnreached");
+    private static final DebugMetric metricTrappingNullCheckDynamicDeoptimize = Debug.metric("TrappingNullCheckDynamicDeoptimize");
+
     @Override
     protected void run(StructuredGraph graph, LowTierContext context) {
         if (context.getTarget().implicitNullCheckLimit <= 0) {
@@ -42,36 +49,82 @@
         assert graph.getGuardsStage().ordinal() >= GuardsStage.AFTER_FSA.ordinal();
 
         for (DeoptimizeNode deopt : graph.getNodes(DeoptimizeNode.class)) {
-            tryUseTrappingNullCheck(deopt);
+            tryUseTrappingNullCheck(deopt, deopt.predecessor(), deopt.reason(), deopt.getSpeculation());
+        }
+        for (DynamicDeoptimizeNode deopt : graph.getNodes(DynamicDeoptimizeNode.class)) {
+            tryUseTrappingNullCheck(context.getMetaAccess(), deopt);
         }
     }
 
-    private static void tryUseTrappingNullCheck(DeoptimizeNode deopt) {
-        if (deopt.reason() != DeoptimizationReason.NullCheckException) {
-            return;
-        }
-        if (deopt.getSpeculation() != null && !deopt.getSpeculation().equals(Constant.NULL_OBJECT)) {
+    private static void tryUseTrappingNullCheck(MetaAccessProvider metaAccessProvider, DynamicDeoptimizeNode deopt) {
+        ValueNode speculation = deopt.getSpeculation();
+        if (!speculation.isConstant() || !speculation.asConstant().equals(Constant.NULL_OBJECT)) {
             return;
         }
         Node predecessor = deopt.predecessor();
+        assert predecessor instanceof MergeNode;
+        if (predecessor instanceof MergeNode) {
+            MergeNode merge = (MergeNode) predecessor;
+
+            // Process each predecessor at the merge, unpacking the reasons as needed.
+            ValueNode reason = deopt.getActionAndReason();
+            List<ValueNode> values = reason instanceof ValuePhiNode ? ((ValuePhiNode) reason).values().snapshot() : Collections.nCopies(merge.forwardEndCount(), reason);
+
+            int index = 0;
+            for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) {
+                ValueNode thisReason = values.get(index++);
+                assert thisReason.isConstant();
+                DeoptimizationReason deoptimizationReason = metaAccessProvider.decodeDeoptReason(thisReason.asConstant());
+                tryUseTrappingNullCheck(deopt, end.predecessor(), deoptimizationReason, null);
+            }
+        }
+    }
+
+    private static void tryUseTrappingNullCheck(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason, Constant speculation) {
+        if (deoptimizationReason != DeoptimizationReason.NullCheckException && deoptimizationReason != DeoptimizationReason.UnreachedCode) {
+            return;
+        }
+        if (speculation != null && !speculation.equals(Constant.NULL_OBJECT)) {
+            return;
+        }
+        if (predecessor instanceof MergeNode) {
+            MergeNode merge = (MergeNode) predecessor;
+            for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) {
+                checkPredecessor(deopt, end.predecessor(), deoptimizationReason);
+            }
+
+        } else if (predecessor instanceof BeginNode) {
+            checkPredecessor(deopt, predecessor, deoptimizationReason);
+        }
+    }
+
+    private static void checkPredecessor(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason) {
+        Node current = predecessor;
         Node branch = null;
-        while (predecessor instanceof BeginNode) {
-            branch = predecessor;
-            predecessor = predecessor.predecessor();
+        while (current instanceof BeginNode) {
+            branch = current;
+            current = current.predecessor();
         }
-        if (predecessor instanceof IfNode) {
-            IfNode ifNode = (IfNode) predecessor;
+        if (current instanceof IfNode) {
+            IfNode ifNode = (IfNode) current;
             if (branch != ifNode.trueSuccessor()) {
                 return;
             }
             LogicNode condition = ifNode.condition();
             if (condition instanceof IsNullNode) {
-                replaceWithTrappingNullCheck(deopt, ifNode, condition);
+                replaceWithTrappingNullCheck(deopt, ifNode, condition, deoptimizationReason);
             }
         }
     }
 
-    private static void replaceWithTrappingNullCheck(DeoptimizeNode deopt, IfNode ifNode, LogicNode condition) {
+    private static void replaceWithTrappingNullCheck(AbstractDeoptimizeNode deopt, IfNode ifNode, LogicNode condition, DeoptimizationReason deoptimizationReason) {
+        metricTrappingNullCheck.increment();
+        if (deopt instanceof DynamicDeoptimizeNode) {
+            metricTrappingNullCheckDynamicDeoptimize.increment();
+        }
+        if (deoptimizationReason == DeoptimizationReason.UnreachedCode) {
+            metricTrappingNullCheckUnreached.increment();
+        }
         IsNullNode isNullNode = (IsNullNode) condition;
         BeginNode nonTrappingContinuation = ifNode.falseSuccessor();
         BeginNode trappingContinuation = ifNode.trueSuccessor();
@@ -79,6 +132,17 @@
         trappingNullCheck.setStateBefore(deopt.stateBefore());
         deopt.graph().replaceSplit(ifNode, trappingNullCheck, nonTrappingContinuation);
 
+        /*
+         * We now have the pattern NullCheck/BeginNode/... It's possible some node is using the
+         * BeginNode as a guard input, so replace guard users of the Begin with the NullCheck and
+         * then remove the Begin from the graph.
+         */
+        nonTrappingContinuation.replaceAtUsages(InputType.Guard, trappingNullCheck);
+        FixedNode next = nonTrappingContinuation.next();
+        nonTrappingContinuation.clearSuccessors();
+        trappingNullCheck.setNext(next);
+        nonTrappingContinuation.safeDelete();
+
         GraphUtil.killCFG(trappingContinuation);
         if (isNullNode.usages().isEmpty()) {
             GraphUtil.killWithUnusedFloatingInputs(isNullNode);