--- /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/DominatorConditionalEliminationPhase.java 2017-02-15 17:08:09.089592297 -0800 @@ -0,0 +1,965 @@ +/* + * 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 { + + 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> blockToNodes; + Function 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> nodes = new BlockMap<>(cfg); + for (Block b : cfg.getBlocks()) { + ArrayList 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 map; + protected Deque loopExits; + protected final Function> blockToNodes; + protected final Function nodeToBlock; + protected final CanonicalizerTool tool; + /** + * Tests which may be eliminated because post dominating tests to prove a broader condition. + */ + private Deque pendingTests; + + public Instance(StructuredGraph graph, Function> blockToNodes, + Function 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 { + protected List 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 oldMap = map; + final Deque 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 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 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 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 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 recursiveFoldStamp(Node node, InfoElementProvider info) { + if (node instanceof LoadFieldNode) { + Pair 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 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 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 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 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 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 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 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 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 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 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 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 { + 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 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 infos; + + public Info() { + infos = new ArrayList<>(); + } + + public Iterable 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; + } +}