/*
* Copyright (c) 2013, 2020, 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 jdk.internal.vm.compiler.collections.EconomicMap;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.AbstractMergeNode;
import org.graalvm.compiler.nodes.DeoptimizingNode;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.FrameState;
import org.graalvm.compiler.nodes.LoopBeginNode;
import org.graalvm.compiler.nodes.LoopExitNode;
import org.graalvm.compiler.nodes.StateSplit;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.StructuredGraph.FrameStateVerification;
import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
import org.graalvm.compiler.nodes.util.GraphUtil;
import org.graalvm.compiler.phases.Phase;
import org.graalvm.compiler.phases.graph.ReentrantNodeIterator;
import org.graalvm.compiler.phases.graph.ReentrantNodeIterator.NodeIteratorClosure;
import jdk.vm.ci.code.BytecodeFrame;
This phase transfers FrameState
nodes from StateSplit
nodes to DeoptimizingNodes
. This allow to enter the AFTER_FSA
stage of the graph where no new node that may cause deoptimization can be introduced anymore. This Phase processes the graph in post order, assigning the FrameState
from the last StateSplit
node to DeoptimizingNodes
.
/**
* This phase transfers {@link FrameState} nodes from {@link StateSplit} nodes to
* {@link DeoptimizingNode DeoptimizingNodes}.
*
* This allow to enter the {@link GuardsStage#AFTER_FSA AFTER_FSA} stage of the graph where no new
* node that may cause deoptimization can be introduced anymore.
* <p>
* This Phase processes the graph in post order, assigning the {@link FrameState} from the last
* {@link StateSplit} node to {@link DeoptimizingNode DeoptimizingNodes}.
*/
public class FrameStateAssignmentPhase extends Phase {
private static class FrameStateAssignmentClosure extends NodeIteratorClosure<FrameState> {
@Override
protected FrameState processNode(FixedNode node, FrameState previousState) {
FrameState currentState = previousState;
if (node instanceof DeoptimizingNode.DeoptBefore) {
DeoptimizingNode.DeoptBefore deopt = (DeoptimizingNode.DeoptBefore) node;
if (deopt.canDeoptimize() && deopt.stateBefore() == null) {
GraalError.guarantee(currentState != null, "no FrameState at DeoptimizingNode %s", deopt);
deopt.setStateBefore(currentState);
}
}
if (node instanceof StateSplit) {
StateSplit stateSplit = (StateSplit) node;
FrameState stateAfter = stateSplit.stateAfter();
if (stateAfter != null) {
if (stateAfter.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
currentState = null;
} else {
currentState = stateAfter;
}
stateSplit.setStateAfter(null);
}
}
if (node instanceof DeoptimizingNode.DeoptDuring) {
DeoptimizingNode.DeoptDuring deopt = (DeoptimizingNode.DeoptDuring) node;
if (deopt.canDeoptimize() && deopt.stateDuring() == null) {
GraalError.guarantee(currentState != null, "no FrameState at DeoptimizingNode %s", deopt);
deopt.computeStateDuring(currentState);
}
}
if (node instanceof DeoptimizingNode.DeoptAfter) {
DeoptimizingNode.DeoptAfter deopt = (DeoptimizingNode.DeoptAfter) node;
if (deopt.canDeoptimize() && deopt.stateAfter() == null) {
GraalError.guarantee(currentState != null, "no FrameState at DeoptimizingNode %s", deopt);
deopt.setStateAfter(currentState);
}
}
return currentState;
}
@Override
protected FrameState merge(AbstractMergeNode merge, List<FrameState> states) {
FrameState singleFrameState = singleFrameState(states);
return singleFrameState == null ? merge.stateAfter() : singleFrameState;
}
@Override
protected FrameState afterSplit(AbstractBeginNode node, FrameState oldState) {
return oldState;
}
@Override
protected EconomicMap<LoopExitNode, FrameState> processLoop(LoopBeginNode loop, FrameState initialState) {
return ReentrantNodeIterator.processLoop(this, loop, initialState).exitStates;
}
}
@Override
protected void run(StructuredGraph graph) {
assert !graph.getGuardsStage().allowsFloatingGuards() && !hasFloatingDeopts(graph);
if (graph.getGuardsStage().areFrameStatesAtSideEffects()) {
ReentrantNodeIterator.apply(new FrameStateAssignmentClosure(), graph.start(), null);
graph.setGuardsStage(GuardsStage.AFTER_FSA);
graph.weakenFrameStateVerification(FrameStateVerification.NONE);
graph.getNodes(FrameState.TYPE).filter(state -> state.hasNoUsages()).forEach(GraphUtil::killWithUnusedFloatingInputs);
}
}
private static boolean hasFloatingDeopts(StructuredGraph graph) {
for (Node n : graph.getNodes()) {
if (n instanceof DeoptimizingNode && GraphUtil.isFloatingNode(n)) {
DeoptimizingNode deoptimizingNode = (DeoptimizingNode) n;
if (deoptimizingNode.canDeoptimize()) {
return true;
}
}
}
return false;
}
private static FrameState singleFrameState(List<FrameState> states) {
FrameState singleState = states.get(0);
for (int i = 1; i < states.size(); ++i) {
if (states.get(i) != singleState) {
return null;
}
}
if (singleState != null && singleState.bci != BytecodeFrame.INVALID_FRAMESTATE_BCI) {
return singleState;
}
return null;
}
@Override
public boolean checkContract() {
// TODO GR-1409
return false;
}
}