blob: 738f0f37b604f2dd0ffcc4502dd7d9ff50f4fb0d [file] [log] [blame]
/*
* Copyright (c) 2015, 2016, 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.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.Iterator;
import java.util.List;
import java.util.function.Function;
import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
import org.graalvm.compiler.core.common.cfg.BlockMap;
import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp;
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.And;
import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Or;
import org.graalvm.compiler.core.common.type.IntegerStamp;
import org.graalvm.compiler.core.common.type.ObjectStamp;
import org.graalvm.compiler.core.common.type.Stamp;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.core.common.type.TypeReference;
import org.graalvm.compiler.debug.Debug;
import org.graalvm.compiler.debug.DebugCounter;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeMap;
import org.graalvm.compiler.graph.spi.CanonicalizerTool;
import org.graalvm.compiler.nodeinfo.InputType;
import org.graalvm.compiler.nodes.AbstractBeginNode;
import org.graalvm.compiler.nodes.BeginNode;
import org.graalvm.compiler.nodes.BinaryOpLogicNode;
import org.graalvm.compiler.nodes.ConditionAnchorNode;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.DeoptimizeNode;
import org.graalvm.compiler.nodes.DeoptimizingGuard;
import org.graalvm.compiler.nodes.FixedGuardNode;
import org.graalvm.compiler.nodes.FixedNode;
import org.graalvm.compiler.nodes.GuardNode;
import org.graalvm.compiler.nodes.GuardProxyNode;
import org.graalvm.compiler.nodes.IfNode;
import org.graalvm.compiler.nodes.LogicNode;
import org.graalvm.compiler.nodes.LoopExitNode;
import org.graalvm.compiler.nodes.ParameterNode;
import org.graalvm.compiler.nodes.ShortCircuitOrNode;
import org.graalvm.compiler.nodes.StructuredGraph;
import org.graalvm.compiler.nodes.UnaryOpLogicNode;
import org.graalvm.compiler.nodes.ValueNode;
import org.graalvm.compiler.nodes.calc.AndNode;
import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
import org.graalvm.compiler.nodes.calc.BinaryNode;
import org.graalvm.compiler.nodes.calc.IntegerEqualsNode;
import org.graalvm.compiler.nodes.calc.ObjectEqualsNode;
import org.graalvm.compiler.nodes.calc.PointerEqualsNode;
import org.graalvm.compiler.nodes.calc.UnaryNode;
import org.graalvm.compiler.nodes.cfg.Block;
import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
import org.graalvm.compiler.nodes.extended.GuardingNode;
import org.graalvm.compiler.nodes.extended.IntegerSwitchNode;
import org.graalvm.compiler.nodes.extended.LoadHubNode;
import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
import org.graalvm.compiler.nodes.java.LoadFieldNode;
import org.graalvm.compiler.nodes.java.TypeSwitchNode;
import org.graalvm.compiler.nodes.spi.NodeWithState;
import org.graalvm.compiler.nodes.util.GraphUtil;
import org.graalvm.compiler.phases.BasePhase;
import org.graalvm.compiler.phases.common.LoweringPhase.Frame;
import org.graalvm.compiler.phases.schedule.SchedulePhase;
import org.graalvm.compiler.phases.tiers.PhaseContext;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.TriState;
public class DominatorConditionalEliminationPhase extends BasePhase<PhaseContext> {
private static final DebugCounter counterStampsRegistered = Debug.counter("StampsRegistered");
private static final DebugCounter counterStampsFound = Debug.counter("StampsFound");
private static final DebugCounter counterIfsKilled = Debug.counter("CE_KilledIfs");
private static final DebugCounter counterLFFolded = Debug.counter("ConstantLFFolded");
private final boolean fullSchedule;
public DominatorConditionalEliminationPhase(boolean fullSchedule) {
this.fullSchedule = fullSchedule;
}
@Override
@SuppressWarnings("try")
protected void run(StructuredGraph graph, PhaseContext context) {
try (Debug.Scope s = Debug.scope("DominatorConditionalElimination")) {
Function<Block, Iterable<? extends Node>> blockToNodes;
Function<Node, Block> nodeToBlock;
Block startBlock;
if (fullSchedule) {
SchedulePhase schedule = new SchedulePhase(SchedulePhase.SchedulingStrategy.EARLIEST);
schedule.apply(graph);
ControlFlowGraph cfg = graph.getLastSchedule().getCFG();
cfg.computePostdominators();
blockToNodes = b -> graph.getLastSchedule().getBlockToNodesMap().get(b);
nodeToBlock = n -> graph.getLastSchedule().getNodeToBlockMap().get(n);
startBlock = cfg.getStartBlock();
} else {
ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, true, true);
BlockMap<List<FixedNode>> nodes = new BlockMap<>(cfg);
for (Block b : cfg.getBlocks()) {
ArrayList<FixedNode> curNodes = new ArrayList<>();
for (FixedNode node : b.getNodes()) {
if (node instanceof AbstractBeginNode || node instanceof FixedGuardNode || node instanceof ConditionAnchorNode || node instanceof IfNode) {
curNodes.add(node);
}
}
nodes.put(b, curNodes);
}
blockToNodes = b -> nodes.get(b);
nodeToBlock = n -> cfg.blockFor(n);
startBlock = cfg.getStartBlock();
}
new Instance(graph, blockToNodes, nodeToBlock, context).processBlock(startBlock);
}
}
public static class Instance {
protected NodeMap<Info> map;
protected Deque<LoopExitNode> loopExits;
protected final Function<Block, Iterable<? extends Node>> blockToNodes;
protected final Function<Node, Block> nodeToBlock;
protected final CanonicalizerTool tool;
/**
* Tests which may be eliminated because post dominating tests to prove a broader condition.
*/
private Deque<PendingTest> pendingTests;
public Instance(StructuredGraph graph, Function<Block, Iterable<? extends Node>> blockToNodes,
Function<Node, Block> nodeToBlock, PhaseContext context) {
map = graph.createNodeMap();
loopExits = new ArrayDeque<>();
this.blockToNodes = blockToNodes;
this.nodeToBlock = nodeToBlock;
pendingTests = new ArrayDeque<>();
tool = GraphUtil.getDefaultSimplifier(context.getMetaAccess(), context.getConstantReflection(), context.getConstantFieldProvider(), false, graph.getAssumptions(), context.getLowerer());
}
public void processBlock(Block startBlock) {
LoweringPhase.processBlock(new InstanceFrame(startBlock, null));
}
public class InstanceFrame extends LoweringPhase.Frame<InstanceFrame> {
protected List<Runnable> undoOperations = new ArrayList<>();
public InstanceFrame(Block block, InstanceFrame parent) {
super(block, parent);
}
@Override
public Frame<?> enter(Block b) {
return new InstanceFrame(b, this);
}
@Override
public void postprocess() {
Debug.log("[Post Processing block %s]", block);
undoOperations.forEach(x -> x.run());
}
protected void processConditionAnchor(ConditionAnchorNode node) {
tryProveCondition(node.condition(), (guard, result) -> {
if (result != node.isNegated()) {
node.replaceAtUsages(guard);
GraphUtil.unlinkFixedNode(node);
GraphUtil.killWithUnusedFloatingInputs(node);
} else {
ValueAnchorNode valueAnchor = node.graph().add(new ValueAnchorNode(null));
node.replaceAtUsages(valueAnchor);
node.graph().replaceFixedWithFixed(node, valueAnchor);
}
return true;
});
}
protected void processGuard(GuardNode node) {
if (!tryProveGuardCondition(node, node.getCondition(), (guard, result) -> {
if (result != node.isNegated()) {
node.replaceAndDelete(guard);
} else {
DeoptimizeNode deopt = node.graph().add(new DeoptimizeNode(node.getAction(), node.getReason(), node.getSpeculation()));
AbstractBeginNode beginNode = (AbstractBeginNode) node.getAnchor();
FixedNode next = beginNode.next();
beginNode.setNext(deopt);
GraphUtil.killCFG(next);
}
return true;
})) {
registerNewCondition(node.getCondition(), node.isNegated(), node);
}
}
protected void processFixedGuard(FixedGuardNode node) {
if (!tryProveGuardCondition(node, node.condition(), (guard, result) -> {
if (result != node.isNegated()) {
node.replaceAtUsages(guard);
GraphUtil.unlinkFixedNode(node);
GraphUtil.killWithUnusedFloatingInputs(node);
} else {
DeoptimizeNode deopt = node.graph().add(new DeoptimizeNode(node.getAction(), node.getReason(), node.getSpeculation()));
deopt.setStateBefore(node.stateBefore());
node.replaceAtPredecessor(deopt);
GraphUtil.killCFG(node);
}
Debug.log("Kill fixed guard guard");
return true;
})) {
registerNewCondition(node.condition(), node.isNegated(), node);
}
}
protected void processIf(IfNode node) {
tryProveCondition(node.condition(), (guard, result) -> {
AbstractBeginNode survivingSuccessor = node.getSuccessor(result);
survivingSuccessor.replaceAtUsages(InputType.Guard, guard);
survivingSuccessor.replaceAtPredecessor(null);
node.replaceAtPredecessor(survivingSuccessor);
GraphUtil.killCFG(node);
if (survivingSuccessor instanceof BeginNode) {
undoOperations.add(() -> {
if (survivingSuccessor.isAlive()) {
((BeginNode) survivingSuccessor).trySimplify();
}
});
}
Debug.log("Kill if");
counterIfsKilled.increment();
return true;
});
}
@Override
public void preprocess() {
Debug.log("[Pre Processing block %s]", block);
AbstractBeginNode beginNode = block.getBeginNode();
if (beginNode instanceof LoopExitNode && beginNode.isAlive()) {
LoopExitNode loopExitNode = (LoopExitNode) beginNode;
Instance.this.loopExits.push(loopExitNode);
undoOperations.add(() -> loopExits.pop());
} else if (block.getDominator() != null && (block.getDominator().getLoopDepth() > block.getLoopDepth() ||
(block.getDominator().getLoopDepth() == block.getLoopDepth() && block.getDominator().getLoop() != block.getLoop()))) {
// We are exiting the loop, but there is not a single loop exit block along our
// dominator tree (e.g., we are a merge of two loop exits).
final NodeMap<Info> oldMap = map;
final Deque<LoopExitNode> oldLoopExits = loopExits;
map = map.graph().createNodeMap();
loopExits = new ArrayDeque<>();
undoOperations.add(() -> {
map = oldMap;
loopExits = oldLoopExits;
});
}
// For now conservatively collect guards only within the same block.
pendingTests.clear();
for (Node n : blockToNodes.apply(block)) {
if (n.isAlive()) {
processNode(n);
}
}
}
protected void processNode(Node node) {
if (node instanceof NodeWithState && !(node instanceof GuardingNode)) {
pendingTests.clear();
}
if (node instanceof AbstractBeginNode) {
processAbstractBegin((AbstractBeginNode) node);
} else if (node instanceof FixedGuardNode) {
processFixedGuard((FixedGuardNode) node);
} else if (node instanceof GuardNode) {
processGuard((GuardNode) node);
} else if (node instanceof ConditionAnchorNode) {
processConditionAnchor((ConditionAnchorNode) node);
} else if (node instanceof IfNode) {
processIf((IfNode) node);
} else {
return;
}
}
protected void registerNewCondition(LogicNode condition, boolean negated, ValueNode guard) {
if (!negated && condition instanceof PointerEqualsNode) {
PointerEqualsNode pe = (PointerEqualsNode) condition;
ValueNode x = pe.getX();
ValueNode y = pe.getY();
if (y.isConstant()) {
JavaConstant constant = y.asJavaConstant();
Stamp succeeding = pe.getSucceedingStampForX(negated);
if (succeeding == null && pe instanceof ObjectEqualsNode && guard instanceof FixedGuardNode) {
succeeding = y.stamp();
}
if (succeeding != null) {
if (y.stamp() instanceof ObjectStamp) {
GuardedConstantStamp cos = new GuardedConstantStamp(constant, (ObjectStamp) succeeding);
registerNewStamp(x, cos, guard);
return;
}
}
}
}
if (condition instanceof UnaryOpLogicNode) {
UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) condition;
Stamp newStamp = unaryLogicNode.getSucceedingStampForValue(negated);
registerNewStamp(unaryLogicNode.getValue(), newStamp, guard);
} else if (condition instanceof BinaryOpLogicNode) {
BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) condition;
ValueNode x = binaryOpLogicNode.getX();
if (!x.isConstant()) {
Stamp newStampX = binaryOpLogicNode.getSucceedingStampForX(negated);
registerNewStamp(x, newStampX, guard);
}
ValueNode y = binaryOpLogicNode.getY();
if (!y.isConstant()) {
Stamp newStampY = binaryOpLogicNode.getSucceedingStampForY(negated);
registerNewStamp(y, newStampY, guard);
}
if (condition instanceof IntegerEqualsNode && guard instanceof DeoptimizingGuard && !negated) {
if (y.isConstant() && x instanceof AndNode) {
AndNode and = (AndNode) x;
if (and.getY() == y) {
/*
* This 'and' proves something about some of the bits in and.getX().
* It's equivalent to or'ing in the mask value since those values
* are known to be set.
*/
BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp()).getOr();
IntegerStamp newStampX = (IntegerStamp) op.foldStamp(and.getX().stamp(), y.stamp());
registerNewStamp(and.getX(), newStampX, guard);
}
}
}
}
if (guard instanceof DeoptimizingGuard) {
pendingTests.push(new PendingTest(condition, (DeoptimizingGuard) guard));
}
registerCondition(condition, negated, guard);
}
@SuppressWarnings("try")
protected Pair<InfoElement, Stamp> foldFromConstLoadField(LoadFieldNode loadFieldNode, InfoElementProvider info) {
ValueNode object = loadFieldNode.object();
if (!loadFieldNode.field().isStatic()) {
// look if we got stamp info for the object and return the constant stamp
Pair<InfoElement, Stamp> pair = getConstantObjectStamp(info, object);
if (pair == null) {
pair = recursiveFoldStamp(object, info);
}
if (pair != null) {
Stamp s = pair.second;
if (s instanceof GuardedConstantStamp) {
ConstantNode c = tryFoldFromLoadField(loadFieldNode, pair.second);
if (c != null) {
counterLFFolded.increment();
if (c.stamp() instanceof ObjectStamp) {
GuardedConstantStamp cos = new GuardedConstantStamp(c.asJavaConstant(), (ObjectStamp) c.stamp());
return new Pair<>(pair.first, cos);
}
return new Pair<>(pair.first, c.stamp());
}
}
}
}
return null;
}
private ConstantNode tryFoldFromLoadField(LoadFieldNode lf, Stamp x) {
GuardedConstantStamp cos = (GuardedConstantStamp) x;
return lf.asConstant(tool, cos.objectConstant);
}
private Pair<InfoElement, Stamp> getConstantObjectStamp(InfoElementProvider infos, ValueNode n) {
for (InfoElement infoElement : infos.getInfoElements(n)) {
Stamp s = infoElement.getStamp();
if (s instanceof GuardedConstantStamp) {
return new Pair<>(infoElement, s);
}
}
return null;
}
Pair<InfoElement, Stamp> recursiveFoldStamp(Node node, InfoElementProvider info) {
if (node instanceof LoadFieldNode) {
Pair<InfoElement, Stamp> pair = foldFromConstLoadField((LoadFieldNode) node, info);
if (pair != null) {
return pair;
}
}
if (node instanceof UnaryNode) {
UnaryNode unary = (UnaryNode) node;
ValueNode value = unary.getValue();
for (InfoElement infoElement : info.getInfoElements(value)) {
Stamp result = unary.foldStamp(infoElement.getStamp());
if (result != null) {
return new Pair<>(infoElement, result);
}
}
Pair<InfoElement, Stamp> foldResult = recursiveFoldStamp(value, info);
if (foldResult != null) {
Stamp result = unary.foldStamp(foldResult.second);
if (result != null) {
return new Pair<>(foldResult.first, result);
}
}
} else if (node instanceof BinaryNode) {
BinaryNode binary = (BinaryNode) node;
ValueNode y = binary.getY();
ValueNode x = binary.getX();
if (y.isConstant()) {
for (InfoElement infoElement : info.getInfoElements(x)) {
Stamp result = binary.foldStamp(infoElement.stamp, y.stamp());
if (result != null) {
return new Pair<>(infoElement, result);
}
}
Pair<InfoElement, Stamp> foldResult = recursiveFoldStamp(x, info);
if (foldResult != null) {
Stamp result = binary.foldStamp(foldResult.second, y.stamp());
if (result != null) {
return new Pair<>(foldResult.first, result);
}
}
} else if (x instanceof LoadFieldNode || y instanceof LoadFieldNode) {
boolean useX = x instanceof LoadFieldNode;
Pair<InfoElement, Stamp> foldResult = recursiveFoldStamp(useX ? x : y, info);
if (foldResult != null) {
Stamp result = binary.foldStamp(useX ? foldResult.second : x.stamp(), useX ? y.stamp() : foldResult.second);
if (result != null) {
return new Pair<>(foldResult.first, result);
}
}
}
}
return null;
}
/**
* Recursively try to fold stamps within this expression using information from
* {@link #getInfoElements(ValueNode)}. It's only safe to use constants and one
* {@link InfoElement} otherwise more than one guard would be required.
*
* @param node
* @return the pair of the @{link InfoElement} used and the stamp produced for the whole
* expression
*/
Pair<InfoElement, Stamp> recursiveFoldStampFromInfo(Node node) {
return recursiveFoldStamp(node, (value) -> getInfoElements(value));
}
/**
* Recursively try to fold stamps within this expression using {@code newStamp} if the
* node {@code original} is encountered in the expression. It's only safe to use
* constants and the passed in stamp otherwise more than one guard would be required.
*
* @param node
* @param original
* @param newStamp
* @return the improved stamp or null is nothing could be done
*/
@SuppressWarnings("unchecked")
Stamp recursiveFoldStamp(Node node, ValueNode original, Stamp newStamp) {
Debug.log("Recursively fold stamp for node %s original %s stamp %s", node, original, newStamp);
InfoElement element = new InfoElement(newStamp, original);
Pair<InfoElement, Stamp> result = recursiveFoldStamp(node, (value) -> value == original ? Collections.singleton(element) : Collections.EMPTY_LIST);
if (result != null) {
return result.second;
}
return null;
}
protected boolean foldPendingTest(DeoptimizingGuard thisGuard, ValueNode original, Stamp newStamp, GuardRewirer rewireGuardFunction) {
for (PendingTest pending : pendingTests) {
TriState result = TriState.UNKNOWN;
if (pending.condition instanceof UnaryOpLogicNode) {
UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) pending.condition;
if (unaryLogicNode.getValue() == original) {
result = unaryLogicNode.tryFold(newStamp);
}
if (!result.isKnown()) {
Stamp foldResult = recursiveFoldStamp(unaryLogicNode.getValue(), original, newStamp);
if (foldResult != null) {
result = unaryLogicNode.tryFold(foldResult);
}
}
} else if (pending.condition instanceof BinaryOpLogicNode) {
BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) pending.condition;
ValueNode x = binaryOpLogicNode.getX();
ValueNode y = binaryOpLogicNode.getY();
if (binaryOpLogicNode.getX() == original) {
result = binaryOpLogicNode.tryFold(newStamp, binaryOpLogicNode.getY().stamp());
} else if (binaryOpLogicNode instanceof IntegerEqualsNode && y.isConstant() && x instanceof AndNode) {
AndNode and = (AndNode) x;
if (and.getY() == y && and.getX() == original) {
BinaryOp<And> andOp = ArithmeticOpTable.forStamp(newStamp).getAnd();
result = binaryOpLogicNode.tryFold(andOp.foldStamp(newStamp, y.stamp()), y.stamp());
}
}
if (!result.isKnown() && y.isConstant()) {
Stamp foldResult = recursiveFoldStamp(x, original, newStamp);
if (foldResult != null) {
result = binaryOpLogicNode.tryFold(foldResult, y.stamp());
}
}
}
if (result.isKnown()) {
/*
* The test case be folded using the information available but the test can
* only be moved up if we're sure there's no schedule dependence. For now
* limit it to the original node and constants.
*/
InputFilter v = new InputFilter(original);
thisGuard.getCondition().applyInputs(v);
if (v.ok && foldGuard(thisGuard, pending.guard, rewireGuardFunction)) {
return true;
}
}
}
return false;
}
protected boolean foldGuard(DeoptimizingGuard thisGuard, DeoptimizingGuard otherGuard, GuardRewirer rewireGuardFunction) {
if (otherGuard.getAction() == thisGuard.getAction() && otherGuard.getReason() == thisGuard.getReason() && otherGuard.getSpeculation() == thisGuard.getSpeculation()) {
LogicNode condition = (LogicNode) thisGuard.getCondition().copyWithInputs();
GuardRewirer rewirer = (guard, result) -> {
if (rewireGuardFunction.rewire(guard, result)) {
otherGuard.setCondition(condition, thisGuard.isNegated());
return true;
}
condition.safeDelete();
return false;
};
// Move the later test up
return rewireGuards(otherGuard.asNode(), !thisGuard.isNegated(), rewirer);
}
return false;
}
protected void registerCondition(LogicNode condition, boolean negated, ValueNode guard) {
registerNewStamp(condition, negated ? StampFactory.contradiction() : StampFactory.tautology(), guard);
}
protected Iterable<InfoElement> getInfoElements(ValueNode proxiedValue) {
ValueNode value = GraphUtil.unproxify(proxiedValue);
if (value == null) {
return Collections.emptyList();
}
Info info = map.get(value);
if (info == null) {
return Collections.emptyList();
} else {
return info.getElements();
}
}
protected boolean rewireGuards(ValueNode guard, boolean result, GuardRewirer rewireGuardFunction) {
assert guard instanceof GuardingNode;
counterStampsFound.increment();
ValueNode proxiedGuard = proxyGuard(guard);
return rewireGuardFunction.rewire(proxiedGuard, result);
}
protected ValueNode proxyGuard(ValueNode guard) {
ValueNode proxiedGuard = guard;
if (!Instance.this.loopExits.isEmpty()) {
while (proxiedGuard instanceof GuardProxyNode) {
proxiedGuard = ((GuardProxyNode) proxiedGuard).value();
}
Block guardBlock = nodeToBlock.apply(proxiedGuard);
assert guardBlock != null;
for (Iterator<LoopExitNode> iter = loopExits.descendingIterator(); iter.hasNext();) {
LoopExitNode loopExitNode = iter.next();
Block loopExitBlock = nodeToBlock.apply(loopExitNode);
if (guardBlock != loopExitBlock && AbstractControlFlowGraph.dominates(guardBlock, loopExitBlock)) {
Block loopBeginBlock = nodeToBlock.apply(loopExitNode.loopBegin());
if (!AbstractControlFlowGraph.dominates(guardBlock, loopBeginBlock) || guardBlock == loopBeginBlock) {
proxiedGuard = proxiedGuard.graph().unique(new GuardProxyNode((GuardingNode) proxiedGuard, loopExitNode));
}
}
}
}
return proxiedGuard;
}
protected boolean tryProveCondition(LogicNode node, GuardRewirer rewireGuardFunction) {
return tryProveGuardCondition(null, node, rewireGuardFunction);
}
protected boolean tryProveGuardCondition(DeoptimizingGuard thisGuard, LogicNode node, GuardRewirer rewireGuardFunction) {
for (InfoElement infoElement : getInfoElements(node)) {
Stamp stamp = infoElement.getStamp();
JavaConstant constant = (JavaConstant) stamp.asConstant();
if (constant != null) {
return rewireGuards(infoElement.getGuard(), constant.asBoolean(), rewireGuardFunction);
}
}
if (node instanceof UnaryOpLogicNode) {
UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) node;
ValueNode value = unaryLogicNode.getValue();
for (InfoElement infoElement : getInfoElements(value)) {
Stamp stamp = infoElement.getStamp();
TriState result = unaryLogicNode.tryFold(stamp);
if (result.isKnown()) {
return rewireGuards(infoElement.getGuard(), result.toBoolean(), rewireGuardFunction);
}
}
Pair<InfoElement, Stamp> foldResult = recursiveFoldStampFromInfo(value);
if (foldResult != null) {
TriState result = unaryLogicNode.tryFold(foldResult.second);
if (result.isKnown()) {
return rewireGuards(foldResult.first.getGuard(), result.toBoolean(), rewireGuardFunction);
}
}
if (thisGuard != null) {
Stamp newStamp = unaryLogicNode.getSucceedingStampForValue(thisGuard.isNegated());
if (newStamp != null && foldPendingTest(thisGuard, value, newStamp, rewireGuardFunction)) {
return true;
}
}
} else if (node instanceof BinaryOpLogicNode) {
BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) node;
for (InfoElement infoElement : getInfoElements(binaryOpLogicNode)) {
if (infoElement.getStamp().equals(StampFactory.contradiction())) {
return rewireGuards(infoElement.getGuard(), false, rewireGuardFunction);
} else if (infoElement.getStamp().equals(StampFactory.tautology())) {
return rewireGuards(infoElement.getGuard(), true, rewireGuardFunction);
}
}
ValueNode x = binaryOpLogicNode.getX();
ValueNode y = binaryOpLogicNode.getY();
for (InfoElement infoElement : getInfoElements(x)) {
TriState result = binaryOpLogicNode.tryFold(infoElement.getStamp(), y.stamp());
if (result.isKnown()) {
return rewireGuards(infoElement.getGuard(), result.toBoolean(), rewireGuardFunction);
}
}
if (y.isConstant()) {
Pair<InfoElement, Stamp> foldResult = recursiveFoldStampFromInfo(x);
if (foldResult != null) {
TriState result = binaryOpLogicNode.tryFold(foldResult.second, y.stamp());
if (result.isKnown()) {
return rewireGuards(foldResult.first.getGuard(), result.toBoolean(), rewireGuardFunction);
}
}
} else {
for (InfoElement infoElement : getInfoElements(y)) {
TriState result = binaryOpLogicNode.tryFold(x.stamp(), infoElement.getStamp());
if (result.isKnown()) {
return rewireGuards(infoElement.getGuard(), result.toBoolean(), rewireGuardFunction);
}
}
}
/*
* For complex expressions involving constants, see if it's possible to fold the
* tests by using stamps one level up in the expression. For instance, (x + n <
* y) might fold if something is known about x and all other values are
* constants. The reason for the constant restriction is that if more than 1
* real value is involved the code might need to adopt multiple guards to have
* proper dependences.
*/
if (x instanceof BinaryArithmeticNode<?> && y.isConstant()) {
BinaryArithmeticNode<?> binary = (BinaryArithmeticNode<?>) x;
if (binary.getY().isConstant()) {
for (InfoElement infoElement : getInfoElements(binary.getX())) {
Stamp newStampX = binary.foldStamp(infoElement.getStamp(), binary.getY().stamp());
TriState result = binaryOpLogicNode.tryFold(newStampX, y.stamp());
if (result.isKnown()) {
return rewireGuards(infoElement.getGuard(), result.toBoolean(), rewireGuardFunction);
}
}
}
}
if (thisGuard != null && binaryOpLogicNode instanceof IntegerEqualsNode && !thisGuard.isNegated()) {
if (y.isConstant() && x instanceof AndNode) {
AndNode and = (AndNode) x;
if (and.getY() == y) {
/*
* This 'and' proves something about some of the bits in and.getX().
* It's equivalent to or'ing in the mask value since those values
* are known to be set.
*/
BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp()).getOr();
IntegerStamp newStampX = (IntegerStamp) op.foldStamp(and.getX().stamp(), y.stamp());
if (foldPendingTest(thisGuard, and.getX(), newStampX, rewireGuardFunction)) {
return true;
}
}
}
}
if (thisGuard != null) {
if (!x.isConstant()) {
Stamp newStampX = binaryOpLogicNode.getSucceedingStampForX(thisGuard.isNegated());
if (newStampX != null && foldPendingTest(thisGuard, x, newStampX, rewireGuardFunction)) {
return true;
}
}
if (!y.isConstant()) {
Stamp newStampY = binaryOpLogicNode.getSucceedingStampForY(thisGuard.isNegated());
if (newStampY != null && foldPendingTest(thisGuard, y, newStampY, rewireGuardFunction)) {
return true;
}
}
}
} else if (node instanceof ShortCircuitOrNode) {
final ShortCircuitOrNode shortCircuitOrNode = (ShortCircuitOrNode) node;
if (Instance.this.loopExits.isEmpty()) {
return tryProveCondition(shortCircuitOrNode.getX(), (guard, result) -> {
if (result == !shortCircuitOrNode.isXNegated()) {
return rewireGuards(guard, true, rewireGuardFunction);
} else {
return tryProveCondition(shortCircuitOrNode.getY(), (innerGuard, innerResult) -> {
if (innerGuard == guard) {
return rewireGuards(guard, innerResult ^ shortCircuitOrNode.isYNegated(), rewireGuardFunction);
}
return false;
});
}
});
}
}
return false;
}
protected void registerNewStamp(ValueNode proxiedValue, Stamp newStamp, ValueNode guard) {
assert proxiedValue != null;
assert guard != null;
if (newStamp != null) {
ValueNode value = GraphUtil.unproxify(proxiedValue);
Info info = map.get(value);
if (info == null) {
info = new Info();
map.set(value, info);
}
counterStampsRegistered.increment();
final Info finalInfo = info;
Debug.log("\t Saving stamp for node %s stamp %s guarded by %s", value, newStamp, guard == null ? "null" : guard);
finalInfo.pushElement(new InfoElement(newStamp, guard));
undoOperations.add(() -> finalInfo.popElement());
}
}
protected void processAbstractBegin(AbstractBeginNode beginNode) {
Node predecessor = beginNode.predecessor();
if (predecessor instanceof IfNode) {
IfNode ifNode = (IfNode) predecessor;
boolean negated = (ifNode.falseSuccessor() == beginNode);
LogicNode condition = ifNode.condition();
registerNewCondition(condition, negated, beginNode);
} else if (predecessor instanceof TypeSwitchNode) {
TypeSwitchNode typeSwitch = (TypeSwitchNode) predecessor;
processTypeSwitch(beginNode, predecessor, typeSwitch);
} else if (predecessor instanceof IntegerSwitchNode) {
IntegerSwitchNode integerSwitchNode = (IntegerSwitchNode) predecessor;
processIntegerSwitch(beginNode, predecessor, integerSwitchNode);
}
}
protected void processIntegerSwitch(AbstractBeginNode beginNode, Node predecessor, IntegerSwitchNode integerSwitchNode) {
Stamp stamp = null;
for (int i = 0; i < integerSwitchNode.keyCount(); i++) {
if (integerSwitchNode.keySuccessor(i) == predecessor) {
if (stamp == null) {
stamp = StampFactory.forConstant(integerSwitchNode.keyAt(i));
} else {
stamp = stamp.meet(StampFactory.forConstant(integerSwitchNode.keyAt(i)));
}
}
}
if (stamp != null) {
registerNewStamp(integerSwitchNode.value(), stamp, beginNode);
}
}
protected void processTypeSwitch(AbstractBeginNode beginNode, Node predecessor, TypeSwitchNode typeSwitch) {
ValueNode hub = typeSwitch.value();
if (hub instanceof LoadHubNode) {
LoadHubNode loadHub = (LoadHubNode) hub;
Stamp stamp = null;
for (int i = 0; i < typeSwitch.keyCount(); i++) {
if (typeSwitch.keySuccessor(i) == predecessor) {
if (stamp == null) {
stamp = StampFactory.objectNonNull(TypeReference.createExactTrusted(typeSwitch.typeAt(i)));
} else {
stamp = stamp.meet(StampFactory.objectNonNull(TypeReference.createExactTrusted(typeSwitch.typeAt(i))));
}
}
}
if (stamp != null) {
registerNewStamp(loadHub.getValue(), stamp, beginNode);
}
}
}
}
}
protected static class Pair<F, S> {
public final F first;
public final S second;
Pair(F first, S second) {
this.first = first;
this.second = second;
}
@Override
public int hashCode() {
return first.hashCode() * 31 ^ second.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Pair<?, ?>) {
Pair<?, ?> other = (Pair<?, ?>) obj;
return this.first.equals(other.first) && this.second.equals(other.second);
}
return false;
}
}
@FunctionalInterface
protected interface InfoElementProvider {
Iterable<InfoElement> getInfoElements(ValueNode value);
}
/**
* Checks for safe nodes when moving pending tests up.
*/
static class InputFilter extends Node.EdgeVisitor {
boolean ok;
private ValueNode value;
InputFilter(ValueNode value) {
this.value = value;
this.ok = true;
}
@Override
public Node apply(Node node, Node curNode) {
if (!(curNode instanceof ValueNode)) {
ok = false;
return curNode;
}
ValueNode curValue = (ValueNode) curNode;
if (curValue.isConstant() || curValue == value || curValue instanceof ParameterNode) {
return curNode;
}
if (curValue instanceof BinaryNode || curValue instanceof UnaryNode) {
curValue.applyInputs(this);
} else {
ok = false;
}
return curNode;
}
}
@FunctionalInterface
protected interface GuardRewirer {
/**
* Called if the condition could be proven to have a constant value ({@code result}) under
* {@code guard}.
*
* Return whether a transformation could be applied.
*/
boolean rewire(ValueNode guard, boolean result);
}
protected static class PendingTest {
private final LogicNode condition;
private final DeoptimizingGuard guard;
public PendingTest(LogicNode condition, DeoptimizingGuard guard) {
this.condition = condition;
this.guard = guard;
}
}
protected static final class InfoElement {
private final Stamp stamp;
private final ValueNode guard;
public InfoElement(Stamp stamp, ValueNode guard) {
this.stamp = stamp;
this.guard = guard;
}
public Stamp getStamp() {
return stamp;
}
public ValueNode getGuard() {
return guard;
}
@Override
public String toString() {
return stamp + " -> " + guard;
}
}
protected static final class Info {
private final ArrayList<InfoElement> infos;
public Info() {
infos = new ArrayList<>();
}
public Iterable<InfoElement> getElements() {
return infos;
}
public void pushElement(InfoElement element) {
Debug.log(Debug.VERBOSE_LOG_LEVEL, "Pushing an info element:%s", element);
infos.add(element);
}
public void popElement() {
infos.remove(infos.size() - 1);
}
}
private static class GuardedConstantStamp extends ObjectStamp {
private final JavaConstant objectConstant;
GuardedConstantStamp(JavaConstant objectConstant, ObjectStamp succeedingStamp) {
super(succeedingStamp.type(), succeedingStamp.isExactType(), succeedingStamp.nonNull(), succeedingStamp.alwaysNull());
this.objectConstant = objectConstant;
}
}
@Override
public float codeSizeIncrease() {
return 1.5f;
}
}