# HG changeset patch # User Tom Rodriguez # Date 1400209876 25200 # Node ID eaeba148bb1537bfc072bc5bb2765df01812908c # Parent e563b7668db578b2003037b5741a4361fbf7cb55 more aggressively fold implicit nulls into memory operations diff -r e563b7668db5 -r eaeba148bb15 graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/NullCheckNode.java --- 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; diff -r e563b7668db5 -r eaeba148bb15 graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/UseTrappingNullChecksPhase.java --- 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 { + 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 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);