--- /dev/null 2017-01-22 10:16:57.869617664 -0800 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/UseTrappingNullChecksPhase.java 2017-02-15 17:08:14.248818176 -0800 @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2014, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.phases.common; + +import java.util.List; + +import org.graalvm.compiler.debug.Debug; +import org.graalvm.compiler.debug.DebugCounter; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.nodeinfo.InputType; +import org.graalvm.compiler.nodes.AbstractBeginNode; +import org.graalvm.compiler.nodes.AbstractDeoptimizeNode; +import org.graalvm.compiler.nodes.AbstractEndNode; +import org.graalvm.compiler.nodes.AbstractMergeNode; +import org.graalvm.compiler.nodes.BeginNode; +import org.graalvm.compiler.nodes.DeoptimizeNode; +import org.graalvm.compiler.nodes.DynamicDeoptimizeNode; +import org.graalvm.compiler.nodes.FixedNode; +import org.graalvm.compiler.nodes.IfNode; +import org.graalvm.compiler.nodes.LogicNode; +import org.graalvm.compiler.nodes.StructuredGraph; +import org.graalvm.compiler.nodes.ValueNode; +import org.graalvm.compiler.nodes.ValuePhiNode; +import org.graalvm.compiler.nodes.calc.IsNullNode; +import org.graalvm.compiler.nodes.extended.NullCheckNode; +import org.graalvm.compiler.nodes.util.GraphUtil; +import org.graalvm.compiler.phases.BasePhase; +import org.graalvm.compiler.phases.tiers.LowTierContext; + +import jdk.vm.ci.meta.DeoptimizationReason; +import jdk.vm.ci.meta.JavaConstant; +import jdk.vm.ci.meta.MetaAccessProvider; + +public class UseTrappingNullChecksPhase extends BasePhase { + + private static final DebugCounter counterTrappingNullCheck = Debug.counter("TrappingNullCheck"); + private static final DebugCounter counterTrappingNullCheckUnreached = Debug.counter("TrappingNullCheckUnreached"); + private static final DebugCounter counterTrappingNullCheckDynamicDeoptimize = Debug.counter("TrappingNullCheckDynamicDeoptimize"); + + @Override + protected void run(StructuredGraph graph, LowTierContext context) { + if (context.getTarget().implicitNullCheckLimit <= 0) { + return; + } + assert graph.getGuardsStage().areFrameStatesAtDeopts(); + + for (DeoptimizeNode deopt : graph.getNodes(DeoptimizeNode.TYPE)) { + tryUseTrappingNullCheck(deopt, deopt.predecessor(), deopt.reason(), deopt.getSpeculation()); + } + for (DynamicDeoptimizeNode deopt : graph.getNodes(DynamicDeoptimizeNode.TYPE)) { + tryUseTrappingNullCheck(context.getMetaAccess(), deopt); + } + } + + private static void tryUseTrappingNullCheck(MetaAccessProvider metaAccessProvider, DynamicDeoptimizeNode deopt) { + Node predecessor = deopt.predecessor(); + if (predecessor instanceof AbstractMergeNode) { + AbstractMergeNode merge = (AbstractMergeNode) predecessor; + + // Process each predecessor at the merge, unpacking the reasons and speculations as + // needed. + ValueNode reason = deopt.getActionAndReason(); + ValuePhiNode reasonPhi = null; + List reasons = null; + int expectedPhis = 0; + + if (reason instanceof ValuePhiNode) { + reasonPhi = (ValuePhiNode) reason; + if (reasonPhi.merge() != merge) { + return; + } + reasons = reasonPhi.values().snapshot(); + expectedPhis++; + } else if (!reason.isConstant()) { + return; + } + + ValueNode speculation = deopt.getSpeculation(); + ValuePhiNode speculationPhi = null; + List speculations = null; + if (speculation instanceof ValuePhiNode) { + speculationPhi = (ValuePhiNode) speculation; + if (speculationPhi.merge() != merge) { + return; + } + speculations = speculationPhi.values().snapshot(); + expectedPhis++; + } + + if (merge.phis().count() != expectedPhis) { + return; + } + + int index = 0; + for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) { + ValueNode thisReason = reasons != null ? reasons.get(index) : reason; + ValueNode thisSpeculation = speculations != null ? speculations.get(index++) : speculation; + if (!thisReason.isConstant() || !thisSpeculation.isConstant() || !thisSpeculation.asConstant().equals(JavaConstant.NULL_POINTER)) { + continue; + } + DeoptimizationReason deoptimizationReason = metaAccessProvider.decodeDeoptReason(thisReason.asJavaConstant()); + tryUseTrappingNullCheck(deopt, end.predecessor(), deoptimizationReason, null); + } + } + } + + private static void tryUseTrappingNullCheck(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason, JavaConstant speculation) { + if (deoptimizationReason != DeoptimizationReason.NullCheckException && deoptimizationReason != DeoptimizationReason.UnreachedCode) { + return; + } + if (speculation != null && !speculation.equals(JavaConstant.NULL_POINTER)) { + return; + } + if (predecessor instanceof AbstractMergeNode) { + AbstractMergeNode merge = (AbstractMergeNode) predecessor; + if (merge.phis().isEmpty()) { + for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) { + checkPredecessor(deopt, end.predecessor(), deoptimizationReason); + } + } + } else if (predecessor instanceof AbstractBeginNode) { + checkPredecessor(deopt, predecessor, deoptimizationReason); + } + } + + private static void checkPredecessor(AbstractDeoptimizeNode deopt, Node predecessor, DeoptimizationReason deoptimizationReason) { + Node current = predecessor; + AbstractBeginNode branch = null; + while (current instanceof AbstractBeginNode) { + branch = (AbstractBeginNode) current; + if (branch.anchored().isNotEmpty()) { + // some input of the deopt framestate is anchored to this branch + return; + } + current = current.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, deoptimizationReason); + } + } + } + + private static void replaceWithTrappingNullCheck(AbstractDeoptimizeNode deopt, IfNode ifNode, LogicNode condition, DeoptimizationReason deoptimizationReason) { + counterTrappingNullCheck.increment(); + if (deopt instanceof DynamicDeoptimizeNode) { + counterTrappingNullCheckDynamicDeoptimize.increment(); + } + if (deoptimizationReason == DeoptimizationReason.UnreachedCode) { + counterTrappingNullCheckUnreached.increment(); + } + IsNullNode isNullNode = (IsNullNode) condition; + AbstractBeginNode nonTrappingContinuation = ifNode.falseSuccessor(); + AbstractBeginNode trappingContinuation = ifNode.trueSuccessor(); + NullCheckNode trappingNullCheck = deopt.graph().add(new NullCheckNode(isNullNode.getValue())); + 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); + if (nonTrappingContinuation instanceof BeginNode) { + FixedNode next = nonTrappingContinuation.next(); + nonTrappingContinuation.clearSuccessors(); + trappingNullCheck.setNext(next); + nonTrappingContinuation.safeDelete(); + } else { + trappingNullCheck.setNext(nonTrappingContinuation); + } + + GraphUtil.killCFG(trappingContinuation); + GraphUtil.tryKillUnused(isNullNode); + } +}