1 /*
   2  * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.nodes;
  24 
  25 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_1;
  26 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2;
  27 
  28 import java.util.ArrayList;
  29 import java.util.Arrays;
  30 import java.util.Iterator;
  31 import java.util.List;
  32 
  33 import jdk.vm.ci.meta.MetaAccessProvider;
  34 import jdk.vm.ci.meta.ResolvedJavaType;
  35 import org.graalvm.compiler.core.common.calc.Condition;
  36 import org.graalvm.compiler.core.common.type.IntegerStamp;
  37 import org.graalvm.compiler.core.common.type.Stamp;
  38 import org.graalvm.compiler.core.common.type.StampFactory;
  39 import org.graalvm.compiler.debug.CounterKey;
  40 import org.graalvm.compiler.debug.DebugContext;
  41 import org.graalvm.compiler.debug.GraalError;
  42 import org.graalvm.compiler.graph.Node;
  43 import org.graalvm.compiler.graph.NodeClass;
  44 import org.graalvm.compiler.graph.iterators.NodeIterable;
  45 import org.graalvm.compiler.graph.spi.Canonicalizable;
  46 import org.graalvm.compiler.graph.spi.Simplifiable;
  47 import org.graalvm.compiler.graph.spi.SimplifierTool;
  48 import org.graalvm.compiler.nodeinfo.InputType;
  49 import org.graalvm.compiler.nodeinfo.NodeInfo;
  50 import org.graalvm.compiler.nodes.calc.CompareNode;
  51 import org.graalvm.compiler.nodes.calc.ConditionalNode;
  52 import org.graalvm.compiler.nodes.calc.IntegerBelowNode;
  53 import org.graalvm.compiler.nodes.calc.IntegerEqualsNode;
  54 import org.graalvm.compiler.nodes.calc.IntegerLessThanNode;
  55 import org.graalvm.compiler.nodes.calc.IsNullNode;
  56 import org.graalvm.compiler.nodes.calc.NormalizeCompareNode;
  57 import org.graalvm.compiler.nodes.calc.ObjectEqualsNode;
  58 import org.graalvm.compiler.nodes.extended.UnboxNode;
  59 import org.graalvm.compiler.nodes.java.InstanceOfNode;
  60 import org.graalvm.compiler.nodes.java.LoadFieldNode;
  61 import org.graalvm.compiler.nodes.spi.LIRLowerable;
  62 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool;
  63 import org.graalvm.compiler.nodes.util.GraphUtil;
  64 import org.graalvm.util.EconomicMap;
  65 import org.graalvm.util.Equivalence;
  66 
  67 import jdk.vm.ci.meta.Constant;
  68 import jdk.vm.ci.meta.ConstantReflectionProvider;
  69 import jdk.vm.ci.meta.JavaConstant;
  70 import jdk.vm.ci.meta.JavaKind;
  71 import jdk.vm.ci.meta.PrimitiveConstant;
  72 
  73 /**
  74  * The {@code IfNode} represents a branch that can go one of two directions depending on the outcome
  75  * of a comparison.
  76  */
  77 @NodeInfo(cycles = CYCLES_1, size = SIZE_2, sizeRationale = "2 jmps")
  78 public final class IfNode extends ControlSplitNode implements Simplifiable, LIRLowerable {
  79     public static final NodeClass<IfNode> TYPE = NodeClass.create(IfNode.class);
  80 
  81     private static final CounterKey CORRECTED_PROBABILITIES = DebugContext.counter("CorrectedProbabilities");
  82 
  83     @Successor AbstractBeginNode trueSuccessor;
  84     @Successor AbstractBeginNode falseSuccessor;
  85     @Input(InputType.Condition) LogicNode condition;
  86     protected double trueSuccessorProbability;
  87 
  88     public LogicNode condition() {
  89         return condition;
  90     }
  91 
  92     public void setCondition(LogicNode x) {
  93         updateUsages(condition, x);
  94         condition = x;
  95     }
  96 
  97     public IfNode(LogicNode condition, FixedNode trueSuccessor, FixedNode falseSuccessor, double trueSuccessorProbability) {
  98         this(condition, BeginNode.begin(trueSuccessor), BeginNode.begin(falseSuccessor), trueSuccessorProbability);
  99     }
 100 
 101     public IfNode(LogicNode condition, AbstractBeginNode trueSuccessor, AbstractBeginNode falseSuccessor, double trueSuccessorProbability) {
 102         super(TYPE, StampFactory.forVoid());
 103         this.condition = condition;
 104         this.falseSuccessor = falseSuccessor;
 105         this.trueSuccessor = trueSuccessor;
 106         setTrueSuccessorProbability(trueSuccessorProbability);
 107     }
 108 
 109     /**
 110      * Gets the true successor.
 111      *
 112      * @return the true successor
 113      */
 114     public AbstractBeginNode trueSuccessor() {
 115         return trueSuccessor;
 116     }
 117 
 118     /**
 119      * Gets the false successor.
 120      *
 121      * @return the false successor
 122      */
 123     public AbstractBeginNode falseSuccessor() {
 124         return falseSuccessor;
 125     }
 126 
 127     public double getTrueSuccessorProbability() {
 128         return this.trueSuccessorProbability;
 129     }
 130 
 131     public void setTrueSuccessor(AbstractBeginNode node) {
 132         updatePredecessor(trueSuccessor, node);
 133         trueSuccessor = node;
 134     }
 135 
 136     public void setFalseSuccessor(AbstractBeginNode node) {
 137         updatePredecessor(falseSuccessor, node);
 138         falseSuccessor = node;
 139     }
 140 
 141     /**
 142      * Gets the node corresponding to the specified outcome of the branch.
 143      *
 144      * @param istrue {@code true} if the true successor is requested, {@code false} otherwise
 145      * @return the corresponding successor
 146      */
 147     public AbstractBeginNode successor(boolean istrue) {
 148         return istrue ? trueSuccessor : falseSuccessor;
 149     }
 150 
 151     public void setTrueSuccessorProbability(double prob) {
 152         assert prob >= -0.000000001 && prob <= 1.000000001 : "Probability out of bounds: " + prob;
 153         trueSuccessorProbability = Math.min(1.0, Math.max(0.0, prob));
 154     }
 155 
 156     @Override
 157     public double probability(AbstractBeginNode successor) {
 158         return successor == trueSuccessor ? trueSuccessorProbability : 1 - trueSuccessorProbability;
 159     }
 160 
 161     @Override
 162     public void generate(NodeLIRBuilderTool gen) {
 163         gen.emitIf(this);
 164     }
 165 
 166     @Override
 167     public boolean verify() {
 168         assertTrue(condition() != null, "missing condition");
 169         assertTrue(trueSuccessor() != null, "missing trueSuccessor");
 170         assertTrue(falseSuccessor() != null, "missing falseSuccessor");
 171         return super.verify();
 172     }
 173 
 174     public void eliminateNegation() {
 175         AbstractBeginNode oldTrueSuccessor = trueSuccessor;
 176         AbstractBeginNode oldFalseSuccessor = falseSuccessor;
 177         trueSuccessor = oldFalseSuccessor;
 178         falseSuccessor = oldTrueSuccessor;
 179         trueSuccessorProbability = 1 - trueSuccessorProbability;
 180         setCondition(((LogicNegationNode) condition).getValue());
 181     }
 182 
 183     @Override
 184     public void simplify(SimplifierTool tool) {
 185         if (trueSuccessor().next() instanceof DeoptimizeNode) {
 186             if (trueSuccessorProbability != 0) {
 187                 CORRECTED_PROBABILITIES.increment(getDebug());
 188                 trueSuccessorProbability = 0;
 189             }
 190         } else if (falseSuccessor().next() instanceof DeoptimizeNode) {
 191             if (trueSuccessorProbability != 1) {
 192                 CORRECTED_PROBABILITIES.increment(getDebug());
 193                 trueSuccessorProbability = 1;
 194             }
 195         }
 196 
 197         if (condition() instanceof LogicNegationNode) {
 198             eliminateNegation();
 199         }
 200         if (condition() instanceof LogicConstantNode) {
 201             LogicConstantNode c = (LogicConstantNode) condition();
 202             if (c.getValue()) {
 203                 tool.deleteBranch(falseSuccessor());
 204                 tool.addToWorkList(trueSuccessor());
 205                 graph().removeSplit(this, trueSuccessor());
 206             } else {
 207                 tool.deleteBranch(trueSuccessor());
 208                 tool.addToWorkList(falseSuccessor());
 209                 graph().removeSplit(this, falseSuccessor());
 210             }
 211             return;
 212         }
 213         if (tool.allUsagesAvailable() && trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages()) {
 214 
 215             pushNodesThroughIf(tool);
 216 
 217             if (checkForUnsignedCompare(tool) || removeOrMaterializeIf(tool)) {
 218                 return;
 219             }
 220         }
 221 
 222         if (removeIntermediateMaterialization(tool)) {
 223             return;
 224         }
 225 
 226         if (splitIfAtPhi(tool)) {
 227             return;
 228         }
 229 
 230         if (conditionalNodeOptimization(tool)) {
 231             return;
 232         }
 233 
 234         if (falseSuccessor().hasNoUsages() && (!(falseSuccessor() instanceof LoopExitNode)) && falseSuccessor().next() instanceof IfNode) {
 235             AbstractBeginNode intermediateBegin = falseSuccessor();
 236             IfNode nextIf = (IfNode) intermediateBegin.next();
 237             double probabilityB = (1.0 - this.trueSuccessorProbability) * nextIf.trueSuccessorProbability;
 238             if (this.trueSuccessorProbability < probabilityB) {
 239                 // Reordering of those two if statements is beneficial from the point of view of
 240                 // their probabilities.
 241                 if (prepareForSwap(tool.getConstantReflection(), condition(), nextIf.condition())) {
 242                     // Reordering is allowed from (if1 => begin => if2) to (if2 => begin => if1).
 243                     assert intermediateBegin.next() == nextIf;
 244                     AbstractBeginNode bothFalseBegin = nextIf.falseSuccessor();
 245                     nextIf.setFalseSuccessor(null);
 246                     intermediateBegin.setNext(null);
 247                     this.setFalseSuccessor(null);
 248 
 249                     this.replaceAtPredecessor(nextIf);
 250                     nextIf.setFalseSuccessor(intermediateBegin);
 251                     intermediateBegin.setNext(this);
 252                     this.setFalseSuccessor(bothFalseBegin);
 253                     nextIf.setTrueSuccessorProbability(probabilityB);
 254                     if (probabilityB == 1.0) {
 255                         this.setTrueSuccessorProbability(0.0);
 256                     } else {
 257                         double newProbability = this.trueSuccessorProbability / (1.0 - probabilityB);
 258                         this.setTrueSuccessorProbability(Math.min(1.0, newProbability));
 259                     }
 260                     return;
 261                 }
 262             }
 263         }
 264 
 265         if (tryEliminateBoxedReferenceEquals(tool)) {
 266             return;
 267         }
 268     }
 269 
 270     private boolean isUnboxedFrom(MetaAccessProvider meta, ValueNode x, ValueNode src) {
 271         if (x == src) {
 272             return true;
 273         } else if (x instanceof UnboxNode) {
 274             return isUnboxedFrom(meta, ((UnboxNode) x).getValue(), src);
 275         } else if (x instanceof PiNode) {
 276             PiNode pi = (PiNode) x;
 277             return isUnboxedFrom(meta, pi.getOriginalNode(), src);
 278         } else if (x instanceof LoadFieldNode) {
 279             LoadFieldNode load = (LoadFieldNode) x;
 280             ResolvedJavaType integerType = meta.lookupJavaType(Integer.class);
 281             if (load.getValue().stamp().javaType(meta).equals(integerType)) {
 282                 return isUnboxedFrom(meta, load.getValue(), src);
 283             } else {
 284                 return false;
 285             }
 286         } else {
 287             return false;
 288         }
 289     }
 290 
 291     /**
 292      * Attempts to replace the following pattern:
 293      *
 294      * <pre>
 295      * Integer x = ...;
 296      * Integer y = ...;
 297      * if ((x == y) || x.equals(y)) { ... }
 298      * </pre>
 299      *
 300      * with:
 301      *
 302      * <pre>
 303      * Integer x = ...;
 304      * Integer y = ...;
 305      * if (x.equals(y)) { ... }
 306      * </pre>
 307      *
 308      * whenever the probability that the reference check will pass is relatively small.
 309      *
 310      * See GR-1315 for more information.
 311      */
 312     private boolean tryEliminateBoxedReferenceEquals(SimplifierTool tool) {
 313         if (!(condition instanceof ObjectEqualsNode)) {
 314             return false;
 315         }
 316 
 317         MetaAccessProvider meta = tool.getMetaAccess();
 318         ObjectEqualsNode equalsCondition = (ObjectEqualsNode) condition;
 319         ValueNode x = equalsCondition.getX();
 320         ValueNode y = equalsCondition.getY();
 321         ResolvedJavaType integerType = meta.lookupJavaType(Integer.class);
 322 
 323         // At least one argument for reference equal must be a boxed primitive.
 324         if (!x.stamp().javaType(meta).equals(integerType) && !y.stamp().javaType(meta).equals(integerType)) {
 325             return false;
 326         }
 327 
 328         // The reference equality check is usually more efficient compared to a boxing check.
 329         // The success of the reference equals must therefore be relatively rare, otherwise it makes
 330         // no sense to eliminate it.
 331         if (getTrueSuccessorProbability() > 0.4) {
 332             return false;
 333         }
 334 
 335         // True branch must be empty.
 336         if (trueSuccessor instanceof BeginNode || trueSuccessor instanceof LoopExitNode) {
 337             if (trueSuccessor.next() instanceof EndNode) {
 338                 // Empty true branch.
 339             } else {
 340                 return false;
 341             }
 342         } else {
 343             return false;
 344         }
 345 
 346         // False branch must only check the unboxed values.
 347         UnboxNode unbox = null;
 348         FixedGuardNode unboxCheck = null;
 349         for (FixedNode node : falseSuccessor.getBlockNodes()) {
 350             if (!(node instanceof BeginNode || node instanceof UnboxNode || node instanceof FixedGuardNode || node instanceof EndNode ||
 351                             node instanceof LoadFieldNode || node instanceof LoopExitNode)) {
 352                 return false;
 353             }
 354             if (node instanceof UnboxNode) {
 355                 if (unbox == null) {
 356                     unbox = (UnboxNode) node;
 357                 } else {
 358                     return false;
 359                 }
 360             }
 361             if (!(node instanceof FixedGuardNode)) {
 362                 continue;
 363             }
 364             FixedGuardNode fixed = (FixedGuardNode) node;
 365             if (!(fixed.condition() instanceof IntegerEqualsNode)) {
 366                 continue;
 367             }
 368             IntegerEqualsNode equals = (IntegerEqualsNode) fixed.condition();
 369             if ((isUnboxedFrom(meta, equals.getX(), x) && isUnboxedFrom(meta, equals.getY(), y)) || (isUnboxedFrom(meta, equals.getX(), y) && isUnboxedFrom(meta, equals.getY(), x))) {
 370                 unboxCheck = fixed;
 371             }
 372         }
 373         if (unbox == null || unboxCheck == null) {
 374             return false;
 375         }
 376 
 377         // Falsify the reference check.
 378         setCondition(graph().addOrUniqueWithInputs(LogicConstantNode.contradiction()));
 379 
 380         return true;
 381     }
 382 
 383     /**
 384      * Try to optimize this as if it were a {@link ConditionalNode}.
 385      */
 386     private boolean conditionalNodeOptimization(SimplifierTool tool) {
 387         if (trueSuccessor().next() instanceof AbstractEndNode && falseSuccessor().next() instanceof AbstractEndNode) {
 388             AbstractEndNode trueEnd = (AbstractEndNode) trueSuccessor().next();
 389             AbstractEndNode falseEnd = (AbstractEndNode) falseSuccessor().next();
 390             if (trueEnd.merge() != falseEnd.merge()) {
 391                 return false;
 392             }
 393             if (!(trueEnd.merge() instanceof MergeNode)) {
 394                 return false;
 395             }
 396             MergeNode merge = (MergeNode) trueEnd.merge();
 397             if (merge.usages().count() != 1 || merge.phis().count() != 1) {
 398                 return false;
 399             }
 400 
 401             if (trueSuccessor().anchored().isNotEmpty() || falseSuccessor().anchored().isNotEmpty()) {
 402                 return false;
 403             }
 404 
 405             PhiNode phi = merge.phis().first();
 406             ValueNode falseValue = phi.valueAt(falseEnd);
 407             ValueNode trueValue = phi.valueAt(trueEnd);
 408 
 409             ValueNode result = ConditionalNode.canonicalizeConditional(condition, trueValue, falseValue, phi.stamp());
 410             if (result != null) {
 411                 /*
 412                  * canonicalizeConditional returns possibly new nodes so add them to the graph.
 413                  */
 414                 if (result.graph() == null) {
 415                     result = graph().addOrUniqueWithInputs(result);
 416                 }
 417                 /*
 418                  * This optimization can be performed even if multiple values merge at this phi
 419                  * since the two inputs get simplified into one.
 420                  */
 421                 phi.setValueAt(trueEnd, result);
 422                 removeThroughFalseBranch(tool, merge);
 423                 return true;
 424             }
 425         }
 426 
 427         return false;
 428     }
 429 
 430     private void pushNodesThroughIf(SimplifierTool tool) {
 431         assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages();
 432         // push similar nodes upwards through the if, thereby deduplicating them
 433         do {
 434             AbstractBeginNode trueSucc = trueSuccessor();
 435             AbstractBeginNode falseSucc = falseSuccessor();
 436             if (trueSucc instanceof BeginNode && falseSucc instanceof BeginNode && trueSucc.next() instanceof FixedWithNextNode && falseSucc.next() instanceof FixedWithNextNode) {
 437                 FixedWithNextNode trueNext = (FixedWithNextNode) trueSucc.next();
 438                 FixedWithNextNode falseNext = (FixedWithNextNode) falseSucc.next();
 439                 NodeClass<?> nodeClass = trueNext.getNodeClass();
 440                 if (trueNext.getClass() == falseNext.getClass()) {
 441                     if (trueNext instanceof AbstractBeginNode) {
 442                         // Cannot do this optimization for begin nodes, because it could
 443                         // move guards above the if that need to stay below a branch.
 444                     } else if (nodeClass.equalInputs(trueNext, falseNext) && trueNext.valueEquals(falseNext)) {
 445                         falseNext.replaceAtUsages(trueNext);
 446                         graph().removeFixed(falseNext);
 447                         GraphUtil.unlinkFixedNode(trueNext);
 448                         graph().addBeforeFixed(this, trueNext);
 449                         for (Node usage : trueNext.usages().snapshot()) {
 450                             if (usage.isAlive()) {
 451                                 NodeClass<?> usageNodeClass = usage.getNodeClass();
 452                                 if (usageNodeClass.valueNumberable() && !usageNodeClass.isLeafNode()) {
 453                                     Node newNode = graph().findDuplicate(usage);
 454                                     if (newNode != null) {
 455                                         usage.replaceAtUsagesAndDelete(newNode);
 456                                     }
 457                                 }
 458                                 if (usage.isAlive()) {
 459                                     tool.addToWorkList(usage);
 460                                 }
 461                             }
 462                         }
 463                         continue;
 464                     }
 465                 }
 466             }
 467             break;
 468         } while (true);
 469     }
 470 
 471     /**
 472      * Recognize a couple patterns that can be merged into an unsigned compare.
 473      *
 474      * @param tool
 475      * @return true if a replacement was done.
 476      */
 477     private boolean checkForUnsignedCompare(SimplifierTool tool) {
 478         assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages();
 479         if (condition() instanceof IntegerLessThanNode) {
 480             IntegerLessThanNode lessThan = (IntegerLessThanNode) condition();
 481             Constant y = lessThan.getY().stamp().asConstant();
 482             if (y instanceof PrimitiveConstant && ((PrimitiveConstant) y).asLong() == 0 && falseSuccessor().next() instanceof IfNode) {
 483                 IfNode ifNode2 = (IfNode) falseSuccessor().next();
 484                 if (ifNode2.condition() instanceof IntegerLessThanNode) {
 485                     IntegerLessThanNode lessThan2 = (IntegerLessThanNode) ifNode2.condition();
 486                     AbstractBeginNode falseSucc = ifNode2.falseSuccessor();
 487                     AbstractBeginNode trueSucc = ifNode2.trueSuccessor();
 488                     IntegerBelowNode below = null;
 489                     /*
 490                      * Convert x >= 0 && x < positive which is represented as !(x < 0) && x <
 491                      * <positive> into an unsigned compare.
 492                      */
 493                     if (lessThan2.getX() == lessThan.getX() && lessThan2.getY().stamp() instanceof IntegerStamp && ((IntegerStamp) lessThan2.getY().stamp()).isPositive() &&
 494                                     sameDestination(trueSuccessor(), ifNode2.falseSuccessor)) {
 495                         below = graph().unique(new IntegerBelowNode(lessThan2.getX(), lessThan2.getY()));
 496                         // swap direction
 497                         AbstractBeginNode tmp = falseSucc;
 498                         falseSucc = trueSucc;
 499                         trueSucc = tmp;
 500                     } else if (lessThan2.getY() == lessThan.getX() && sameDestination(trueSuccessor(), ifNode2.trueSuccessor)) {
 501                         /*
 502                          * Convert x >= 0 && x <= positive which is represented as !(x < 0) &&
 503                          * !(<positive> > x), into x <| positive + 1. This can only be done for
 504                          * constants since there isn't a IntegerBelowEqualThanNode but that doesn't
 505                          * appear to be interesting.
 506                          */
 507                         JavaConstant positive = lessThan2.getX().asJavaConstant();
 508                         if (positive != null && positive.asLong() > 0 && positive.asLong() < positive.getJavaKind().getMaxValue()) {
 509                             ConstantNode newLimit = ConstantNode.forIntegerStamp(lessThan2.getX().stamp(), positive.asLong() + 1, graph());
 510                             below = graph().unique(new IntegerBelowNode(lessThan.getX(), newLimit));
 511                         }
 512                     }
 513                     if (below != null) {
 514                         ifNode2.setTrueSuccessor(null);
 515                         ifNode2.setFalseSuccessor(null);
 516 
 517                         IfNode newIfNode = graph().add(new IfNode(below, falseSucc, trueSucc, 1 - trueSuccessorProbability));
 518                         // Remove the < 0 test.
 519                         tool.deleteBranch(trueSuccessor);
 520                         graph().removeSplit(this, falseSuccessor);
 521 
 522                         // Replace the second test with the new one.
 523                         ifNode2.predecessor().replaceFirstSuccessor(ifNode2, newIfNode);
 524                         ifNode2.safeDelete();
 525                         return true;
 526                     }
 527                 }
 528             }
 529         }
 530         return false;
 531     }
 532 
 533     /**
 534      * Check it these two blocks end up at the same place. Meeting at the same merge, or
 535      * deoptimizing in the same way.
 536      */
 537     private static boolean sameDestination(AbstractBeginNode succ1, AbstractBeginNode succ2) {
 538         Node next1 = succ1.next();
 539         Node next2 = succ2.next();
 540         if (next1 instanceof EndNode && next2 instanceof EndNode) {
 541             EndNode end1 = (EndNode) next1;
 542             EndNode end2 = (EndNode) next2;
 543             if (end1.merge() == end2.merge()) {
 544                 for (PhiNode phi : end1.merge().phis()) {
 545                     if (phi.valueAt(end1) != phi.valueAt(end2)) {
 546                         return false;
 547                     }
 548                 }
 549                 // They go to the same MergeNode and merge the same values
 550                 return true;
 551             }
 552         } else if (next1 instanceof DeoptimizeNode && next2 instanceof DeoptimizeNode) {
 553             DeoptimizeNode deopt1 = (DeoptimizeNode) next1;
 554             DeoptimizeNode deopt2 = (DeoptimizeNode) next2;
 555             if (deopt1.reason() == deopt2.reason() && deopt1.action() == deopt2.action()) {
 556                 // Same deoptimization reason and action.
 557                 return true;
 558             }
 559         } else if (next1 instanceof LoopExitNode && next2 instanceof LoopExitNode) {
 560             LoopExitNode exit1 = (LoopExitNode) next1;
 561             LoopExitNode exit2 = (LoopExitNode) next2;
 562             if (exit1.loopBegin() == exit2.loopBegin() && exit1.stateAfter() == exit2.stateAfter() && exit1.stateAfter() == null && sameDestination(exit1, exit2)) {
 563                 // Exit the same loop and end up at the same place.
 564                 return true;
 565             }
 566         } else if (next1 instanceof ReturnNode && next2 instanceof ReturnNode) {
 567             ReturnNode exit1 = (ReturnNode) next1;
 568             ReturnNode exit2 = (ReturnNode) next2;
 569             if (exit1.result() == exit2.result()) {
 570                 // Exit the same loop and end up at the same place.
 571                 return true;
 572             }
 573         }
 574         return false;
 575     }
 576 
 577     private static boolean prepareForSwap(ConstantReflectionProvider constantReflection, LogicNode a, LogicNode b) {
 578         DebugContext debug = a.getDebug();
 579         if (a instanceof InstanceOfNode) {
 580             InstanceOfNode instanceOfA = (InstanceOfNode) a;
 581             if (b instanceof IsNullNode) {
 582                 IsNullNode isNullNode = (IsNullNode) b;
 583                 if (isNullNode.getValue() == instanceOfA.getValue()) {
 584                     debug.log("Can swap instanceof and isnull if");
 585                     return true;
 586                 }
 587             } else if (b instanceof InstanceOfNode) {
 588                 InstanceOfNode instanceOfB = (InstanceOfNode) b;
 589                 if (instanceOfA.getValue() == instanceOfB.getValue() && !instanceOfA.type().getType().isInterface() && !instanceOfB.type().getType().isInterface() &&
 590                                 !instanceOfA.type().getType().isAssignableFrom(instanceOfB.type().getType()) && !instanceOfB.type().getType().isAssignableFrom(instanceOfA.type().getType())) {
 591                     // Two instanceof on the same value with mutually exclusive types.
 592                     debug.log("Can swap instanceof for types %s and %s", instanceOfA.type(), instanceOfB.type());
 593                     return true;
 594                 }
 595             }
 596         } else if (a instanceof CompareNode) {
 597             CompareNode compareA = (CompareNode) a;
 598             Condition conditionA = compareA.condition();
 599             if (compareA.unorderedIsTrue()) {
 600                 return false;
 601             }
 602             if (b instanceof CompareNode) {
 603                 CompareNode compareB = (CompareNode) b;
 604                 if (compareA == compareB) {
 605                     debug.log("Same conditions => do not swap and leave the work for global value numbering.");
 606                     return false;
 607                 }
 608                 if (compareB.unorderedIsTrue()) {
 609                     return false;
 610                 }
 611                 Condition comparableCondition = null;
 612                 Condition conditionB = compareB.condition();
 613                 if (compareB.getX() == compareA.getX() && compareB.getY() == compareA.getY()) {
 614                     comparableCondition = conditionB;
 615                 } else if (compareB.getX() == compareA.getY() && compareB.getY() == compareA.getX()) {
 616                     comparableCondition = conditionB.mirror();
 617                 }
 618 
 619                 if (comparableCondition != null) {
 620                     Condition combined = conditionA.join(comparableCondition);
 621                     if (combined == null) {
 622                         // The two conditions are disjoint => can reorder.
 623                         debug.log("Can swap disjoint coditions on same values: %s and %s", conditionA, comparableCondition);
 624                         return true;
 625                     }
 626                 } else if (conditionA == Condition.EQ && conditionB == Condition.EQ) {
 627                     boolean canSwap = false;
 628                     if ((compareA.getX() == compareB.getX() && valuesDistinct(constantReflection, compareA.getY(), compareB.getY()))) {
 629                         canSwap = true;
 630                     } else if ((compareA.getX() == compareB.getY() && valuesDistinct(constantReflection, compareA.getY(), compareB.getX()))) {
 631                         canSwap = true;
 632                     } else if ((compareA.getY() == compareB.getX() && valuesDistinct(constantReflection, compareA.getX(), compareB.getY()))) {
 633                         canSwap = true;
 634                     } else if ((compareA.getY() == compareB.getY() && valuesDistinct(constantReflection, compareA.getX(), compareB.getX()))) {
 635                         canSwap = true;
 636                     }
 637 
 638                     if (canSwap) {
 639                         debug.log("Can swap equality condition with one shared and one disjoint value.");
 640                         return true;
 641                     }
 642                 }
 643             }
 644         }
 645 
 646         return false;
 647     }
 648 
 649     private static boolean valuesDistinct(ConstantReflectionProvider constantReflection, ValueNode a, ValueNode b) {
 650         if (a.isConstant() && b.isConstant()) {
 651             Boolean equal = constantReflection.constantEquals(a.asConstant(), b.asConstant());
 652             if (equal != null) {
 653                 return !equal.booleanValue();
 654             }
 655         }
 656 
 657         Stamp stampA = a.stamp();
 658         Stamp stampB = b.stamp();
 659         return stampA.alwaysDistinct(stampB);
 660     }
 661 
 662     /**
 663      * Tries to remove an empty if construct or replace an if construct with a materialization.
 664      *
 665      * @return true if a transformation was made, false otherwise
 666      */
 667     private boolean removeOrMaterializeIf(SimplifierTool tool) {
 668         assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages();
 669         if (trueSuccessor().next() instanceof AbstractEndNode && falseSuccessor().next() instanceof AbstractEndNode) {
 670             AbstractEndNode trueEnd = (AbstractEndNode) trueSuccessor().next();
 671             AbstractEndNode falseEnd = (AbstractEndNode) falseSuccessor().next();
 672             AbstractMergeNode merge = trueEnd.merge();
 673             if (merge == falseEnd.merge() && trueSuccessor().anchored().isEmpty() && falseSuccessor().anchored().isEmpty()) {
 674                 PhiNode singlePhi = null;
 675                 int distinct = 0;
 676                 for (PhiNode phi : merge.phis()) {
 677                     ValueNode trueValue = phi.valueAt(trueEnd);
 678                     ValueNode falseValue = phi.valueAt(falseEnd);
 679                     if (trueValue != falseValue) {
 680                         distinct++;
 681                         singlePhi = phi;
 682                     }
 683                 }
 684                 if (distinct == 0) {
 685                     /*
 686                      * Multiple phis but merging same values for true and false, so simply delete
 687                      * the path
 688                      */
 689                     removeThroughFalseBranch(tool, merge);
 690                     return true;
 691                 } else if (distinct == 1) {
 692                     ValueNode trueValue = singlePhi.valueAt(trueEnd);
 693                     ValueNode falseValue = singlePhi.valueAt(falseEnd);
 694                     ValueNode conditional = canonicalizeConditionalCascade(trueValue, falseValue);
 695                     if (conditional != null) {
 696                         singlePhi.setValueAt(trueEnd, conditional);
 697                         removeThroughFalseBranch(tool, merge);
 698                         return true;
 699                     }
 700                 }
 701             }
 702         }
 703         if (trueSuccessor().next() instanceof ReturnNode && falseSuccessor().next() instanceof ReturnNode) {
 704             ReturnNode trueEnd = (ReturnNode) trueSuccessor().next();
 705             ReturnNode falseEnd = (ReturnNode) falseSuccessor().next();
 706             ValueNode trueValue = trueEnd.result();
 707             ValueNode falseValue = falseEnd.result();
 708             ValueNode value = null;
 709             if (trueValue != null) {
 710                 if (trueValue == falseValue) {
 711                     value = trueValue;
 712                 } else {
 713                     value = canonicalizeConditionalCascade(trueValue, falseValue);
 714                     if (value == null) {
 715                         return false;
 716                     }
 717                 }
 718             }
 719             ReturnNode newReturn = graph().add(new ReturnNode(value));
 720             replaceAtPredecessor(newReturn);
 721             GraphUtil.killCFG(this);
 722             return true;
 723         }
 724         return false;
 725     }
 726 
 727     protected void removeThroughFalseBranch(SimplifierTool tool, AbstractMergeNode merge) {
 728         AbstractBeginNode trueBegin = trueSuccessor();
 729         LogicNode conditionNode = condition();
 730         graph().removeSplitPropagate(this, trueBegin);
 731         tool.addToWorkList(trueBegin);
 732         if (conditionNode != null) {
 733             GraphUtil.tryKillUnused(conditionNode);
 734         }
 735         if (merge.isAlive() && merge.forwardEndCount() > 1) {
 736             for (FixedNode end : merge.forwardEnds()) {
 737                 Node cur = end;
 738                 while (cur != null && cur.predecessor() instanceof BeginNode) {
 739                     cur = cur.predecessor();
 740                 }
 741                 if (cur != null && cur.predecessor() instanceof IfNode) {
 742                     tool.addToWorkList(cur.predecessor());
 743                 }
 744             }
 745         }
 746     }
 747 
 748     private ValueNode canonicalizeConditionalCascade(ValueNode trueValue, ValueNode falseValue) {
 749         if (trueValue.getStackKind() != falseValue.getStackKind()) {
 750             return null;
 751         }
 752         if (trueValue.getStackKind() != JavaKind.Int && trueValue.getStackKind() != JavaKind.Long) {
 753             return null;
 754         }
 755         if (trueValue.isConstant() && falseValue.isConstant()) {
 756             return graph().unique(new ConditionalNode(condition(), trueValue, falseValue));
 757         } else if (!graph().isAfterExpandLogic()) {
 758             ConditionalNode conditional = null;
 759             ValueNode constant = null;
 760             boolean negateCondition;
 761             if (trueValue instanceof ConditionalNode && falseValue.isConstant()) {
 762                 conditional = (ConditionalNode) trueValue;
 763                 constant = falseValue;
 764                 negateCondition = true;
 765             } else if (falseValue instanceof ConditionalNode && trueValue.isConstant()) {
 766                 conditional = (ConditionalNode) falseValue;
 767                 constant = trueValue;
 768                 negateCondition = false;
 769             } else {
 770                 return null;
 771             }
 772             boolean negateConditionalCondition = false;
 773             ValueNode otherValue = null;
 774             if (constant == conditional.trueValue()) {
 775                 otherValue = conditional.falseValue();
 776                 negateConditionalCondition = false;
 777             } else if (constant == conditional.falseValue()) {
 778                 otherValue = conditional.trueValue();
 779                 negateConditionalCondition = true;
 780             }
 781             if (otherValue != null && otherValue.isConstant()) {
 782                 double shortCutProbability = probability(trueSuccessor());
 783                 LogicNode newCondition = LogicNode.or(condition(), negateCondition, conditional.condition(), negateConditionalCondition, shortCutProbability);
 784                 return graph().unique(new ConditionalNode(newCondition, constant, otherValue));
 785             } else if (!negateCondition && constant.isJavaConstant() && conditional.trueValue().isJavaConstant() && conditional.falseValue().isJavaConstant()) {
 786                 IntegerLessThanNode lessThan = null;
 787                 IntegerEqualsNode equals = null;
 788                 if (condition() instanceof IntegerLessThanNode && conditional.condition() instanceof IntegerEqualsNode && constant.asJavaConstant().asLong() == -1 &&
 789                                 conditional.trueValue().asJavaConstant().asLong() == 0 && conditional.falseValue().asJavaConstant().asLong() == 1) {
 790                     lessThan = (IntegerLessThanNode) condition();
 791                     equals = (IntegerEqualsNode) conditional.condition();
 792                 } else if (condition() instanceof IntegerEqualsNode && conditional.condition() instanceof IntegerLessThanNode && constant.asJavaConstant().asLong() == 0 &&
 793                                 conditional.trueValue().asJavaConstant().asLong() == -1 && conditional.falseValue().asJavaConstant().asLong() == 1) {
 794                     lessThan = (IntegerLessThanNode) conditional.condition();
 795                     equals = (IntegerEqualsNode) condition();
 796                 }
 797                 if (lessThan != null) {
 798                     assert equals != null;
 799                     if ((lessThan.getX() == equals.getX() && lessThan.getY() == equals.getY()) || (lessThan.getX() == equals.getY() && lessThan.getY() == equals.getX())) {
 800                         return graph().unique(new NormalizeCompareNode(lessThan.getX(), lessThan.getY(), conditional.trueValue().stamp().getStackKind(), false));
 801                     }
 802                 }
 803             }
 804         }
 805         return null;
 806     }
 807 
 808     /**
 809      * Take an if that is immediately dominated by a merge with a single phi and split off any paths
 810      * where the test would be statically decidable creating a new merge below the approriate side
 811      * of the IfNode. Any undecidable tests will continue to use the original IfNode.
 812      *
 813      * @param tool
 814      */
 815     private boolean splitIfAtPhi(SimplifierTool tool) {
 816         if (graph().getGuardsStage().areFrameStatesAtSideEffects()) {
 817             // Disabled until we make sure we have no FrameState-less merges at this stage
 818             return false;
 819         }
 820 
 821         if (!(predecessor() instanceof MergeNode)) {
 822             return false;
 823         }
 824         MergeNode merge = (MergeNode) predecessor();
 825         if (merge.forwardEndCount() == 1) {
 826             // Don't bother.
 827             return false;
 828         }
 829         if (merge.usages().count() != 1 || merge.phis().count() != 1) {
 830             return false;
 831         }
 832         if (merge.stateAfter() != null) {
 833             /* We'll get the chance to simplify this after frame state assignment. */
 834             return false;
 835         }
 836         PhiNode phi = merge.phis().first();
 837         if (phi.usages().count() != 1) {
 838             /*
 839              * For simplicity the below code assumes assumes the phi goes dead at the end so skip
 840              * this case.
 841              */
 842             return false;
 843         }
 844 
 845         /*
 846          * Check that the condition uses the phi and that there is only one user of the condition
 847          * expression.
 848          */
 849         if (!conditionUses(condition(), phi)) {
 850             return false;
 851         }
 852 
 853         /*
 854          * We could additionally filter for the case that at least some of the Phi inputs or one of
 855          * the condition inputs are constants but there are cases where a non-constant is
 856          * simplifiable, usually where the stamp allows the question to be answered.
 857          */
 858 
 859         /* Each successor of the if gets a new merge if needed. */
 860         MergeNode trueMerge = null;
 861         MergeNode falseMerge = null;
 862         assert merge.stateAfter() == null;
 863 
 864         for (EndNode end : merge.forwardEnds().snapshot()) {
 865             Node value = phi.valueAt(end);
 866             LogicNode result = computeCondition(tool, condition, phi, value);
 867             if (result instanceof LogicConstantNode) {
 868                 merge.removeEnd(end);
 869                 if (((LogicConstantNode) result).getValue()) {
 870                     if (trueMerge == null) {
 871                         trueMerge = insertMerge(trueSuccessor());
 872                     }
 873                     trueMerge.addForwardEnd(end);
 874                 } else {
 875                     if (falseMerge == null) {
 876                         falseMerge = insertMerge(falseSuccessor());
 877                     }
 878                     falseMerge.addForwardEnd(end);
 879                 }
 880             } else if (result != condition) {
 881                 // Build a new IfNode using the new condition
 882                 BeginNode trueBegin = graph().add(new BeginNode());
 883                 BeginNode falseBegin = graph().add(new BeginNode());
 884 
 885                 if (result.graph() == null) {
 886                     result = graph().addOrUniqueWithInputs(result);
 887                 }
 888                 IfNode newIfNode = graph().add(new IfNode(result, trueBegin, falseBegin, trueSuccessorProbability));
 889                 merge.removeEnd(end);
 890                 ((FixedWithNextNode) end.predecessor()).setNext(newIfNode);
 891 
 892                 if (trueMerge == null) {
 893                     trueMerge = insertMerge(trueSuccessor());
 894                 }
 895                 trueBegin.setNext(graph().add(new EndNode()));
 896                 trueMerge.addForwardEnd((EndNode) trueBegin.next());
 897 
 898                 if (falseMerge == null) {
 899                     falseMerge = insertMerge(falseSuccessor());
 900                 }
 901                 falseBegin.setNext(graph().add(new EndNode()));
 902                 falseMerge.addForwardEnd((EndNode) falseBegin.next());
 903 
 904                 end.safeDelete();
 905             }
 906         }
 907 
 908         transferProxies(trueSuccessor(), trueMerge);
 909         transferProxies(falseSuccessor(), falseMerge);
 910 
 911         cleanupMerge(merge);
 912         cleanupMerge(trueMerge);
 913         cleanupMerge(falseMerge);
 914 
 915         return true;
 916     }
 917 
 918     /**
 919      * @param condition
 920      * @param phi
 921      * @return true if the passed in {@code condition} uses {@code phi} and the condition is only
 922      *         used once. Since the phi will go dead the condition using it will also have to be
 923      *         dead after the optimization.
 924      */
 925     private static boolean conditionUses(LogicNode condition, PhiNode phi) {
 926         if (condition.usages().count() != 1) {
 927             return false;
 928         }
 929         if (condition instanceof ShortCircuitOrNode) {
 930             if (condition.graph().getGuardsStage().areDeoptsFixed()) {
 931                 /*
 932                  * It can be unsafe to simplify a ShortCircuitOr before deopts are fixed because
 933                  * conversion to guards assumes that all the required conditions are being tested.
 934                  * Simplfying the condition based on context before this happens may lose a
 935                  * condition.
 936                  */
 937                 ShortCircuitOrNode orNode = (ShortCircuitOrNode) condition;
 938                 return (conditionUses(orNode.x, phi) || conditionUses(orNode.y, phi));
 939             }
 940         } else if (condition instanceof Canonicalizable.Unary<?>) {
 941             Canonicalizable.Unary<?> unary = (Canonicalizable.Unary<?>) condition;
 942             return unary.getValue() == phi;
 943         } else if (condition instanceof Canonicalizable.Binary<?>) {
 944             Canonicalizable.Binary<?> binary = (Canonicalizable.Binary<?>) condition;
 945             return binary.getX() == phi || binary.getY() == phi;
 946         }
 947         return false;
 948     }
 949 
 950     /**
 951      * Canonicalize {@code} condition using {@code value} in place of {@code phi}.
 952      *
 953      * @param tool
 954      * @param condition
 955      * @param phi
 956      * @param value
 957      * @return an improved LogicNode or the original condition
 958      */
 959     @SuppressWarnings("unchecked")
 960     private static LogicNode computeCondition(SimplifierTool tool, LogicNode condition, PhiNode phi, Node value) {
 961         if (condition instanceof ShortCircuitOrNode) {
 962             if (condition.graph().getGuardsStage().areDeoptsFixed() && !condition.graph().isAfterExpandLogic()) {
 963                 ShortCircuitOrNode orNode = (ShortCircuitOrNode) condition;
 964                 LogicNode resultX = computeCondition(tool, orNode.x, phi, value);
 965                 LogicNode resultY = computeCondition(tool, orNode.y, phi, value);
 966                 if (resultX != orNode.x || resultY != orNode.y) {
 967                     LogicNode result = orNode.canonical(tool, resultX, resultY);
 968                     if (result != orNode) {
 969                         return result;
 970                     }
 971                     /*
 972                      * Create a new node to carry the optimized inputs.
 973                      */
 974                     ShortCircuitOrNode newOr = new ShortCircuitOrNode(resultX, orNode.xNegated, resultY,
 975                                     orNode.yNegated, orNode.getShortCircuitProbability());
 976                     return newOr.canonical(tool);
 977                 }
 978                 return orNode;
 979             }
 980         } else if (condition instanceof Canonicalizable.Binary<?>) {
 981             Canonicalizable.Binary<Node> compare = (Canonicalizable.Binary<Node>) condition;
 982             if (compare.getX() == phi) {
 983                 return (LogicNode) compare.canonical(tool, value, compare.getY());
 984             } else if (compare.getY() == phi) {
 985                 return (LogicNode) compare.canonical(tool, compare.getX(), value);
 986             }
 987         } else if (condition instanceof Canonicalizable.Unary<?>) {
 988             Canonicalizable.Unary<Node> compare = (Canonicalizable.Unary<Node>) condition;
 989             if (compare.getValue() == phi) {
 990                 return (LogicNode) compare.canonical(tool, value);
 991             }
 992         }
 993         if (condition instanceof Canonicalizable) {
 994             return (LogicNode) ((Canonicalizable) condition).canonical(tool);
 995         }
 996         return condition;
 997     }
 998 
 999     private static void transferProxies(AbstractBeginNode successor, MergeNode falseMerge) {
1000         if (successor instanceof LoopExitNode && falseMerge != null) {
1001             LoopExitNode loopExitNode = (LoopExitNode) successor;
1002             for (ProxyNode proxy : loopExitNode.proxies().snapshot()) {
1003                 proxy.replaceFirstInput(successor, falseMerge);
1004             }
1005         }
1006     }
1007 
1008     private void cleanupMerge(MergeNode merge) {
1009         if (merge != null && merge.isAlive()) {
1010             if (merge.forwardEndCount() == 0) {
1011                 GraphUtil.killCFG(merge);
1012             } else if (merge.forwardEndCount() == 1) {
1013                 graph().reduceTrivialMerge(merge);
1014             }
1015         }
1016     }
1017 
1018     private MergeNode insertMerge(AbstractBeginNode begin) {
1019         MergeNode merge = graph().add(new MergeNode());
1020         if (!begin.anchored().isEmpty()) {
1021             Object before = null;
1022             before = begin.anchored().snapshot();
1023             begin.replaceAtUsages(InputType.Guard, merge);
1024             begin.replaceAtUsages(InputType.Anchor, merge);
1025             assert begin.anchored().isEmpty() : before + " " + begin.anchored().snapshot();
1026         }
1027 
1028         AbstractBeginNode theBegin = begin;
1029         if (begin instanceof LoopExitNode) {
1030             // Insert an extra begin to make it easier.
1031             theBegin = graph().add(new BeginNode());
1032             begin.replaceAtPredecessor(theBegin);
1033             theBegin.setNext(begin);
1034         }
1035         FixedNode next = theBegin.next();
1036         next.replaceAtPredecessor(merge);
1037         theBegin.setNext(graph().add(new EndNode()));
1038         merge.addForwardEnd((EndNode) theBegin.next());
1039         merge.setNext(next);
1040         return merge;
1041     }
1042 
1043     /**
1044      * Tries to connect code that initializes a variable directly with the successors of an if
1045      * construct that switches on the variable. For example, the pseudo code below:
1046      *
1047      * <pre>
1048      * contains(list, e, yes, no) {
1049      *     if (list == null || e == null) {
1050      *         condition = false;
1051      *     } else {
1052      *         condition = false;
1053      *         for (i in list) {
1054      *             if (i.equals(e)) {
1055      *                 condition = true;
1056      *                 break;
1057      *             }
1058      *         }
1059      *     }
1060      *     if (condition) {
1061      *         return yes;
1062      *     } else {
1063      *         return no;
1064      *     }
1065      * }
1066      * </pre>
1067      *
1068      * will be transformed into:
1069      *
1070      * <pre>
1071      * contains(list, e, yes, no) {
1072      *     if (list == null || e == null) {
1073      *         return no;
1074      *     } else {
1075      *         condition = false;
1076      *         for (i in list) {
1077      *             if (i.equals(e)) {
1078      *                 return yes;
1079      *             }
1080      *         }
1081      *         return no;
1082      *     }
1083      * }
1084      * </pre>
1085      *
1086      * @return true if a transformation was made, false otherwise
1087      */
1088     private boolean removeIntermediateMaterialization(SimplifierTool tool) {
1089         if (!(predecessor() instanceof AbstractMergeNode) || predecessor() instanceof LoopBeginNode) {
1090             return false;
1091         }
1092         AbstractMergeNode merge = (AbstractMergeNode) predecessor();
1093 
1094         if (!(condition() instanceof CompareNode)) {
1095             return false;
1096         }
1097 
1098         CompareNode compare = (CompareNode) condition();
1099         if (compare.getUsageCount() != 1) {
1100             return false;
1101         }
1102 
1103         // Only consider merges with a single usage that is both a phi and an operand of the
1104         // comparison
1105         NodeIterable<Node> mergeUsages = merge.usages();
1106         if (mergeUsages.count() != 1) {
1107             return false;
1108         }
1109         Node singleUsage = mergeUsages.first();
1110         if (!(singleUsage instanceof ValuePhiNode) || (singleUsage != compare.getX() && singleUsage != compare.getY())) {
1111             return false;
1112         }
1113 
1114         // Ensure phi is used by at most the comparison and the merge's frame state (if any)
1115         ValuePhiNode phi = (ValuePhiNode) singleUsage;
1116         NodeIterable<Node> phiUsages = phi.usages();
1117         if (phiUsages.count() > 2) {
1118             return false;
1119         }
1120         for (Node usage : phiUsages) {
1121             if (usage != compare && usage != merge.stateAfter()) {
1122                 return false;
1123             }
1124         }
1125 
1126         List<EndNode> mergePredecessors = merge.cfgPredecessors().snapshot();
1127         assert phi.valueCount() == merge.forwardEndCount();
1128 
1129         Constant[] xs = constantValues(compare.getX(), merge, false);
1130         Constant[] ys = constantValues(compare.getY(), merge, false);
1131         if (xs == null || ys == null) {
1132             return false;
1133         }
1134 
1135         // Sanity check that both ends are not followed by a merge without frame state.
1136         if (!checkFrameState(trueSuccessor()) && !checkFrameState(falseSuccessor())) {
1137             return false;
1138         }
1139 
1140         List<EndNode> falseEnds = new ArrayList<>(mergePredecessors.size());
1141         List<EndNode> trueEnds = new ArrayList<>(mergePredecessors.size());
1142         EconomicMap<AbstractEndNode, ValueNode> phiValues = EconomicMap.create(Equivalence.IDENTITY, mergePredecessors.size());
1143 
1144         AbstractBeginNode oldFalseSuccessor = falseSuccessor();
1145         AbstractBeginNode oldTrueSuccessor = trueSuccessor();
1146 
1147         setFalseSuccessor(null);
1148         setTrueSuccessor(null);
1149 
1150         Iterator<EndNode> ends = mergePredecessors.iterator();
1151         for (int i = 0; i < xs.length; i++) {
1152             EndNode end = ends.next();
1153             phiValues.put(end, phi.valueAt(end));
1154             if (compare.condition().foldCondition(xs[i], ys[i], tool.getConstantReflection(), compare.unorderedIsTrue())) {
1155                 trueEnds.add(end);
1156             } else {
1157                 falseEnds.add(end);
1158             }
1159         }
1160         assert !ends.hasNext();
1161         assert falseEnds.size() + trueEnds.size() == xs.length;
1162 
1163         connectEnds(falseEnds, phiValues, oldFalseSuccessor, merge, tool);
1164         connectEnds(trueEnds, phiValues, oldTrueSuccessor, merge, tool);
1165 
1166         if (this.trueSuccessorProbability == 0.0) {
1167             for (AbstractEndNode endNode : trueEnds) {
1168                 propagateZeroProbability(endNode);
1169             }
1170         }
1171 
1172         if (this.trueSuccessorProbability == 1.0) {
1173             for (AbstractEndNode endNode : falseEnds) {
1174                 propagateZeroProbability(endNode);
1175             }
1176         }
1177 
1178         /*
1179          * Remove obsolete ends only after processing all ends, otherwise oldTrueSuccessor or
1180          * oldFalseSuccessor might have been removed if it is a LoopExitNode.
1181          */
1182         if (falseEnds.isEmpty()) {
1183             GraphUtil.killCFG(oldFalseSuccessor);
1184         }
1185         if (trueEnds.isEmpty()) {
1186             GraphUtil.killCFG(oldTrueSuccessor);
1187         }
1188         GraphUtil.killCFG(merge);
1189 
1190         assert !merge.isAlive() : merge;
1191         assert !phi.isAlive() : phi;
1192         assert !compare.isAlive() : compare;
1193         assert !this.isAlive() : this;
1194 
1195         return true;
1196     }
1197 
1198     private void propagateZeroProbability(FixedNode startNode) {
1199         Node prev = null;
1200         for (FixedNode node : GraphUtil.predecessorIterable(startNode)) {
1201             if (node instanceof IfNode) {
1202                 IfNode ifNode = (IfNode) node;
1203                 if (ifNode.trueSuccessor() == prev) {
1204                     if (ifNode.trueSuccessorProbability == 0.0) {
1205                         return;
1206                     } else if (ifNode.trueSuccessorProbability == 1.0) {
1207                         continue;
1208                     } else {
1209                         ifNode.setTrueSuccessorProbability(0.0);
1210                         return;
1211                     }
1212                 } else if (ifNode.falseSuccessor() == prev) {
1213                     if (ifNode.trueSuccessorProbability == 1.0) {
1214                         return;
1215                     } else if (ifNode.trueSuccessorProbability == 0.0) {
1216                         continue;
1217                     } else {
1218                         ifNode.setTrueSuccessorProbability(1.0);
1219                         return;
1220                     }
1221                 } else {
1222                     throw new GraalError("Illegal state");
1223                 }
1224             } else if (node instanceof AbstractMergeNode && !(node instanceof LoopBeginNode)) {
1225                 for (AbstractEndNode endNode : ((AbstractMergeNode) node).cfgPredecessors()) {
1226                     propagateZeroProbability(endNode);
1227                 }
1228                 return;
1229             }
1230             prev = node;
1231         }
1232     }
1233 
1234     private static boolean checkFrameState(FixedNode start) {
1235         FixedNode node = start;
1236         while (true) {
1237             if (node instanceof AbstractMergeNode) {
1238                 AbstractMergeNode mergeNode = (AbstractMergeNode) node;
1239                 if (mergeNode.stateAfter() == null) {
1240                     return false;
1241                 } else {
1242                     return true;
1243                 }
1244             } else if (node instanceof StateSplit) {
1245                 StateSplit stateSplitNode = (StateSplit) node;
1246                 if (stateSplitNode.stateAfter() != null) {
1247                     return true;
1248                 }
1249             }
1250 
1251             if (node instanceof ControlSplitNode) {
1252                 ControlSplitNode controlSplitNode = (ControlSplitNode) node;
1253                 for (Node succ : controlSplitNode.cfgSuccessors()) {
1254                     if (checkFrameState((FixedNode) succ)) {
1255                         return true;
1256                     }
1257                 }
1258                 return false;
1259             } else if (node instanceof FixedWithNextNode) {
1260                 FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) node;
1261                 node = fixedWithNextNode.next();
1262             } else if (node instanceof AbstractEndNode) {
1263                 AbstractEndNode endNode = (AbstractEndNode) node;
1264                 node = endNode.merge();
1265             } else if (node instanceof ControlSinkNode) {
1266                 return true;
1267             } else {
1268                 return false;
1269             }
1270         }
1271     }
1272 
1273     /**
1274      * Connects a set of ends to a given successor, inserting a merge node if there is more than one
1275      * end. If {@code ends} is not empty, then {@code successor} is added to {@code tool}'s
1276      * {@linkplain SimplifierTool#addToWorkList(org.graalvm.compiler.graph.Node) work list}.
1277      *
1278      * @param oldMerge the merge being removed
1279      * @param phiValues the values of the phi at the merge, keyed by the merge ends
1280      */
1281     private void connectEnds(List<EndNode> ends, EconomicMap<AbstractEndNode, ValueNode> phiValues, AbstractBeginNode successor, AbstractMergeNode oldMerge, SimplifierTool tool) {
1282         if (!ends.isEmpty()) {
1283             if (ends.size() == 1) {
1284                 AbstractEndNode end = ends.get(0);
1285                 ((FixedWithNextNode) end.predecessor()).setNext(successor);
1286                 oldMerge.removeEnd(end);
1287                 GraphUtil.killCFG(end);
1288             } else {
1289                 // Need a new phi in case the frame state is used by more than the merge being
1290                 // removed
1291                 AbstractMergeNode newMerge = graph().add(new MergeNode());
1292                 PhiNode oldPhi = (PhiNode) oldMerge.usages().first();
1293                 PhiNode newPhi = graph().addWithoutUnique(new ValuePhiNode(oldPhi.stamp(), newMerge));
1294 
1295                 for (EndNode end : ends) {
1296                     newPhi.addInput(phiValues.get(end));
1297                     newMerge.addForwardEnd(end);
1298                 }
1299 
1300                 FrameState stateAfter = oldMerge.stateAfter();
1301                 if (stateAfter != null) {
1302                     stateAfter = stateAfter.duplicate();
1303                     stateAfter.replaceFirstInput(oldPhi, newPhi);
1304                     newMerge.setStateAfter(stateAfter);
1305                 }
1306 
1307                 newMerge.setNext(successor);
1308             }
1309             tool.addToWorkList(successor);
1310         }
1311     }
1312 
1313     /**
1314      * Gets an array of constants derived from a node that is either a {@link ConstantNode} or a
1315      * {@link PhiNode} whose input values are all constants. The length of the returned array is
1316      * equal to the number of ends terminating in a given merge node.
1317      *
1318      * @return null if {@code node} is neither a {@link ConstantNode} nor a {@link PhiNode} whose
1319      *         input values are all constants
1320      */
1321     public static Constant[] constantValues(ValueNode node, AbstractMergeNode merge, boolean allowNull) {
1322         if (node.isConstant()) {
1323             Constant[] result = new Constant[merge.forwardEndCount()];
1324             Arrays.fill(result, node.asConstant());
1325             return result;
1326         }
1327 
1328         if (node instanceof PhiNode) {
1329             PhiNode phi = (PhiNode) node;
1330             if (phi.merge() == merge && phi instanceof ValuePhiNode && phi.valueCount() == merge.forwardEndCount()) {
1331                 Constant[] result = new Constant[merge.forwardEndCount()];
1332                 int i = 0;
1333                 for (ValueNode n : phi.values()) {
1334                     if (!allowNull && !n.isConstant()) {
1335                         return null;
1336                     }
1337                     result[i++] = n.asConstant();
1338                 }
1339                 return result;
1340             }
1341         }
1342 
1343         return null;
1344     }
1345 
1346     @Override
1347     public AbstractBeginNode getPrimarySuccessor() {
1348         return this.trueSuccessor();
1349     }
1350 
1351     public AbstractBeginNode getSuccessor(boolean result) {
1352         return result ? this.trueSuccessor() : this.falseSuccessor();
1353     }
1354 
1355     @Override
1356     public boolean setProbability(AbstractBeginNode successor, double value) {
1357         if (successor == this.trueSuccessor()) {
1358             this.setTrueSuccessorProbability(value);
1359             return true;
1360         } else if (successor == this.falseSuccessor()) {
1361             this.setTrueSuccessorProbability(1.0 - value);
1362             return true;
1363         }
1364         return false;
1365     }
1366 
1367     @Override
1368     public int getSuccessorCount() {
1369         return 2;
1370     }
1371 }