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

com.oracle.svm.hosted.phases.HostedGraphBuilderPhase Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2013, 2017, 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 com.oracle.svm.hosted.phases;

import java.util.HashMap;
import java.util.Map;

import org.graalvm.compiler.core.common.type.StampPair;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.java.BytecodeParser;
import org.graalvm.compiler.java.FrameStateBuilder;
import org.graalvm.compiler.java.GraphBuilderPhase;
import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.FixedWithNextNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.Invoke;
import org.graalvm.compiler.nodes.KillingBeginNode;
import org.graalvm.compiler.nodes.LoopBeginNode;
import org.graalvm.compiler.nodes.StateSplit;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
import org.graalvm.compiler.nodes.java.ExceptionObjectNode;
import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
import org.graalvm.compiler.phases.OptimisticOptimizations;
import org.graalvm.compiler.phases.util.Providers;
import org.graalvm.compiler.word.WordTypes;

import com.oracle.svm.core.code.FrameInfoEncoder;
import com.oracle.svm.core.graal.nodes.DeoptEntryNode;
import com.oracle.svm.core.graal.nodes.DeoptProxyAnchorNode;
import com.oracle.svm.hosted.meta.HostedMethod;
import com.oracle.svm.hosted.nodes.DeoptProxyNode;
import com.oracle.svm.hosted.nodes.SubstrateMethodCallTargetNode;
import com.oracle.svm.hosted.phases.SubstrateGraphBuilderPhase.SubstrateBytecodeParser;

import jdk.vm.ci.meta.JavaTypeProfile;
import jdk.vm.ci.meta.ResolvedJavaMethod;

public class HostedGraphBuilderPhase extends SubstrateGraphBuilderPhase {

    public HostedGraphBuilderPhase(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext,
                    WordTypes wordTypes) {
        super(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext, wordTypes);
    }

    @Override
    protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
        return new HostedBytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext);
    }
}

class HostedBytecodeParser extends SubstrateBytecodeParser {

    private static final DeoptEntryNode STICKY_DEOPT_ENTRY = new DeoptEntryNode();

    private int currentDeoptIndex;
    private Map deoptEntries = new HashMap<>();

    HostedBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
        super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext, true);
    }

    @Override
    public HostedMethod getMethod() {
        return (HostedMethod) super.getMethod();
    }

    @Override
    protected boolean forceLoopPhis() {
        return getMethod().compilationInfo.isDeoptTarget() || super.forceLoopPhis();
    }

    @Override
    protected boolean stampFromValueForForcedPhis() {
        return true;
    }

    @Override
    protected void build(FixedWithNextNode startInstruction, FrameStateBuilder startFrameState) {
        super.build(startInstruction, startFrameState);

        /* We never have floating guards in AOT compiled code. */
        getGraph().setGuardsStage(GuardsStage.FIXED_DEOPTS);

        assert !getMethod().isEntryPoint() : "Cannot directly use as entry point, create a call stub";

        if (getMethod().compilationInfo.isDeoptTarget()) {
            /*
             * Remove dangling DeoptProxyNodes which remained after deletion of the corresponding
             * DeoptEntryNodes.
             */
            for (DeoptProxyNode deoptProxy : graph.getNodes(DeoptProxyNode.TYPE)) {
                if (!deoptProxy.hasProxyPoint()) {
                    ValueNode originalValue = deoptProxy.getOriginalNode();
                    deoptProxy.replaceAtUsagesAndDelete(originalValue);
                }
            }
        }
    }

    @Override
    public MethodCallTargetNode createMethodCallTarget(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, StampPair returnStamp, JavaTypeProfile profile) {
        return new SubstrateMethodCallTargetNode(invokeKind, targetMethod, args, returnStamp, getMethod().getProfilingInfo(), bci());
    }

    private void insertProxies(FixedNode deoptTarget, FrameStateBuilder state) {

        /*
         * At a deoptimization point we wrap non-constant locals (and java stack elements) with
         * proxy nodes. This is to avoid global value numbering on locals (or derived expressions).
         * The effect is that when a local is accessed after a deoptimization point it is really
         * loaded from its location. This is similar to what happens in the GraphBuilderPhase if
         * entryBCI is set for OSR.
         */
        state.insertProxies(value -> createProxyNode(value, deoptTarget));
        currentDeoptIndex++;
    }

    private ValueNode createProxyNode(ValueNode value, FixedNode deoptTarget) {
        ValueNode v = DeoptProxyNode.create(value, deoptTarget, currentDeoptIndex);
        if (v.graph() != null) {
            return v;
        }
        return graph.addOrUniqueWithInputs(v);
    }

    /**
     * Insert a deopt entry for the graph's start node.
     */
    @Override
    protected void finishPrepare(FixedWithNextNode startInstr, int bci, FrameStateBuilder state) {
        super.finishPrepare(startInstr, bci, state);

        if (getMethod().compilationInfo.isDeoptEntry(bci, false, false)) {
            DeoptEntryNode deoptEntry = append(new DeoptEntryNode());
            deoptEntry.setStateAfter(frameState.create(bci, deoptEntry));
            deoptEntries.put(Long.valueOf(bci), deoptEntry);
            insertProxies(deoptEntry, state);
        }
    }

    @Override
    protected void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, IntrinsicContext calleeIntrinsicContext) {
        assert calleeIntrinsicContext != null : "only inlining replacements";
        if (getMethod().compilationInfo.isDeoptEntry(bci(), false, false)) {
            /*
             * Replacements use the frame state before the invoke for all nodes that need a state,
             * i.e., we want to re-execute the whole replacement in case of deoptimization.
             * Therefore, we need to register a DeoptEntryNode before inlining the replacement. The
             * frame state used for the DeoptEntryNode needs to be the same state that will be used
             * later on in the intrinsic.
             */
            FrameState stateAfter = frameState.create(bci(), getNonIntrinsicAncestor(), false, targetMethod.getSignature().toParameterKinds(!targetMethod.isStatic()), args);
            long encodedBci = FrameInfoEncoder.encodeBci(stateAfter.bci, stateAfter.duringCall(), stateAfter.rethrowException());
            if (!deoptEntries.containsKey(encodedBci)) {
                DeoptEntryNode deoptEntry = graph.add(new DeoptEntryNode());
                deoptEntry.setStateAfter(stateAfter);
                insertProxies(deoptEntry, frameState);

                lastInstr.setNext(deoptEntry);
                lastInstr = deoptEntry;

                for (int i = 0; i < args.length; i++) {
                    args[i] = createProxyNode(args[i], deoptEntry);
                }
            }
            /*
             * Ensure that no one registers a later state (after the replacement) with the same
             * frame state.
             */
            deoptEntries.put(encodedBci, STICKY_DEOPT_ENTRY);
        }

        super.parseAndInlineCallee(targetMethod, args, calleeIntrinsicContext);
    }

    /**
     * Insert deopt entries after all state splits.
     */
    @Override
    protected FixedWithNextNode finishInstruction(FixedWithNextNode instr, FrameStateBuilder stateBuilder) {
        if (getMethod().compilationInfo.isDeoptTarget() && !parsingIntrinsic()) {
            FrameState stateAfter = null;
            if (instr instanceof StateSplit && !(instr instanceof DeoptEntryNode)) {
                /*
                 * The regular case: the instruction is a state split and we insert a DeoptEntryNode
                 * right after it.
                 */
                StateSplit stateSplit = (StateSplit) instr;
                stateAfter = stateSplit.stateAfter();
            } else if (instr instanceof AbstractBeginNode) {
                /*
                 * We are at a block begin. If the block predecessor is a LoopExitNode or an
                 * InvokeWithException (both are state splits), we didn't inserted a deopt entry
                 * yet. So we do it at the begin of a block.
                 *
                 * Note that this only happens if the LoopExitNode/InvokeWithException is the
                 * _single_ predcessor of this block. In case of multiple predecessors, the block
                 * starts with a MergeNode and this is handled like a regular case.
                 */
                Node predecessor = instr.predecessor();
                if (predecessor instanceof KillingBeginNode) {
                    /*
                     * This is between an InvokeWithException and the BlockPlaceholderNode.
                     */
                    predecessor = predecessor.predecessor();
                }
                if (predecessor instanceof StateSplit && !(predecessor instanceof DeoptEntryNode)) {
                    stateAfter = ((StateSplit) predecessor).stateAfter();
                }
            }

            boolean needsDeoptEntry = false;
            boolean needsProxies = false;
            if (stateAfter != null) {
                if (getMethod().compilationInfo.isDeoptEntry(stateAfter.bci, stateAfter.duringCall(), stateAfter.rethrowException())) {
                    needsDeoptEntry = true;
                    needsProxies = true;
                } else if (instr.predecessor() instanceof Invoke && getMethod().compilationInfo.isDeoptEntry(((Invoke) instr.predecessor()).bci(), true, false)) {
                    /*
                     * Invoke nodes can be implicit deoptimization entry points. But we cannot
                     * anchor proxy nodes on invocations: The invoke has two successors (normal and
                     * exception handler), and we need to proxy values at the beginning of both.
                     */
                    needsProxies = true;
                } else if (instr instanceof ExceptionObjectNode && getMethod().compilationInfo.isDeoptEntry(((ExceptionObjectNode) instr).stateAfter().bci, true, false)) {
                    /*
                     * The predecessor of the ExceptionObjectNode will be an Invoke, but the Invoke
                     * has not been created yet. So the check above for the predecessor does not
                     * trigger.
                     */
                    needsProxies = true;
                }
            }

            if (needsProxies) {
                long encodedBci = FrameInfoEncoder.encodeBci(stateAfter.bci, stateAfter.duringCall(), stateAfter.rethrowException());
                DeoptProxyAnchorNode existingDeoptEntry = deoptEntries.get(encodedBci);
                if (existingDeoptEntry != STICKY_DEOPT_ENTRY) {
                    if (existingDeoptEntry != null) {
                        /*
                         * Some state splits (i.e. MergeNode and DispatchBeginNode) do not have a
                         * correspondent byte code. Therefore there can be a previously added deopt
                         * entry with the same BCI. For MergeNodes we replace the previous entry
                         * because the new frame state has less live locals.
                         */
                        existingDeoptEntry.replaceAtUsages(null);
                        graph.removeFixed(existingDeoptEntry);
                        deoptEntries.remove(encodedBci);

                        if (existingDeoptEntry instanceof DeoptEntryNode) {
                            /*
                             * We already had a DeoptEntryNode registered earlier for some reason,
                             * so be conservative and create one again (and not just a
                             * DeoptProxyAnchorNode).
                             */
                            needsDeoptEntry = true;
                        }
                    }
                    assert !deoptEntries.containsKey(encodedBci) : "duplicate deopt entry for encoded BCI " + encodedBci;
                    DeoptProxyAnchorNode deoptEntry = createDeoptEntry(stateBuilder, stateAfter, !needsDeoptEntry);

                    if (instr instanceof LoopBeginNode) {
                        /*
                         * Loop headers to not have their own bci. Never move a deopt entry for the
                         * loop header down, e.g., into a loop end (that might then end up to be
                         * dead code).
                         */
                        deoptEntries.put(encodedBci, STICKY_DEOPT_ENTRY);
                    } else {
                        deoptEntries.put(encodedBci, deoptEntry);
                    }

                    assert instr.next() == null : "cannot append instruction to instruction which isn't end (" + instr + "->" + instr.next() + ")";
                    instr.setNext(deoptEntry);
                    return deoptEntry;
                }
            }
        }
        return super.finishInstruction(instr, stateBuilder);
    }

    private DeoptProxyAnchorNode createDeoptEntry(FrameStateBuilder stateBuilder, FrameState stateAfter, boolean anchorOnly) {
        DeoptProxyAnchorNode deoptEntry = graph.add(anchorOnly ? new DeoptProxyAnchorNode() : new DeoptEntryNode());
        deoptEntry.setStateAfter(stateAfter);

        insertProxies(deoptEntry, stateBuilder);
        return deoptEntry;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy