UseTrappingNullChecksPhase.java revision 12651:6ef01bd40ce2
1/*
2 * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.phases.common;
24
25import java.util.List;
26
27import org.graalvm.compiler.debug.Debug;
28import org.graalvm.compiler.debug.DebugCounter;
29import org.graalvm.compiler.graph.Node;
30import org.graalvm.compiler.nodeinfo.InputType;
31import org.graalvm.compiler.nodes.AbstractBeginNode;
32import org.graalvm.compiler.nodes.AbstractDeoptimizeNode;
33import org.graalvm.compiler.nodes.AbstractEndNode;
34import org.graalvm.compiler.nodes.AbstractMergeNode;
35import org.graalvm.compiler.nodes.BeginNode;
36import org.graalvm.compiler.nodes.DeoptimizeNode;
37import org.graalvm.compiler.nodes.DynamicDeoptimizeNode;
38import org.graalvm.compiler.nodes.FixedNode;
39import org.graalvm.compiler.nodes.IfNode;
40import org.graalvm.compiler.nodes.LogicNode;
41import org.graalvm.compiler.nodes.StructuredGraph;
42import org.graalvm.compiler.nodes.ValueNode;
43import org.graalvm.compiler.nodes.ValuePhiNode;
44import org.graalvm.compiler.nodes.calc.IsNullNode;
45import org.graalvm.compiler.nodes.extended.NullCheckNode;
46import org.graalvm.compiler.nodes.util.GraphUtil;
47import org.graalvm.compiler.phases.BasePhase;
48import org.graalvm.compiler.phases.tiers.LowTierContext;
49
50import jdk.vm.ci.meta.DeoptimizationReason;
51import jdk.vm.ci.meta.JavaConstant;
52import jdk.vm.ci.meta.MetaAccessProvider;
53
54public class UseTrappingNullChecksPhase extends BasePhase<LowTierContext> {
55
56    private static final DebugCounter counterTrappingNullCheck = Debug.counter("TrappingNullCheck");
57    private static final DebugCounter counterTrappingNullCheckUnreached = Debug.counter("TrappingNullCheckUnreached");
58    private static final DebugCounter counterTrappingNullCheckDynamicDeoptimize = Debug.counter("TrappingNullCheckDynamicDeoptimize");
59
60    @Override
61    protected void run(StructuredGraph graph, LowTierContext context) {
62        if (context.getTarget().implicitNullCheckLimit <= 0) {
63            return;
64        }
65        assert graph.getGuardsStage().areFrameStatesAtDeopts();
66
67        for (DeoptimizeNode deopt : graph.getNodes(DeoptimizeNode.TYPE)) {
68            tryUseTrappingNullCheck(deopt, deopt.predecessor(), deopt.reason(), deopt.getSpeculation());
69        }
70        for (DynamicDeoptimizeNode deopt : graph.getNodes(DynamicDeoptimizeNode.TYPE)) {
71            tryUseTrappingNullCheck(context.getMetaAccess(), deopt);
72        }
73    }
74
75    private static void tryUseTrappingNullCheck(MetaAccessProvider metaAccessProvider, DynamicDeoptimizeNode deopt) {
76        Node predecessor = deopt.predecessor();
77        if (predecessor instanceof AbstractMergeNode) {
78            AbstractMergeNode merge = (AbstractMergeNode) predecessor;
79
80            // Process each predecessor at the merge, unpacking the reasons and speculations as
81            // needed.
82            ValueNode reason = deopt.getActionAndReason();
83            ValuePhiNode reasonPhi = null;
84            List<ValueNode> reasons = null;
85            int expectedPhis = 0;
86
87            if (reason instanceof ValuePhiNode) {
88                reasonPhi = (ValuePhiNode) reason;
89                if (reasonPhi.merge() != merge) {
90                    return;
91                }
92                reasons = reasonPhi.values().snapshot();
93                expectedPhis++;
94            } else if (!reason.isConstant()) {
95                return;
96            }
97
98            ValueNode speculation = deopt.getSpeculation();
99            ValuePhiNode speculationPhi = null;
100            List<ValueNode> speculations = null;
101            if (speculation instanceof ValuePhiNode) {
102                speculationPhi = (ValuePhiNode) speculation;
103                if (speculationPhi.merge() != merge) {
104                    return;
105                }
106                speculations = speculationPhi.values().snapshot();
107                expectedPhis++;
108            }
109
110            if (merge.phis().count() != expectedPhis) {
111                return;
112            }
113
114            int index = 0;
115            for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) {
116                ValueNode thisReason = reasons != null ? reasons.get(index) : reason;
117                ValueNode thisSpeculation = speculations != null ? speculations.get(index++) : speculation;
118                if (!thisReason.isConstant() || !thisSpeculation.isConstant() || !thisSpeculation.asConstant().equals(JavaConstant.NULL_POINTER)) {
119                    continue;
120                }
121                DeoptimizationReason deoptimizationReason = metaAccessProvider.decodeDeoptReason(thisReason.asJavaConstant());
122                tryUseTrappingNullCheck(deopt, end.predecessor(), deoptimizationReason, null);
123            }
124        }
125    }
126
127    private static void tryUseTrappingNullCheck(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason, JavaConstant speculation) {
128        if (deoptimizationReason != DeoptimizationReason.NullCheckException && deoptimizationReason != DeoptimizationReason.UnreachedCode) {
129            return;
130        }
131        if (speculation != null && !speculation.equals(JavaConstant.NULL_POINTER)) {
132            return;
133        }
134        if (predecessor instanceof AbstractMergeNode) {
135            AbstractMergeNode merge = (AbstractMergeNode) predecessor;
136            if (merge.phis().isEmpty()) {
137                for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) {
138                    checkPredecessor(deopt, end.predecessor(), deoptimizationReason);
139                }
140            }
141        } else if (predecessor instanceof AbstractBeginNode) {
142            checkPredecessor(deopt, predecessor, deoptimizationReason);
143        }
144    }
145
146    private static void checkPredecessor(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason) {
147        Node current = predecessor;
148        AbstractBeginNode branch = null;
149        while (current instanceof AbstractBeginNode) {
150            branch = (AbstractBeginNode) current;
151            if (branch.anchored().isNotEmpty()) {
152                // some input of the deopt framestate is anchored to this branch
153                return;
154            }
155            current = current.predecessor();
156        }
157        if (current instanceof IfNode) {
158            IfNode ifNode = (IfNode) current;
159            if (branch != ifNode.trueSuccessor()) {
160                return;
161            }
162            LogicNode condition = ifNode.condition();
163            if (condition instanceof IsNullNode) {
164                replaceWithTrappingNullCheck(deopt, ifNode, condition, deoptimizationReason);
165            }
166        }
167    }
168
169    private static void replaceWithTrappingNullCheck(AbstractDeoptimizeNode deopt, IfNode ifNode, LogicNode condition, DeoptimizationReason deoptimizationReason) {
170        counterTrappingNullCheck.increment();
171        if (deopt instanceof DynamicDeoptimizeNode) {
172            counterTrappingNullCheckDynamicDeoptimize.increment();
173        }
174        if (deoptimizationReason == DeoptimizationReason.UnreachedCode) {
175            counterTrappingNullCheckUnreached.increment();
176        }
177        IsNullNode isNullNode = (IsNullNode) condition;
178        AbstractBeginNode nonTrappingContinuation = ifNode.falseSuccessor();
179        AbstractBeginNode trappingContinuation = ifNode.trueSuccessor();
180        NullCheckNode trappingNullCheck = deopt.graph().add(new NullCheckNode(isNullNode.getValue()));
181        trappingNullCheck.setStateBefore(deopt.stateBefore());
182        deopt.graph().replaceSplit(ifNode, trappingNullCheck, nonTrappingContinuation);
183
184        /*
185         * We now have the pattern NullCheck/BeginNode/... It's possible some node is using the
186         * BeginNode as a guard input, so replace guard users of the Begin with the NullCheck and
187         * then remove the Begin from the graph.
188         */
189        nonTrappingContinuation.replaceAtUsages(InputType.Guard, trappingNullCheck);
190        if (nonTrappingContinuation instanceof BeginNode) {
191            FixedNode next = nonTrappingContinuation.next();
192            nonTrappingContinuation.clearSuccessors();
193            trappingNullCheck.setNext(next);
194            nonTrappingContinuation.safeDelete();
195        } else {
196            trappingNullCheck.setNext(nonTrappingContinuation);
197        }
198
199        GraphUtil.killCFG(trappingContinuation);
200        GraphUtil.tryKillUnused(isNullNode);
201    }
202}
203