/*
 * Copyright (c) 2009, 2015, 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.nodes;

import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_2;
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.graalvm.compiler.core.common.CollectionsFactory;
import org.graalvm.compiler.core.common.calc.Condition;
import org.graalvm.compiler.core.common.type.IntegerStamp;
import org.graalvm.compiler.core.common.type.Stamp;
import org.graalvm.compiler.core.common.type.StampFactory;
import org.graalvm.compiler.debug.Debug;
import org.graalvm.compiler.debug.DebugCounter;
import org.graalvm.compiler.debug.GraalError;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.graph.NodeClass;
import org.graalvm.compiler.graph.iterators.NodeIterable;
import org.graalvm.compiler.graph.spi.Canonicalizable;
import org.graalvm.compiler.graph.spi.Simplifiable;
import org.graalvm.compiler.graph.spi.SimplifierTool;
import org.graalvm.compiler.nodeinfo.InputType;
import org.graalvm.compiler.nodeinfo.NodeInfo;
import org.graalvm.compiler.nodes.calc.CompareNode;
import org.graalvm.compiler.nodes.calc.ConditionalNode;
import org.graalvm.compiler.nodes.calc.IntegerBelowNode;
import org.graalvm.compiler.nodes.calc.IntegerLessThanNode;
import org.graalvm.compiler.nodes.calc.IsNullNode;
import org.graalvm.compiler.nodes.java.InstanceOfNode;
import org.graalvm.compiler.nodes.spi.LIRLowerable;
import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
import org.graalvm.compiler.nodes.util.GraphUtil;

import jdk.vm.ci.meta.Constant;
import jdk.vm.ci.meta.ConstantReflectionProvider;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.PrimitiveConstant;

The IfNode represents a branch that can go one of two directions depending on the outcome of a comparison.
/** * The {@code IfNode} represents a branch that can go one of two directions depending on the outcome * of a comparison. */
@NodeInfo(cycles = CYCLES_2, size = SIZE_2, sizeRationale = "2 jmps") public final class IfNode extends ControlSplitNode implements Simplifiable, LIRLowerable { public static final NodeClass<IfNode> TYPE = NodeClass.create(IfNode.class); private static final DebugCounter CORRECTED_PROBABILITIES = Debug.counter("CorrectedProbabilities"); @Successor AbstractBeginNode trueSuccessor; @Successor AbstractBeginNode falseSuccessor; @Input(InputType.Condition) LogicNode condition; protected double trueSuccessorProbability; public LogicNode condition() { return condition; } public void setCondition(LogicNode x) { updateUsages(condition, x); condition = x; } public IfNode(LogicNode condition, FixedNode trueSuccessor, FixedNode falseSuccessor, double trueSuccessorProbability) { this(condition, BeginNode.begin(trueSuccessor), BeginNode.begin(falseSuccessor), trueSuccessorProbability); } public IfNode(LogicNode condition, AbstractBeginNode trueSuccessor, AbstractBeginNode falseSuccessor, double trueSuccessorProbability) { super(TYPE, StampFactory.forVoid()); this.condition = condition; this.falseSuccessor = falseSuccessor; this.trueSuccessor = trueSuccessor; setTrueSuccessorProbability(trueSuccessorProbability); }
Gets the true successor.
Returns:the true successor
/** * Gets the true successor. * * @return the true successor */
public AbstractBeginNode trueSuccessor() { return trueSuccessor; }
Gets the false successor.
Returns:the false successor
/** * Gets the false successor. * * @return the false successor */
public AbstractBeginNode falseSuccessor() { return falseSuccessor; } public double getTrueSuccessorProbability() { return this.trueSuccessorProbability; } public void setTrueSuccessor(AbstractBeginNode node) { updatePredecessor(trueSuccessor, node); trueSuccessor = node; } public void setFalseSuccessor(AbstractBeginNode node) { updatePredecessor(falseSuccessor, node); falseSuccessor = node; }
Gets the node corresponding to the specified outcome of the branch.
Params:
  • istrue – true if the true successor is requested, false otherwise
Returns:the corresponding successor
/** * Gets the node corresponding to the specified outcome of the branch. * * @param istrue {@code true} if the true successor is requested, {@code false} otherwise * @return the corresponding successor */
public AbstractBeginNode successor(boolean istrue) { return istrue ? trueSuccessor : falseSuccessor; } public void setTrueSuccessorProbability(double prob) { assert prob >= -0.000000001 && prob <= 1.000000001 : "Probability out of bounds: " + prob; trueSuccessorProbability = Math.min(1.0, Math.max(0.0, prob)); } @Override public double probability(AbstractBeginNode successor) { return successor == trueSuccessor ? trueSuccessorProbability : 1 - trueSuccessorProbability; } @Override public void generate(NodeLIRBuilderTool gen) { gen.emitIf(this); } @Override public boolean verify() { assertTrue(condition() != null, "missing condition"); assertTrue(trueSuccessor() != null, "missing trueSuccessor"); assertTrue(falseSuccessor() != null, "missing falseSuccessor"); return super.verify(); } public void eliminateNegation() { AbstractBeginNode oldTrueSuccessor = trueSuccessor; AbstractBeginNode oldFalseSuccessor = falseSuccessor; trueSuccessor = oldFalseSuccessor; falseSuccessor = oldTrueSuccessor; trueSuccessorProbability = 1 - trueSuccessorProbability; setCondition(((LogicNegationNode) condition).getValue()); } @Override public void simplify(SimplifierTool tool) { if (trueSuccessor().next() instanceof DeoptimizeNode) { if (trueSuccessorProbability != 0) { CORRECTED_PROBABILITIES.increment(); trueSuccessorProbability = 0; } } else if (falseSuccessor().next() instanceof DeoptimizeNode) { if (trueSuccessorProbability != 1) { CORRECTED_PROBABILITIES.increment(); trueSuccessorProbability = 1; } } if (condition() instanceof LogicNegationNode) { eliminateNegation(); } if (condition() instanceof LogicConstantNode) { LogicConstantNode c = (LogicConstantNode) condition(); if (c.getValue()) { tool.deleteBranch(falseSuccessor()); tool.addToWorkList(trueSuccessor()); graph().removeSplit(this, trueSuccessor()); } else { tool.deleteBranch(trueSuccessor()); tool.addToWorkList(falseSuccessor()); graph().removeSplit(this, falseSuccessor()); } return; } if (tool.allUsagesAvailable() && trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages()) { pushNodesThroughIf(tool); if (checkForUnsignedCompare(tool) || removeOrMaterializeIf(tool)) { return; } } if (removeIntermediateMaterialization(tool)) { return; } if (splitIfAtPhi(tool)) { return; } if (conditionalNodeOptimization(tool)) { return; } if (falseSuccessor().hasNoUsages() && (!(falseSuccessor() instanceof LoopExitNode)) && falseSuccessor().next() instanceof IfNode) { AbstractBeginNode intermediateBegin = falseSuccessor(); IfNode nextIf = (IfNode) intermediateBegin.next(); double probabilityB = (1.0 - this.trueSuccessorProbability) * nextIf.trueSuccessorProbability; if (this.trueSuccessorProbability < probabilityB) { // Reordering of those two if statements is beneficial from the point of view of // their probabilities. if (prepareForSwap(tool.getConstantReflection(), condition(), nextIf.condition())) { // Reordering is allowed from (if1 => begin => if2) to (if2 => begin => if1). assert intermediateBegin.next() == nextIf; AbstractBeginNode bothFalseBegin = nextIf.falseSuccessor(); nextIf.setFalseSuccessor(null); intermediateBegin.setNext(null); this.setFalseSuccessor(null); this.replaceAtPredecessor(nextIf); nextIf.setFalseSuccessor(intermediateBegin); intermediateBegin.setNext(this); this.setFalseSuccessor(bothFalseBegin); nextIf.setTrueSuccessorProbability(probabilityB); if (probabilityB == 1.0) { this.setTrueSuccessorProbability(0.0); } else { double newProbability = this.trueSuccessorProbability / (1.0 - probabilityB); this.setTrueSuccessorProbability(Math.min(1.0, newProbability)); } return; } } } }
Try to optimize this as if it were a ConditionalNode.
/** * Try to optimize this as if it were a {@link ConditionalNode}. */
private boolean conditionalNodeOptimization(SimplifierTool tool) { if (trueSuccessor().next() instanceof AbstractEndNode && falseSuccessor().next() instanceof AbstractEndNode) { AbstractEndNode trueEnd = (AbstractEndNode) trueSuccessor().next(); AbstractEndNode falseEnd = (AbstractEndNode) falseSuccessor().next(); if (trueEnd.merge() != falseEnd.merge()) { return false; } if (!(trueEnd.merge() instanceof MergeNode)) { return false; } MergeNode merge = (MergeNode) trueEnd.merge(); if (merge.usages().count() != 1 || merge.phis().count() != 1) { return false; } PhiNode phi = merge.phis().first(); ValueNode falseValue = phi.valueAt(falseEnd); ValueNode trueValue = phi.valueAt(trueEnd); ValueNode result = ConditionalNode.canonicalizeConditional(condition, trueValue, falseValue, phi.stamp()); if (result != null) { /* * canonicalizeConditional returns possibly new nodes so add them to the graph. */ if (result.graph() == null) { result = graph().addOrUniqueWithInputs(result); } /* * This optimization can be performed even if multiple values merge at this phi * since the two inputs get simplified into one. */ phi.setValueAt(trueEnd, result); removeThroughFalseBranch(tool); return true; } } return false; } private void pushNodesThroughIf(SimplifierTool tool) { assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages(); // push similar nodes upwards through the if, thereby deduplicating them do { AbstractBeginNode trueSucc = trueSuccessor(); AbstractBeginNode falseSucc = falseSuccessor(); if (trueSucc instanceof BeginNode && falseSucc instanceof BeginNode && trueSucc.next() instanceof FixedWithNextNode && falseSucc.next() instanceof FixedWithNextNode) { FixedWithNextNode trueNext = (FixedWithNextNode) trueSucc.next(); FixedWithNextNode falseNext = (FixedWithNextNode) falseSucc.next(); NodeClass<?> nodeClass = trueNext.getNodeClass(); if (trueNext.getClass() == falseNext.getClass()) { if (nodeClass.equalInputs(trueNext, falseNext) && trueNext.valueEquals(falseNext)) { falseNext.replaceAtUsages(trueNext); graph().removeFixed(falseNext); GraphUtil.unlinkFixedNode(trueNext); graph().addBeforeFixed(this, trueNext); for (Node usage : trueNext.usages().snapshot()) { if (usage.isAlive()) { NodeClass<?> usageNodeClass = usage.getNodeClass(); if (usageNodeClass.valueNumberable() && !usageNodeClass.isLeafNode()) { Node newNode = graph().findDuplicate(usage); if (newNode != null) { usage.replaceAtUsagesAndDelete(newNode); } } if (usage.isAlive()) { tool.addToWorkList(usage); } } } continue; } } } break; } while (true); }
Recognize a couple patterns that can be merged into an unsigned compare.
Params:
  • tool –
Returns:true if a replacement was done.
/** * Recognize a couple patterns that can be merged into an unsigned compare. * * @param tool * @return true if a replacement was done. */
private boolean checkForUnsignedCompare(SimplifierTool tool) { assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages(); if (condition() instanceof IntegerLessThanNode) { IntegerLessThanNode lessThan = (IntegerLessThanNode) condition(); Constant y = lessThan.getY().stamp().asConstant(); if (y instanceof PrimitiveConstant && ((PrimitiveConstant) y).asLong() == 0 && falseSuccessor().next() instanceof IfNode) { IfNode ifNode2 = (IfNode) falseSuccessor().next(); if (ifNode2.condition() instanceof IntegerLessThanNode) { IntegerLessThanNode lessThan2 = (IntegerLessThanNode) ifNode2.condition(); AbstractBeginNode falseSucc = ifNode2.falseSuccessor(); AbstractBeginNode trueSucc = ifNode2.trueSuccessor(); IntegerBelowNode below = null; /* * Convert x >= 0 && x < positive which is represented as !(x < 0) && x < * <positive> into an unsigned compare. */ if (lessThan2.getX() == lessThan.getX() && lessThan2.getY().stamp() instanceof IntegerStamp && ((IntegerStamp) lessThan2.getY().stamp()).isPositive() && sameDestination(trueSuccessor(), ifNode2.falseSuccessor)) { below = graph().unique(new IntegerBelowNode(lessThan2.getX(), lessThan2.getY())); // swap direction AbstractBeginNode tmp = falseSucc; falseSucc = trueSucc; trueSucc = tmp; } else if (lessThan2.getY() == lessThan.getX() && sameDestination(trueSuccessor(), ifNode2.trueSuccessor)) { /* * Convert x >= 0 && x <= positive which is represented as !(x < 0) && * !(<positive> > x), into x <| positive + 1. This can only be done for * constants since there isn't a IntegerBelowEqualThanNode but that doesn't * appear to be interesting. */ JavaConstant positive = lessThan2.getX().asJavaConstant(); if (positive != null && positive.asLong() > 0 && positive.asLong() < positive.getJavaKind().getMaxValue()) { ConstantNode newLimit = ConstantNode.forIntegerStamp(lessThan2.getX().stamp(), positive.asLong() + 1, graph()); below = graph().unique(new IntegerBelowNode(lessThan.getX(), newLimit)); } } if (below != null) { ifNode2.setTrueSuccessor(null); ifNode2.setFalseSuccessor(null); IfNode newIfNode = graph().add(new IfNode(below, falseSucc, trueSucc, 1 - trueSuccessorProbability)); // Remove the < 0 test. tool.deleteBranch(trueSuccessor); graph().removeSplit(this, falseSuccessor); // Replace the second test with the new one. ifNode2.predecessor().replaceFirstSuccessor(ifNode2, newIfNode); ifNode2.safeDelete(); return true; } } } } return false; }
Check it these two blocks end up at the same place. Meeting at the same merge, or deoptimizing in the same way.
/** * Check it these two blocks end up at the same place. Meeting at the same merge, or * deoptimizing in the same way. */
private static boolean sameDestination(AbstractBeginNode succ1, AbstractBeginNode succ2) { Node next1 = succ1.next(); Node next2 = succ2.next(); if (next1 instanceof EndNode && next2 instanceof EndNode) { EndNode end1 = (EndNode) next1; EndNode end2 = (EndNode) next2; if (end1.merge() == end2.merge()) { for (PhiNode phi : end1.merge().phis()) { if (phi.valueAt(end1) != phi.valueAt(end2)) { return false; } } // They go to the same MergeNode and merge the same values return true; } } else if (next1 instanceof DeoptimizeNode && next2 instanceof DeoptimizeNode) { DeoptimizeNode deopt1 = (DeoptimizeNode) next1; DeoptimizeNode deopt2 = (DeoptimizeNode) next2; if (deopt1.reason() == deopt2.reason() && deopt1.action() == deopt2.action()) { // Same deoptimization reason and action. return true; } } else if (next1 instanceof LoopExitNode && next2 instanceof LoopExitNode) { LoopExitNode exit1 = (LoopExitNode) next1; LoopExitNode exit2 = (LoopExitNode) next2; if (exit1.loopBegin() == exit2.loopBegin() && exit1.stateAfter() == exit2.stateAfter() && exit1.stateAfter() == null && sameDestination(exit1, exit2)) { // Exit the same loop and end up at the same place. return true; } } else if (next1 instanceof ReturnNode && next2 instanceof ReturnNode) { ReturnNode exit1 = (ReturnNode) next1; ReturnNode exit2 = (ReturnNode) next2; if (exit1.result() == exit2.result()) { // Exit the same loop and end up at the same place. return true; } } return false; } private static boolean prepareForSwap(ConstantReflectionProvider constantReflection, LogicNode a, LogicNode b) { if (a instanceof InstanceOfNode) { InstanceOfNode instanceOfA = (InstanceOfNode) a; if (b instanceof IsNullNode) { IsNullNode isNullNode = (IsNullNode) b; if (isNullNode.getValue() == instanceOfA.getValue()) { Debug.log("Can swap instanceof and isnull if"); return true; } } else if (b instanceof InstanceOfNode) { InstanceOfNode instanceOfB = (InstanceOfNode) b; if (instanceOfA.getValue() == instanceOfB.getValue() && !instanceOfA.type().getType().isInterface() && !instanceOfB.type().getType().isInterface() && !instanceOfA.type().getType().isAssignableFrom(instanceOfB.type().getType()) && !instanceOfB.type().getType().isAssignableFrom(instanceOfA.type().getType())) { // Two instanceof on the same value with mutually exclusive types. Debug.log("Can swap instanceof for types %s and %s", instanceOfA.type(), instanceOfB.type()); return true; } } } else if (a instanceof CompareNode) { CompareNode compareA = (CompareNode) a; Condition conditionA = compareA.condition(); if (compareA.unorderedIsTrue()) { return false; } if (b instanceof CompareNode) { CompareNode compareB = (CompareNode) b; if (compareA == compareB) { Debug.log("Same conditions => do not swap and leave the work for global value numbering."); return false; } if (compareB.unorderedIsTrue()) { return false; } Condition comparableCondition = null; Condition conditionB = compareB.condition(); if (compareB.getX() == compareA.getX() && compareB.getY() == compareA.getY()) { comparableCondition = conditionB; } else if (compareB.getX() == compareA.getY() && compareB.getY() == compareA.getX()) { comparableCondition = conditionB.mirror(); } if (comparableCondition != null) { Condition combined = conditionA.join(comparableCondition); if (combined == null) { // The two conditions are disjoint => can reorder. Debug.log("Can swap disjoint coditions on same values: %s and %s", conditionA, comparableCondition); return true; } } else if (conditionA == Condition.EQ && conditionB == Condition.EQ) { boolean canSwap = false; if ((compareA.getX() == compareB.getX() && valuesDistinct(constantReflection, compareA.getY(), compareB.getY()))) { canSwap = true; } else if ((compareA.getX() == compareB.getY() && valuesDistinct(constantReflection, compareA.getY(), compareB.getX()))) { canSwap = true; } else if ((compareA.getY() == compareB.getX() && valuesDistinct(constantReflection, compareA.getX(), compareB.getY()))) { canSwap = true; } else if ((compareA.getY() == compareB.getY() && valuesDistinct(constantReflection, compareA.getX(), compareB.getX()))) { canSwap = true; } if (canSwap) { Debug.log("Can swap equality condition with one shared and one disjoint value."); return true; } } } } return false; } private static boolean valuesDistinct(ConstantReflectionProvider constantReflection, ValueNode a, ValueNode b) { if (a.isConstant() && b.isConstant()) { Boolean equal = constantReflection.constantEquals(a.asConstant(), b.asConstant()); if (equal != null) { return !equal.booleanValue(); } } Stamp stampA = a.stamp(); Stamp stampB = b.stamp(); return stampA.alwaysDistinct(stampB); }
Tries to remove an empty if construct or replace an if construct with a materialization.
Returns:true if a transformation was made, false otherwise
/** * Tries to remove an empty if construct or replace an if construct with a materialization. * * @return true if a transformation was made, false otherwise */
private boolean removeOrMaterializeIf(SimplifierTool tool) { assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages(); if (trueSuccessor().next() instanceof AbstractEndNode && falseSuccessor().next() instanceof AbstractEndNode) { AbstractEndNode trueEnd = (AbstractEndNode) trueSuccessor().next(); AbstractEndNode falseEnd = (AbstractEndNode) falseSuccessor().next(); AbstractMergeNode merge = trueEnd.merge(); if (merge == falseEnd.merge() && trueSuccessor().anchored().isEmpty() && falseSuccessor().anchored().isEmpty()) { PhiNode singlePhi = null; int distinct = 0; for (PhiNode phi : merge.phis()) { ValueNode trueValue = phi.valueAt(trueEnd); ValueNode falseValue = phi.valueAt(falseEnd); if (trueValue != falseValue) { distinct++; singlePhi = phi; } } if (distinct == 0) { /* * Multiple phis but merging same values for true and false, so simply delete * the path */ removeThroughFalseBranch(tool); return true; } else if (distinct == 1) { ValueNode trueValue = singlePhi.valueAt(trueEnd); ValueNode falseValue = singlePhi.valueAt(falseEnd); ConditionalNode conditional = canonicalizeConditionalCascade(trueValue, falseValue); if (conditional != null) { singlePhi.setValueAt(trueEnd, conditional); removeThroughFalseBranch(tool); return true; } } } } if (trueSuccessor().next() instanceof ReturnNode && falseSuccessor().next() instanceof ReturnNode) { ReturnNode trueEnd = (ReturnNode) trueSuccessor().next(); ReturnNode falseEnd = (ReturnNode) falseSuccessor().next(); ValueNode trueValue = trueEnd.result(); ValueNode falseValue = falseEnd.result(); ValueNode value = null; if (trueValue != null) { if (trueValue == falseValue) { value = trueValue; } else { value = canonicalizeConditionalCascade(trueValue, falseValue); if (value == null) { return false; } } } ReturnNode newReturn = graph().add(new ReturnNode(value)); replaceAtPredecessor(newReturn); GraphUtil.killCFG(this); return true; } return false; } protected void removeThroughFalseBranch(SimplifierTool tool) { AbstractBeginNode trueBegin = trueSuccessor(); graph().removeSplitPropagate(this, trueBegin, tool); tool.addToWorkList(trueBegin); if (condition() != null && condition().isAlive() && condition().hasNoUsages()) { GraphUtil.killWithUnusedFloatingInputs(condition()); } } private ConditionalNode canonicalizeConditionalCascade(ValueNode trueValue, ValueNode falseValue) { if (trueValue.getStackKind() != falseValue.getStackKind()) { return null; } if (trueValue.getStackKind() != JavaKind.Int && trueValue.getStackKind() != JavaKind.Long) { return null; } if (trueValue.isConstant() && falseValue.isConstant()) { return graph().unique(new ConditionalNode(condition(), trueValue, falseValue)); } else { ConditionalNode conditional = null; ValueNode constant = null; boolean negateCondition; if (trueValue instanceof ConditionalNode && falseValue.isConstant()) { conditional = (ConditionalNode) trueValue; constant = falseValue; negateCondition = true; } else if (falseValue instanceof ConditionalNode && trueValue.isConstant()) { conditional = (ConditionalNode) falseValue; constant = trueValue; negateCondition = false; } else { return null; } boolean negateConditionalCondition; ValueNode otherValue; if (constant == conditional.trueValue()) { otherValue = conditional.falseValue(); negateConditionalCondition = false; } else if (constant == conditional.falseValue()) { otherValue = conditional.trueValue(); negateConditionalCondition = true; } else { return null; } if (otherValue.isConstant()) { double shortCutProbability = probability(trueSuccessor()); LogicNode newCondition = LogicNode.or(condition(), negateCondition, conditional.condition(), negateConditionalCondition, shortCutProbability); return graph().unique(new ConditionalNode(newCondition, constant, otherValue)); } } return null; }
Take an if that is immediately dominated by a merge with a single phi and split off any paths where the test would be statically decidable creating a new merge below the approriate side of the IfNode. Any undecidable tests will continue to use the original IfNode.
Params:
  • tool –
/** * Take an if that is immediately dominated by a merge with a single phi and split off any paths * where the test would be statically decidable creating a new merge below the approriate side * of the IfNode. Any undecidable tests will continue to use the original IfNode. * * @param tool */
private boolean splitIfAtPhi(SimplifierTool tool) { if (!(predecessor() instanceof MergeNode)) { return false; } MergeNode merge = (MergeNode) predecessor(); if (merge.forwardEndCount() == 1) { // Don't bother. return false; } if (merge.usages().count() != 1 || merge.phis().count() != 1) { return false; } if (merge.stateAfter() != null) { /* We'll get the chance to simplify this after frame state assignment. */ return false; } PhiNode phi = merge.phis().first(); if (phi.usages().count() != 1) { /* * For simplicity the below code assumes assumes the phi goes dead at the end so skip * this case. */ return false; } /* * Check that the condition uses the phi and that there is only one user of the condition * expression. */ if (!conditionUses(condition(), phi)) { return false; } /* * We could additionally filter for the case that at least some of the Phi inputs or one of * the condition inputs are constants but there are cases where a non-constant is * simplifiable, usually where the stamp allows the question to be answered. */ /* Each successor of the if gets a new merge if needed. */ MergeNode trueMerge = null; MergeNode falseMerge = null; assert merge.stateAfter() == null; for (EndNode end : merge.forwardEnds().snapshot()) { Node value = phi.valueAt(end); LogicNode result = computeCondition(tool, condition, phi, value); if (result instanceof LogicConstantNode) { merge.removeEnd(end); if (((LogicConstantNode) result).getValue()) { if (trueMerge == null) { trueMerge = insertMerge(trueSuccessor()); } trueMerge.addForwardEnd(end); } else { if (falseMerge == null) { falseMerge = insertMerge(falseSuccessor()); } falseMerge.addForwardEnd(end); } } else if (result != condition) { // Build a new IfNode using the new condition BeginNode trueBegin = graph().add(new BeginNode()); BeginNode falseBegin = graph().add(new BeginNode()); if (result.graph() == null) { result = graph().addOrUniqueWithInputs(result); } IfNode newIfNode = graph().add(new IfNode(result, trueBegin, falseBegin, trueSuccessorProbability)); merge.removeEnd(end); ((FixedWithNextNode) end.predecessor()).setNext(newIfNode); if (trueMerge == null) { trueMerge = insertMerge(trueSuccessor()); } trueBegin.setNext(graph().add(new EndNode())); trueMerge.addForwardEnd((EndNode) trueBegin.next()); if (falseMerge == null) { falseMerge = insertMerge(falseSuccessor()); } falseBegin.setNext(graph().add(new EndNode())); falseMerge.addForwardEnd((EndNode) falseBegin.next()); end.safeDelete(); } } transferProxies(trueSuccessor(), trueMerge); transferProxies(falseSuccessor(), falseMerge); cleanupMerge(tool, merge); cleanupMerge(tool, trueMerge); cleanupMerge(tool, falseMerge); return true; }
Params:
  • condition –
  • phi –
Returns:true if the passed in condition uses phi and the condition is only used once. Since the phi will go dead the condition using it will also have to be dead after the optimization.
/** * @param condition * @param phi * @return true if the passed in {@code condition} uses {@code phi} and the condition is only * used once. Since the phi will go dead the condition using it will also have to be * dead after the optimization. */
private static boolean conditionUses(LogicNode condition, PhiNode phi) { if (condition.usages().count() != 1) { return false; } if (condition instanceof ShortCircuitOrNode) { if (condition.graph().getGuardsStage().areDeoptsFixed()) { /* * It can be unsafe to simplify a ShortCircuitOr before deopts are fixed because * conversion to guards assumes that all the required conditions are being tested. * Simplfying the condition based on context before this happens may lose a * condition. */ ShortCircuitOrNode orNode = (ShortCircuitOrNode) condition; return (conditionUses(orNode.x, phi) || conditionUses(orNode.y, phi)); } } else if (condition instanceof Canonicalizable.Unary<?>) { Canonicalizable.Unary<?> unary = (Canonicalizable.Unary<?>) condition; return unary.getValue() == phi; } else if (condition instanceof Canonicalizable.Binary<?>) { Canonicalizable.Binary<?> binary = (Canonicalizable.Binary<?>) condition; return binary.getX() == phi || binary.getY() == phi; } return false; }
Canonicalize condition using value in place of phi.
Params:
  • tool –
  • condition –
  • phi –
  • value –
Returns:an improved LogicNode or the original condition
/** * Canonicalize {@code} condition using {@code value} in place of {@code phi}. * * @param tool * @param condition * @param phi * @param value * @return an improved LogicNode or the original condition */
@SuppressWarnings("unchecked") private static LogicNode computeCondition(SimplifierTool tool, LogicNode condition, PhiNode phi, Node value) { if (condition instanceof ShortCircuitOrNode) { if (condition.graph().getGuardsStage().areDeoptsFixed()) { ShortCircuitOrNode orNode = (ShortCircuitOrNode) condition; LogicNode resultX = computeCondition(tool, orNode.x, phi, value); LogicNode resultY = computeCondition(tool, orNode.y, phi, value); if (resultX != orNode.x || resultY != orNode.y) { LogicNode result = orNode.canonical(tool, resultX, resultY); if (result != orNode) { return result; } /* * Create a new node to carry the optimized inputs. */ ShortCircuitOrNode newOr = new ShortCircuitOrNode(resultX, orNode.xNegated, resultY, orNode.yNegated, orNode.getShortCircuitProbability()); return newOr.canonical(tool); } return orNode; } } else if (condition instanceof Canonicalizable.Binary<?>) { Canonicalizable.Binary<Node> compare = (Canonicalizable.Binary<Node>) condition; if (compare.getX() == phi) { return (LogicNode) compare.canonical(tool, value, compare.getY()); } else if (compare.getY() == phi) { return (LogicNode) compare.canonical(tool, compare.getX(), value); } } else if (condition instanceof Canonicalizable.Unary<?>) { Canonicalizable.Unary<Node> compare = (Canonicalizable.Unary<Node>) condition; if (compare.getValue() == phi) { return (LogicNode) compare.canonical(tool, value); } } if (condition instanceof Canonicalizable) { return (LogicNode) ((Canonicalizable) condition).canonical(tool); } return condition; } private static void transferProxies(AbstractBeginNode successor, MergeNode falseMerge) { if (successor instanceof LoopExitNode && falseMerge != null) { LoopExitNode loopExitNode = (LoopExitNode) successor; for (ProxyNode proxy : loopExitNode.proxies().snapshot()) { proxy.replaceFirstInput(successor, falseMerge); } } } private void cleanupMerge(SimplifierTool tool, MergeNode merge) { if (merge != null && merge.isAlive()) { if (merge.forwardEndCount() == 0) { GraphUtil.killCFG(merge, tool); } else if (merge.forwardEndCount() == 1) { graph().reduceTrivialMerge(merge); } } } private MergeNode insertMerge(AbstractBeginNode begin) { MergeNode merge = graph().add(new MergeNode()); if (!begin.anchored().isEmpty()) { Object before = null; before = begin.anchored().snapshot(); begin.replaceAtUsages(InputType.Guard, merge); begin.replaceAtUsages(InputType.Anchor, merge); assert begin.anchored().isEmpty() : before + " " + begin.anchored().snapshot(); } AbstractBeginNode theBegin = begin; if (begin instanceof LoopExitNode) { // Insert an extra begin to make it easier. theBegin = graph().add(new BeginNode()); begin.replaceAtPredecessor(theBegin); theBegin.setNext(begin); } FixedNode next = theBegin.next(); next.replaceAtPredecessor(merge); theBegin.setNext(graph().add(new EndNode())); merge.addForwardEnd((EndNode) theBegin.next()); merge.setNext(next); return merge; }
Tries to connect code that initializes a variable directly with the successors of an if construct that switches on the variable. For example, the pseudo code below:
contains(list, e, yes, no) {
    if (list == null || e == null) {
        condition = false;
    } else {
        condition = false;
        for (i in list) {
            if (i.equals(e)) {
                condition = true;
                break;
            }
        }
    }
    if (condition) {
        return yes;
    } else {
        return no;
    }
}
will be transformed into:
contains(list, e, yes, no) {
    if (list == null || e == null) {
        return no;
    } else {
        condition = false;
        for (i in list) {
            if (i.equals(e)) {
                return yes;
            }
        }
        return no;
    }
}
Returns:true if a transformation was made, false otherwise
/** * Tries to connect code that initializes a variable directly with the successors of an if * construct that switches on the variable. For example, the pseudo code below: * * <pre> * contains(list, e, yes, no) { * if (list == null || e == null) { * condition = false; * } else { * condition = false; * for (i in list) { * if (i.equals(e)) { * condition = true; * break; * } * } * } * if (condition) { * return yes; * } else { * return no; * } * } * </pre> * * will be transformed into: * * <pre> * contains(list, e, yes, no) { * if (list == null || e == null) { * return no; * } else { * condition = false; * for (i in list) { * if (i.equals(e)) { * return yes; * } * } * return no; * } * } * </pre> * * @return true if a transformation was made, false otherwise */
private boolean removeIntermediateMaterialization(SimplifierTool tool) { if (!(predecessor() instanceof AbstractMergeNode) || predecessor() instanceof LoopBeginNode) { return false; } AbstractMergeNode merge = (AbstractMergeNode) predecessor(); if (!(condition() instanceof CompareNode)) { return false; } CompareNode compare = (CompareNode) condition(); if (compare.getUsageCount() != 1) { return false; } // Only consider merges with a single usage that is both a phi and an operand of the // comparison NodeIterable<Node> mergeUsages = merge.usages(); if (mergeUsages.count() != 1) { return false; } Node singleUsage = mergeUsages.first(); if (!(singleUsage instanceof ValuePhiNode) || (singleUsage != compare.getX() && singleUsage != compare.getY())) { return false; } // Ensure phi is used by at most the comparison and the merge's frame state (if any) ValuePhiNode phi = (ValuePhiNode) singleUsage; NodeIterable<Node> phiUsages = phi.usages(); if (phiUsages.count() > 2) { return false; } for (Node usage : phiUsages) { if (usage != compare && usage != merge.stateAfter()) { return false; } } List<EndNode> mergePredecessors = merge.cfgPredecessors().snapshot(); assert phi.valueCount() == merge.forwardEndCount(); Constant[] xs = constantValues(compare.getX(), merge, false); Constant[] ys = constantValues(compare.getY(), merge, false); if (xs == null || ys == null) { return false; } // Sanity check that both ends are not followed by a merge without frame state. if (!checkFrameState(trueSuccessor()) && !checkFrameState(falseSuccessor())) { return false; } List<EndNode> falseEnds = new ArrayList<>(mergePredecessors.size()); List<EndNode> trueEnds = new ArrayList<>(mergePredecessors.size()); Map<AbstractEndNode, ValueNode> phiValues = CollectionsFactory.newMap(mergePredecessors.size()); AbstractBeginNode oldFalseSuccessor = falseSuccessor(); AbstractBeginNode oldTrueSuccessor = trueSuccessor(); setFalseSuccessor(null); setTrueSuccessor(null); Iterator<EndNode> ends = mergePredecessors.iterator(); for (int i = 0; i < xs.length; i++) { EndNode end = ends.next(); phiValues.put(end, phi.valueAt(end)); if (compare.condition().foldCondition(xs[i], ys[i], tool.getConstantReflection(), compare.unorderedIsTrue())) { trueEnds.add(end); } else { falseEnds.add(end); } } assert !ends.hasNext(); assert falseEnds.size() + trueEnds.size() == xs.length; connectEnds(falseEnds, phiValues, oldFalseSuccessor, merge, tool); connectEnds(trueEnds, phiValues, oldTrueSuccessor, merge, tool); if (this.trueSuccessorProbability == 0.0) { for (AbstractEndNode endNode : trueEnds) { propagateZeroProbability(endNode); } } if (this.trueSuccessorProbability == 1.0) { for (AbstractEndNode endNode : falseEnds) { propagateZeroProbability(endNode); } } /* * Remove obsolete ends only after processing all ends, otherwise oldTrueSuccessor or * oldFalseSuccessor might have been removed if it is a LoopExitNode. */ if (falseEnds.isEmpty()) { GraphUtil.killCFG(oldFalseSuccessor); } if (trueEnds.isEmpty()) { GraphUtil.killCFG(oldTrueSuccessor); } GraphUtil.killCFG(merge); assert !merge.isAlive() : merge; assert !phi.isAlive() : phi; assert !compare.isAlive() : compare; assert !this.isAlive() : this; return true; } private void propagateZeroProbability(FixedNode startNode) { Node prev = null; for (FixedNode node : GraphUtil.predecessorIterable(startNode)) { if (node instanceof IfNode) { IfNode ifNode = (IfNode) node; if (ifNode.trueSuccessor() == prev) { if (ifNode.trueSuccessorProbability == 0.0) { return; } else if (ifNode.trueSuccessorProbability == 1.0) { continue; } else { ifNode.setTrueSuccessorProbability(0.0); return; } } else if (ifNode.falseSuccessor() == prev) { if (ifNode.trueSuccessorProbability == 1.0) { return; } else if (ifNode.trueSuccessorProbability == 0.0) { continue; } else { ifNode.setTrueSuccessorProbability(1.0); return; } } else { throw new GraalError("Illegal state"); } } else if (node instanceof AbstractMergeNode && !(node instanceof LoopBeginNode)) { for (AbstractEndNode endNode : ((AbstractMergeNode) node).cfgPredecessors()) { propagateZeroProbability(endNode); } return; } prev = node; } } private static boolean checkFrameState(FixedNode start) { FixedNode node = start; while (true) { if (node instanceof AbstractMergeNode) { AbstractMergeNode mergeNode = (AbstractMergeNode) node; if (mergeNode.stateAfter() == null) { return false; } else { return true; } } else if (node instanceof StateSplit) { StateSplit stateSplitNode = (StateSplit) node; if (stateSplitNode.stateAfter() != null) { return true; } } if (node instanceof ControlSplitNode) { ControlSplitNode controlSplitNode = (ControlSplitNode) node; for (Node succ : controlSplitNode.cfgSuccessors()) { if (checkFrameState((FixedNode) succ)) { return true; } } return false; } else if (node instanceof FixedWithNextNode) { FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) node; node = fixedWithNextNode.next(); } else if (node instanceof AbstractEndNode) { AbstractEndNode endNode = (AbstractEndNode) node; node = endNode.merge(); } else if (node instanceof ControlSinkNode) { return true; } else { return false; } } }
Connects a set of ends to a given successor, inserting a merge node if there is more than one end. If ends is not empty, then successor is added to tool's work list.
Params:
  • oldMerge – the merge being removed
  • phiValues – the values of the phi at the merge, keyed by the merge ends
/** * Connects a set of ends to a given successor, inserting a merge node if there is more than one * end. If {@code ends} is not empty, then {@code successor} is added to {@code tool}'s * {@linkplain SimplifierTool#addToWorkList(org.graalvm.compiler.graph.Node) work list}. * * @param oldMerge the merge being removed * @param phiValues the values of the phi at the merge, keyed by the merge ends */
private void connectEnds(List<EndNode> ends, Map<AbstractEndNode, ValueNode> phiValues, AbstractBeginNode successor, AbstractMergeNode oldMerge, SimplifierTool tool) { if (!ends.isEmpty()) { if (ends.size() == 1) { AbstractEndNode end = ends.get(0); ((FixedWithNextNode) end.predecessor()).setNext(successor); oldMerge.removeEnd(end); GraphUtil.killCFG(end); } else { // Need a new phi in case the frame state is used by more than the merge being // removed AbstractMergeNode newMerge = graph().add(new MergeNode()); PhiNode oldPhi = (PhiNode) oldMerge.usages().first(); PhiNode newPhi = graph().addWithoutUnique(new ValuePhiNode(oldPhi.stamp(), newMerge)); for (EndNode end : ends) { newPhi.addInput(phiValues.get(end)); newMerge.addForwardEnd(end); } FrameState stateAfter = oldMerge.stateAfter(); if (stateAfter != null) { stateAfter = stateAfter.duplicate(); stateAfter.replaceFirstInput(oldPhi, newPhi); newMerge.setStateAfter(stateAfter); } newMerge.setNext(successor); } tool.addToWorkList(successor); } }
Gets an array of constants derived from a node that is either a ConstantNode or a PhiNode whose input values are all constants. The length of the returned array is equal to the number of ends terminating in a given merge node.
Returns:null if node is neither a ConstantNode nor a PhiNode whose input values are all constants
/** * Gets an array of constants derived from a node that is either a {@link ConstantNode} or a * {@link PhiNode} whose input values are all constants. The length of the returned array is * equal to the number of ends terminating in a given merge node. * * @return null if {@code node} is neither a {@link ConstantNode} nor a {@link PhiNode} whose * input values are all constants */
public static Constant[] constantValues(ValueNode node, AbstractMergeNode merge, boolean allowNull) { if (node.isConstant()) { Constant[] result = new Constant[merge.forwardEndCount()]; Arrays.fill(result, node.asConstant()); return result; } if (node instanceof PhiNode) { PhiNode phi = (PhiNode) node; if (phi.merge() == merge && phi instanceof ValuePhiNode && phi.valueCount() == merge.forwardEndCount()) { Constant[] result = new Constant[merge.forwardEndCount()]; int i = 0; for (ValueNode n : phi.values()) { if (!allowNull && !n.isConstant()) { return null; } result[i++] = n.asConstant(); } return result; } } return null; } @Override public AbstractBeginNode getPrimarySuccessor() { return this.trueSuccessor(); } public AbstractBeginNode getSuccessor(boolean result) { return result ? this.trueSuccessor() : this.falseSuccessor(); } }