All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jdk.graal.compiler.phases.common.UseTrappingOperationPhase Maven / Gradle / Ivy

There is a newer version: 24.1.1
Show newest version
/*
 * Copyright (c) 2022, 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.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * 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 jdk.graal.compiler.phases.common;

import java.util.List;
import java.util.Optional;

import jdk.graal.compiler.debug.DebugContext;
import jdk.graal.compiler.graph.Node;
import jdk.graal.compiler.nodeinfo.InputType;
import jdk.graal.compiler.nodes.AbstractBeginNode;
import jdk.graal.compiler.nodes.AbstractDeoptimizeNode;
import jdk.graal.compiler.nodes.AbstractEndNode;
import jdk.graal.compiler.nodes.AbstractMergeNode;
import jdk.graal.compiler.nodes.BeginNode;
import jdk.graal.compiler.nodes.DeoptimizingFixedWithNextNode;
import jdk.graal.compiler.nodes.DynamicDeoptimizeNode;
import jdk.graal.compiler.nodes.EndNode;
import jdk.graal.compiler.nodes.GraphState;
import jdk.graal.compiler.nodes.IfNode;
import jdk.graal.compiler.nodes.LogicNode;
import jdk.graal.compiler.nodes.LoopExitNode;
import jdk.graal.compiler.nodes.StructuredGraph;
import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.ValuePhiNode;
import jdk.graal.compiler.nodes.memory.address.AddressNode;
import jdk.graal.compiler.nodes.util.GraphUtil;
import jdk.graal.compiler.phases.BasePhase;
import jdk.graal.compiler.phases.tiers.LowTierContext;

import jdk.vm.ci.meta.DeoptimizationReason;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.MetaAccessProvider;
import jdk.vm.ci.meta.SpeculationLog.Speculation;

/**
 * Abstract base class for phases that try to fold guards (in the form of fixed {@link IfNode}) into
 * fixed {@link DeoptimizingFixedWithNextNode} that use implicit expections.
 */
public abstract class UseTrappingOperationPhase extends BasePhase {

    public abstract boolean isSupportedReason(DeoptimizationReason reason);

    public abstract boolean canReplaceCondition(LogicNode condition, IfNode ifNode);

    public abstract boolean useAddressOptimization(AddressNode adr, LowTierContext context);

    public abstract DeoptimizingFixedWithNextNode tryReplaceExisting(StructuredGraph graph, AbstractBeginNode nonTrappingContinuation, AbstractBeginNode trappingContinuation, LogicNode condition,
                    IfNode ifNode, AbstractDeoptimizeNode deopt, JavaConstant deoptReasonAndAction, JavaConstant deoptSpeculation, LowTierContext context);

    public abstract DeoptimizingFixedWithNextNode createImplicitNode(StructuredGraph graph, LogicNode condition, JavaConstant deoptReasonAndAction, JavaConstant deoptSpeculation);

    public abstract boolean trueSuccessorIsDeopt();

    public abstract void finalAction(DeoptimizingFixedWithNextNode trappingVersionNode, LogicNode condition);

    public abstract void actionBeforeGuardRewrite(DeoptimizingFixedWithNextNode trappingVersionNode);

    @Override
    public Optional notApplicableTo(GraphState graphState) {
        return ALWAYS_APPLICABLE;
    }

    protected void tryUseTrappingVersion(MetaAccessProvider metaAccessProvider, DynamicDeoptimizeNode deopt, LowTierContext context) {
        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()) {
                merge.getDebug().log("Non constant reason %s", merge);
                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;
            List predecessors = merge.cfgPredecessors().snapshot();
            for (AbstractEndNode end : predecessors) {
                Node endPredecesssor = end.predecessor();
                ValueNode thisReason = reasons != null ? reasons.get(index) : reason;
                ValueNode thisSpeculation = speculations != null ? speculations.get(index) : speculation;
                if (!merge.isAlive()) {
                    // When evacuating a merge the last successor simplfies the merge away so it
                    // must be handled specially.
                    assert predecessors.get(predecessors.size() - 1) == end : "must be last end";
                    endPredecesssor = deopt.predecessor();
                    thisSpeculation = deopt.getSpeculation();
                    thisReason = deopt.getActionAndReason();
                }

                index++;
                if (!thisReason.isConstant() || !thisSpeculation.isConstant()) {
                    end.getDebug().log("Non constant deopt %s", end);
                    continue;
                }
                Speculation speculationConstant = metaAccessProvider.decodeSpeculation(thisSpeculation.asJavaConstant(), deopt.graph().getSpeculationLog());
                tryUseTrappingVersion(deopt, endPredecesssor, speculationConstant, thisReason.asJavaConstant(), thisSpeculation.asJavaConstant(), context);
            }
        }
    }

    protected void tryUseTrappingVersion(AbstractDeoptimizeNode deopt, Node predecessor, Speculation speculation, JavaConstant deoptReasonAndAction,
                    JavaConstant deoptSpeculation, LowTierContext context) {
        assert predecessor != null;
        assert speculation != null;

        // Skip over loop exit nodes.
        Node pred = predecessor;
        while (pred instanceof LoopExitNode) {
            pred = pred.predecessor();
        }
        if (pred instanceof AbstractMergeNode) {
            AbstractMergeNode merge = (AbstractMergeNode) pred;
            if (merge.phis().isEmpty()) {
                for (AbstractEndNode end : merge.cfgPredecessors().snapshot()) {
                    checkPredecessor(deopt, end.predecessor(), deoptReasonAndAction, deoptSpeculation, context);
                }
            }
        } else if (pred instanceof AbstractBeginNode) {
            checkPredecessor(deopt, pred, deoptReasonAndAction, deoptSpeculation, context);
        } else {
            deopt.getDebug().log(DebugContext.INFO_LEVEL, "Not a Begin or Merge %s", pred);
        }
    }

    protected void checkPredecessor(AbstractDeoptimizeNode deopt, Node predecessor, JavaConstant deoptReasonAndAction, JavaConstant deoptSpeculation, LowTierContext context) {
        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 (trueSuccessorIsDeopt()) {
                if (branch != ifNode.trueSuccessor()) {
                    return;
                }
            } else {
                if (branch != ifNode.falseSuccessor()) {
                    return;
                }
            }
            LogicNode condition = ifNode.condition();
            if (canReplaceCondition(condition, ifNode)) {
                replaceWithTrappingVersion(deopt, ifNode, condition, deoptReasonAndAction, deoptSpeculation, context);
            }
        }
    }

    protected void replaceWithTrappingVersion(AbstractDeoptimizeNode deopt, IfNode ifNode, LogicNode condition, JavaConstant deoptReasonAndAction, JavaConstant deoptSpeculation,
                    LowTierContext context) {
        StructuredGraph graph = deopt.graph();
        AbstractBeginNode nonTrappingContinuation = trueSuccessorIsDeopt() ? ifNode.falseSuccessor() : ifNode.trueSuccessor();
        AbstractBeginNode trappingContinuation = trueSuccessorIsDeopt() ? ifNode.trueSuccessor() : ifNode.falseSuccessor();
        DeoptimizingFixedWithNextNode trappingVersionNode = null;
        trappingVersionNode = tryReplaceExisting(graph, nonTrappingContinuation, trappingContinuation, condition, ifNode, deopt, deoptReasonAndAction, deoptSpeculation, context);
        if (trappingVersionNode == null) {
            // Need to add a null check node.
            trappingVersionNode = createImplicitNode(graph, condition, deoptReasonAndAction, deoptSpeculation);
            graph.replaceSplit(ifNode, trappingVersionNode, nonTrappingContinuation);
            graph.getOptimizationLog().report(getClass(), "NullCheckInsertion", ifNode);
        }
        trappingVersionNode.setStateBefore(deopt.stateBefore());
        actionBeforeGuardRewrite(trappingVersionNode);
        /*
         * 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(trappingVersionNode, InputType.Guard);
        if (nonTrappingContinuation instanceof BeginNode) {
            GraphUtil.unlinkFixedNode(nonTrappingContinuation);
            nonTrappingContinuation.safeDelete();
        }
        finalAction(trappingVersionNode, condition);
        GraphUtil.killCFG(trappingContinuation);
        GraphUtil.tryKillUnused(condition);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy