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 */ 23 package org.graalvm.compiler.phases.common; 24 25 import java.util.List; 26 27 import org.graalvm.compiler.debug.Debug; 28 import org.graalvm.compiler.debug.DebugCounter; 29 import org.graalvm.compiler.graph.Node; 30 import org.graalvm.compiler.nodeinfo.InputType; 31 import org.graalvm.compiler.nodes.AbstractBeginNode; 32 import org.graalvm.compiler.nodes.AbstractDeoptimizeNode; 33 import org.graalvm.compiler.nodes.AbstractEndNode; 34 import org.graalvm.compiler.nodes.AbstractMergeNode; 35 import org.graalvm.compiler.nodes.BeginNode; 36 import org.graalvm.compiler.nodes.DeoptimizeNode; 37 import org.graalvm.compiler.nodes.DynamicDeoptimizeNode; 38 import org.graalvm.compiler.nodes.FixedNode; 39 import org.graalvm.compiler.nodes.IfNode; 40 import org.graalvm.compiler.nodes.LogicNode; 41 import org.graalvm.compiler.nodes.StructuredGraph; 42 import org.graalvm.compiler.nodes.ValueNode; 43 import org.graalvm.compiler.nodes.ValuePhiNode; 44 import org.graalvm.compiler.nodes.calc.IsNullNode; 45 import org.graalvm.compiler.nodes.extended.NullCheckNode; 46 import org.graalvm.compiler.nodes.util.GraphUtil; 47 import org.graalvm.compiler.phases.BasePhase; 48 import org.graalvm.compiler.phases.tiers.LowTierContext; 49 50 import jdk.vm.ci.meta.DeoptimizationReason; 51 import jdk.vm.ci.meta.JavaConstant; 52 import jdk.vm.ci.meta.MetaAccessProvider; 53 54 public 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 }