1 /* 2 * Copyright (c) 2009, 2019, 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 24 25 package org.graalvm.compiler.nodes; 26 27 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_1; 28 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_2; 29 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.Iterator; 33 import java.util.List; 34 import java.util.Objects; 35 36 import jdk.internal.vm.compiler.collections.EconomicMap; 37 import jdk.internal.vm.compiler.collections.Equivalence; 38 import org.graalvm.compiler.bytecode.BytecodeDisassembler; 39 import org.graalvm.compiler.bytecode.Bytecodes; 40 import org.graalvm.compiler.bytecode.Bytes; 41 import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode; 42 import org.graalvm.compiler.core.common.calc.Condition; 43 import org.graalvm.compiler.core.common.type.FloatStamp; 44 import org.graalvm.compiler.core.common.type.IntegerStamp; 45 import org.graalvm.compiler.core.common.type.PrimitiveStamp; 46 import org.graalvm.compiler.core.common.type.Stamp; 47 import org.graalvm.compiler.core.common.type.StampFactory; 48 import org.graalvm.compiler.debug.CounterKey; 49 import org.graalvm.compiler.debug.DebugCloseable; 50 import org.graalvm.compiler.debug.DebugContext; 51 import org.graalvm.compiler.debug.GraalError; 52 import org.graalvm.compiler.graph.IterableNodeType; 53 import org.graalvm.compiler.graph.Node; 54 import org.graalvm.compiler.graph.NodeClass; 55 import org.graalvm.compiler.graph.NodeSourcePosition; 56 import org.graalvm.compiler.graph.iterators.NodeIterable; 57 import org.graalvm.compiler.graph.spi.Canonicalizable; 58 import org.graalvm.compiler.graph.spi.Simplifiable; 59 import org.graalvm.compiler.graph.spi.SimplifierTool; 60 import org.graalvm.compiler.nodeinfo.InputType; 61 import org.graalvm.compiler.nodeinfo.NodeInfo; 62 import org.graalvm.compiler.nodes.calc.AddNode; 63 import org.graalvm.compiler.nodes.calc.CompareNode; 64 import org.graalvm.compiler.nodes.calc.ConditionalNode; 65 import org.graalvm.compiler.nodes.calc.FloatNormalizeCompareNode; 66 import org.graalvm.compiler.nodes.calc.IntegerBelowNode; 67 import org.graalvm.compiler.nodes.calc.IntegerEqualsNode; 68 import org.graalvm.compiler.nodes.calc.IntegerLessThanNode; 69 import org.graalvm.compiler.nodes.calc.IntegerNormalizeCompareNode; 70 import org.graalvm.compiler.nodes.calc.IsNullNode; 71 import org.graalvm.compiler.nodes.calc.ObjectEqualsNode; 72 import org.graalvm.compiler.nodes.extended.UnboxNode; 73 import org.graalvm.compiler.nodes.java.InstanceOfNode; 74 import org.graalvm.compiler.nodes.java.LoadFieldNode; 75 import org.graalvm.compiler.nodes.spi.LIRLowerable; 76 import org.graalvm.compiler.nodes.spi.NodeLIRBuilderTool; 77 import org.graalvm.compiler.nodes.spi.SwitchFoldable; 78 import org.graalvm.compiler.nodes.util.GraphUtil; 79 80 import jdk.vm.ci.code.CodeUtil; 81 import jdk.vm.ci.meta.Constant; 82 import jdk.vm.ci.meta.JavaConstant; 83 import jdk.vm.ci.meta.JavaKind; 84 import jdk.vm.ci.meta.MetaAccessProvider; 85 import jdk.vm.ci.meta.PrimitiveConstant; 86 import jdk.vm.ci.meta.ResolvedJavaMethod; 87 import jdk.vm.ci.meta.ResolvedJavaType; 88 import jdk.vm.ci.meta.TriState; 89 90 /** 91 * The {@code IfNode} represents a branch that can go one of two directions depending on the outcome 92 * of a comparison. 93 */ 94 @NodeInfo(cycles = CYCLES_1, size = SIZE_2, sizeRationale = "2 jmps") 95 public final class IfNode extends ControlSplitNode implements Simplifiable, LIRLowerable, IterableNodeType, SwitchFoldable { 96 public static final NodeClass<IfNode> TYPE = NodeClass.create(IfNode.class); 97 98 private static final int MAX_USAGE_COLOR_SET_SIZE = 64; 99 private static final int MAX_FRAMESTATE_SEARCH_DEPTH = 4; 100 101 private static final CounterKey CORRECTED_PROBABILITIES = DebugContext.counter("CorrectedProbabilities"); 102 103 @Successor AbstractBeginNode trueSuccessor; 104 @Successor AbstractBeginNode falseSuccessor; 105 @Input(InputType.Condition) LogicNode condition; 106 protected double trueSuccessorProbability; 107 108 public LogicNode condition() { 109 return condition; 110 } 111 112 public void setCondition(LogicNode x) { 113 updateUsages(condition, x); 114 condition = x; 115 } 116 117 public IfNode(LogicNode condition, FixedNode trueSuccessor, FixedNode falseSuccessor, double trueSuccessorProbability) { 118 this(condition, BeginNode.begin(trueSuccessor), BeginNode.begin(falseSuccessor), trueSuccessorProbability); 119 } 120 121 public IfNode(LogicNode condition, AbstractBeginNode trueSuccessor, AbstractBeginNode falseSuccessor, double trueSuccessorProbability) { 122 super(TYPE, StampFactory.forVoid()); 123 this.condition = condition; 124 this.falseSuccessor = falseSuccessor; 125 this.trueSuccessor = trueSuccessor; 126 setTrueSuccessorProbability(trueSuccessorProbability); 127 } 128 129 /** 130 * Gets the true successor. 131 * 132 * @return the true successor 133 */ 134 public AbstractBeginNode trueSuccessor() { 135 return trueSuccessor; 136 } 137 138 /** 139 * Gets the false successor. 140 * 141 * @return the false successor 142 */ 143 public AbstractBeginNode falseSuccessor() { 144 return falseSuccessor; 145 } 146 147 public double getTrueSuccessorProbability() { 148 return this.trueSuccessorProbability; 149 } 150 151 public void setTrueSuccessor(AbstractBeginNode node) { 152 updatePredecessor(trueSuccessor, node); 153 trueSuccessor = node; 154 } 155 156 public void setFalseSuccessor(AbstractBeginNode node) { 157 updatePredecessor(falseSuccessor, node); 158 falseSuccessor = node; 159 } 160 161 /** 162 * Gets the node corresponding to the specified outcome of the branch. 163 * 164 * @param istrue {@code true} if the true successor is requested, {@code false} otherwise 165 * @return the corresponding successor 166 */ 167 public AbstractBeginNode successor(boolean istrue) { 168 return istrue ? trueSuccessor : falseSuccessor; 169 } 170 171 public void setTrueSuccessorProbability(double prob) { 172 assert prob >= -0.000000001 && prob <= 1.000000001 : "Probability out of bounds: " + prob; 173 trueSuccessorProbability = Math.min(1.0, Math.max(0.0, prob)); 174 } 175 176 @Override 177 public double probability(AbstractBeginNode successor) { 178 return successor == trueSuccessor ? trueSuccessorProbability : 1 - trueSuccessorProbability; 179 } 180 181 @Override 182 public void generate(NodeLIRBuilderTool gen) { 183 gen.emitIf(this); 184 } 185 186 @Override 187 public boolean verify() { 188 assertTrue(condition() != null, "missing condition"); 189 assertTrue(trueSuccessor() != null, "missing trueSuccessor"); 190 assertTrue(falseSuccessor() != null, "missing falseSuccessor"); 191 return super.verify(); 192 } 193 194 private boolean compareCallContext(NodeSourcePosition successorPosition) { 195 NodeSourcePosition position = getNodeSourcePosition(); 196 NodeSourcePosition successor = successorPosition; 197 while (position != null) { 198 assertTrue(Objects.equals(position.getMethod(), successor.getMethod()), "method mismatch"); 199 position = position.getCaller(); 200 successor = successor.getCaller(); 201 } 202 assertTrue(successor == null, "successor position has more methods"); 203 return true; 204 } 205 206 @Override 207 public boolean verifySourcePosition() { 208 NodeSourcePosition sourcePosition = getNodeSourcePosition(); 209 assertTrue(sourcePosition != null, "missing IfNode source position"); 210 211 NodeSourcePosition trueSuccessorPosition = trueSuccessor.getNodeSourcePosition(); 212 assertTrue(trueSuccessorPosition != null, "missing IfNode true successor source position"); 213 214 NodeSourcePosition falseSuccessorPosition = falseSuccessor.getNodeSourcePosition(); 215 assertTrue(falseSuccessorPosition != null, "missing IfNode false successor source position"); 216 217 int bci = sourcePosition.getBCI(); 218 ResolvedJavaMethod method = sourcePosition.getMethod(); 219 int bytecode = BytecodeDisassembler.getBytecodeAt(method, bci); 220 221 if (!Bytecodes.isIfBytecode(bytecode)) { 222 return true; 223 } 224 225 byte[] code = (new ResolvedJavaMethodBytecode(method)).getCode(); 226 int targetBCI = bci + Bytes.beS2(code, bci + 1); 227 int nextBCI = bci + Bytecodes.lengthOf(bytecode); 228 229 // At least one successor should have the correct BCI to indicate any possible negation that 230 // occurred after bytecode parsing 231 boolean matchingSuccessorFound = false; 232 if (trueSuccessorPosition.getBCI() == nextBCI || trueSuccessorPosition.getBCI() == targetBCI) { 233 assertTrue(compareCallContext(trueSuccessorPosition), "call context different from IfNode in trueSuccessor"); 234 matchingSuccessorFound = true; 235 } 236 237 if (falseSuccessorPosition.getBCI() == nextBCI || falseSuccessorPosition.getBCI() == targetBCI) { 238 assertTrue(compareCallContext(falseSuccessorPosition), "call context different from IfNode in falseSuccessor"); 239 matchingSuccessorFound = true; 240 } 241 242 assertTrue(matchingSuccessorFound, "no matching successor position found in IfNode"); 243 assertTrue(trueSuccessorPosition.getBCI() != falseSuccessorPosition.getBCI(), "successor positions same in IfNode"); 244 245 return true; 246 } 247 248 public void eliminateNegation() { 249 AbstractBeginNode oldTrueSuccessor = trueSuccessor; 250 AbstractBeginNode oldFalseSuccessor = falseSuccessor; 251 trueSuccessor = oldFalseSuccessor; 252 falseSuccessor = oldTrueSuccessor; 253 trueSuccessorProbability = 1 - trueSuccessorProbability; 254 setCondition(((LogicNegationNode) condition).getValue()); 255 } 256 257 @Override 258 public void simplify(SimplifierTool tool) { 259 if (trueSuccessor().next() instanceof DeoptimizeNode) { 260 if (trueSuccessorProbability != 0) { 261 CORRECTED_PROBABILITIES.increment(getDebug()); 262 trueSuccessorProbability = 0; 263 } 264 } else if (falseSuccessor().next() instanceof DeoptimizeNode) { 265 if (trueSuccessorProbability != 1) { 266 CORRECTED_PROBABILITIES.increment(getDebug()); 267 trueSuccessorProbability = 1; 268 } 269 } 270 271 if (condition() instanceof LogicNegationNode) { 272 eliminateNegation(); 273 } 274 if (condition() instanceof LogicConstantNode) { 275 LogicConstantNode c = (LogicConstantNode) condition(); 276 if (c.getValue()) { 277 tool.deleteBranch(falseSuccessor()); 278 tool.addToWorkList(trueSuccessor()); 279 graph().removeSplit(this, trueSuccessor()); 280 } else { 281 tool.deleteBranch(trueSuccessor()); 282 tool.addToWorkList(falseSuccessor()); 283 graph().removeSplit(this, falseSuccessor()); 284 } 285 return; 286 } 287 if (tool.allUsagesAvailable() && trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages()) { 288 289 pushNodesThroughIf(tool); 290 291 if (checkForUnsignedCompare(tool) || removeOrMaterializeIf(tool)) { 292 return; 293 } 294 } 295 296 if (removeIntermediateMaterialization(tool)) { 297 return; 298 } 299 300 if (splitIfAtPhi(tool)) { 301 return; 302 } 303 304 if (conditionalNodeOptimization(tool)) { 305 return; 306 } 307 308 if (switchTransformationOptimization(tool)) { 309 return; 310 } 311 312 if (falseSuccessor().hasNoUsages() && (!(falseSuccessor() instanceof LoopExitNode)) && falseSuccessor().next() instanceof IfNode && 313 !(((IfNode) falseSuccessor().next()).falseSuccessor() instanceof LoopExitNode)) { 314 AbstractBeginNode intermediateBegin = falseSuccessor(); 315 IfNode nextIf = (IfNode) intermediateBegin.next(); 316 double probabilityB = (1.0 - this.trueSuccessorProbability) * nextIf.trueSuccessorProbability; 317 if (this.trueSuccessorProbability < probabilityB) { 318 // Reordering of those two if statements is beneficial from the point of view of 319 // their probabilities. 320 if (prepareForSwap(tool, condition(), nextIf.condition())) { 321 // Reordering is allowed from (if1 => begin => if2) to (if2 => begin => if1). 322 assert intermediateBegin.next() == nextIf; 323 AbstractBeginNode bothFalseBegin = nextIf.falseSuccessor(); 324 nextIf.setFalseSuccessor(null); 325 intermediateBegin.setNext(null); 326 this.setFalseSuccessor(null); 327 328 this.replaceAtPredecessor(nextIf); 329 nextIf.setFalseSuccessor(intermediateBegin); 330 intermediateBegin.setNext(this); 331 this.setFalseSuccessor(bothFalseBegin); 332 333 NodeSourcePosition intermediateBeginPosition = intermediateBegin.getNodeSourcePosition(); 334 intermediateBegin.setNodeSourcePosition(bothFalseBegin.getNodeSourcePosition()); 335 bothFalseBegin.setNodeSourcePosition(intermediateBeginPosition); 336 337 nextIf.setTrueSuccessorProbability(probabilityB); 338 if (probabilityB == 1.0) { 339 this.setTrueSuccessorProbability(0.0); 340 } else { 341 double newProbability = this.trueSuccessorProbability / (1.0 - probabilityB); 342 this.setTrueSuccessorProbability(Math.min(1.0, newProbability)); 343 } 344 return; 345 } 346 } 347 } 348 349 if (tryEliminateBoxedReferenceEquals(tool)) { 350 return; 351 } 352 } 353 354 private static boolean isUnboxedFrom(MetaAccessProvider meta, NodeView view, ValueNode x, ValueNode src) { 355 if (x == src) { 356 return true; 357 } else if (x instanceof UnboxNode) { 358 return isUnboxedFrom(meta, view, ((UnboxNode) x).getValue(), src); 359 } else if (x instanceof PiNode) { 360 PiNode pi = (PiNode) x; 361 return isUnboxedFrom(meta, view, pi.getOriginalNode(), src); 362 } else if (x instanceof LoadFieldNode) { 363 LoadFieldNode load = (LoadFieldNode) x; 364 ResolvedJavaType integerType = meta.lookupJavaType(Integer.class); 365 if (load.getValue().stamp(view).javaType(meta).equals(integerType)) { 366 return isUnboxedFrom(meta, view, load.getValue(), src); 367 } else { 368 return false; 369 } 370 } else { 371 return false; 372 } 373 } 374 375 /** 376 * Attempts to replace the following pattern: 377 * 378 * <pre> 379 * Integer x = ...; 380 * Integer y = ...; 381 * if ((x == y) || x.equals(y)) { ... } 382 * </pre> 383 * 384 * with: 385 * 386 * <pre> 387 * Integer x = ...; 388 * Integer y = ...; 389 * if (x.equals(y)) { ... } 390 * </pre> 391 * 392 * whenever the probability that the reference check will pass is relatively small. 393 * 394 * See GR-1315 for more information. 395 */ 396 private boolean tryEliminateBoxedReferenceEquals(SimplifierTool tool) { 397 if (!(condition instanceof ObjectEqualsNode)) { 398 return false; 399 } 400 401 MetaAccessProvider meta = tool.getMetaAccess(); 402 ObjectEqualsNode equalsCondition = (ObjectEqualsNode) condition; 403 ValueNode x = equalsCondition.getX(); 404 ValueNode y = equalsCondition.getY(); 405 ResolvedJavaType integerType = meta.lookupJavaType(Integer.class); 406 407 // At least one argument for reference equal must be a boxed primitive. 408 NodeView view = NodeView.from(tool); 409 if (!x.stamp(view).javaType(meta).equals(integerType) && !y.stamp(view).javaType(meta).equals(integerType)) { 410 return false; 411 } 412 413 // The reference equality check is usually more efficient compared to a boxing check. 414 // The success of the reference equals must therefore be relatively rare, otherwise it makes 415 // no sense to eliminate it. 416 if (getTrueSuccessorProbability() > 0.4) { 417 return false; 418 } 419 420 // True branch must be empty. 421 if (trueSuccessor instanceof BeginNode || trueSuccessor instanceof LoopExitNode) { 422 if (trueSuccessor.next() instanceof EndNode) { 423 // Empty true branch. 424 } else { 425 return false; 426 } 427 } else { 428 return false; 429 } 430 431 // False branch must only check the unboxed values. 432 UnboxNode unbox = null; 433 FixedGuardNode unboxCheck = null; 434 for (FixedNode node : falseSuccessor.getBlockNodes()) { 435 if (!(node instanceof BeginNode || node instanceof UnboxNode || node instanceof FixedGuardNode || node instanceof EndNode || 436 node instanceof LoadFieldNode || node instanceof LoopExitNode)) { 437 return false; 438 } 439 if (node instanceof UnboxNode) { 440 if (unbox == null) { 441 unbox = (UnboxNode) node; 442 } else { 443 return false; 444 } 445 } 446 if (!(node instanceof FixedGuardNode)) { 447 continue; 448 } 449 FixedGuardNode fixed = (FixedGuardNode) node; 450 if (!(fixed.condition() instanceof IntegerEqualsNode)) { 451 continue; 452 } 453 IntegerEqualsNode equals = (IntegerEqualsNode) fixed.condition(); 454 if ((isUnboxedFrom(meta, view, equals.getX(), x) && isUnboxedFrom(meta, view, equals.getY(), y)) || 455 (isUnboxedFrom(meta, view, equals.getX(), y) && isUnboxedFrom(meta, view, equals.getY(), x))) { 456 unboxCheck = fixed; 457 } 458 } 459 if (unbox == null || unboxCheck == null) { 460 return false; 461 } 462 463 // Falsify the reference check. 464 setCondition(graph().addOrUniqueWithInputs(LogicConstantNode.contradiction())); 465 466 return true; 467 } 468 469 // SwitchFoldable implementation. 470 471 @Override 472 public Node getNextSwitchFoldableBranch() { 473 return falseSuccessor(); 474 } 475 476 @Override 477 public boolean isInSwitch(ValueNode switchValue) { 478 return SwitchFoldable.maybeIsInSwitch(condition()) && SwitchFoldable.sameSwitchValue(condition(), switchValue); 479 } 480 481 @Override 482 public void cutOffCascadeNode() { 483 setTrueSuccessor(null); 484 } 485 486 @Override 487 public void cutOffLowestCascadeNode() { 488 setFalseSuccessor(null); 489 setTrueSuccessor(null); 490 } 491 492 @Override 493 public AbstractBeginNode getDefault() { 494 return falseSuccessor(); 495 } 496 497 @Override 498 public ValueNode switchValue() { 499 if (SwitchFoldable.maybeIsInSwitch(condition())) { 500 return ((IntegerEqualsNode) condition()).getX(); 501 } 502 return null; 503 } 504 505 @Override 506 public boolean isNonInitializedProfile() { 507 return getTrueSuccessorProbability() == 0.5d; 508 } 509 510 @Override 511 public int intKeyAt(int i) { 512 assert i == 0; 513 return ((IntegerEqualsNode) condition()).getY().asJavaConstant().asInt(); 514 } 515 516 @Override 517 public double keyProbability(int i) { 518 assert i == 0; 519 return getTrueSuccessorProbability(); 520 } 521 522 @Override 523 public AbstractBeginNode keySuccessor(int i) { 524 assert i == 0; 525 return trueSuccessor(); 526 } 527 528 @Override 529 public double defaultProbability() { 530 return 1.0d - getTrueSuccessorProbability(); 531 } 532 533 /** 534 * Try to optimize this as if it were a {@link ConditionalNode}. 535 */ 536 private boolean conditionalNodeOptimization(SimplifierTool tool) { 537 if (trueSuccessor().next() instanceof AbstractEndNode && falseSuccessor().next() instanceof AbstractEndNode) { 538 AbstractEndNode trueEnd = (AbstractEndNode) trueSuccessor().next(); 539 AbstractEndNode falseEnd = (AbstractEndNode) falseSuccessor().next(); 540 if (trueEnd.merge() != falseEnd.merge()) { 541 return false; 542 } 543 if (!(trueEnd.merge() instanceof MergeNode)) { 544 return false; 545 } 546 MergeNode merge = (MergeNode) trueEnd.merge(); 547 if (!merge.hasExactlyOneUsage() || merge.phis().count() != 1) { 548 return false; 549 } 550 551 if (trueSuccessor().anchored().isNotEmpty() || falseSuccessor().anchored().isNotEmpty()) { 552 return false; 553 } 554 555 PhiNode phi = merge.phis().first(); 556 ValueNode falseValue = phi.valueAt(falseEnd); 557 ValueNode trueValue = phi.valueAt(trueEnd); 558 559 NodeView view = NodeView.from(tool); 560 ValueNode result = ConditionalNode.canonicalizeConditional(condition, trueValue, falseValue, phi.stamp(view), view); 561 if (result != null) { 562 /* 563 * canonicalizeConditional returns possibly new nodes so add them to the graph. 564 */ 565 if (result.graph() == null) { 566 result = graph().addOrUniqueWithInputs(result); 567 } 568 result = proxyReplacement(result); 569 /* 570 * This optimization can be performed even if multiple values merge at this phi 571 * since the two inputs get simplified into one. 572 */ 573 phi.setValueAt(trueEnd, result); 574 removeThroughFalseBranch(tool, merge); 575 return true; 576 } 577 } 578 579 return false; 580 } 581 582 private void pushNodesThroughIf(SimplifierTool tool) { 583 assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages(); 584 // push similar nodes upwards through the if, thereby deduplicating them 585 do { 586 AbstractBeginNode trueSucc = trueSuccessor(); 587 AbstractBeginNode falseSucc = falseSuccessor(); 588 if (trueSucc instanceof BeginNode && falseSucc instanceof BeginNode && trueSucc.next() instanceof FixedWithNextNode && falseSucc.next() instanceof FixedWithNextNode) { 589 FixedWithNextNode trueNext = (FixedWithNextNode) trueSucc.next(); 590 FixedWithNextNode falseNext = (FixedWithNextNode) falseSucc.next(); 591 NodeClass<?> nodeClass = trueNext.getNodeClass(); 592 if (trueNext.getClass() == falseNext.getClass()) { 593 if (trueNext instanceof AbstractBeginNode) { 594 // Cannot do this optimization for begin nodes, because it could 595 // move guards above the if that need to stay below a branch. 596 } else if (nodeClass.equalInputs(trueNext, falseNext) && trueNext.valueEquals(falseNext)) { 597 falseNext.replaceAtUsages(trueNext); 598 graph().removeFixed(falseNext); 599 GraphUtil.unlinkFixedNode(trueNext); 600 graph().addBeforeFixed(this, trueNext); 601 for (Node usage : trueNext.usages().snapshot()) { 602 if (usage.isAlive()) { 603 NodeClass<?> usageNodeClass = usage.getNodeClass(); 604 if (usageNodeClass.valueNumberable() && !usageNodeClass.isLeafNode()) { 605 Node newNode = graph().findDuplicate(usage); 606 if (newNode != null) { 607 usage.replaceAtUsagesAndDelete(newNode); 608 } 609 } 610 if (usage.isAlive()) { 611 tool.addToWorkList(usage); 612 } 613 } 614 } 615 continue; 616 } 617 } 618 } 619 break; 620 } while (true); 621 } 622 623 /** 624 * Recognize a couple patterns that can be merged into an unsigned compare. 625 * 626 * @param tool 627 * @return true if a replacement was done. 628 */ 629 @SuppressWarnings("try") 630 private boolean checkForUnsignedCompare(SimplifierTool tool) { 631 assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages(); 632 if (condition() instanceof IntegerLessThanNode) { 633 NodeView view = NodeView.from(tool); 634 IntegerLessThanNode lessThan = (IntegerLessThanNode) condition(); 635 Constant y = lessThan.getY().stamp(view).asConstant(); 636 if (y instanceof PrimitiveConstant && ((PrimitiveConstant) y).asLong() == 0 && falseSuccessor().next() instanceof IfNode) { 637 IfNode ifNode2 = (IfNode) falseSuccessor().next(); 638 if (ifNode2.condition() instanceof IntegerLessThanNode) { 639 IntegerLessThanNode lessThan2 = (IntegerLessThanNode) ifNode2.condition(); 640 AbstractBeginNode falseSucc = ifNode2.falseSuccessor(); 641 AbstractBeginNode trueSucc = ifNode2.trueSuccessor(); 642 IntegerBelowNode below = null; 643 /* 644 * Convert x >= 0 && x < positive which is represented as !(x < 0) && x < 645 * <positive> into an unsigned compare. 646 */ 647 if (lessThan2.getX() == lessThan.getX() && lessThan2.getY().stamp(view) instanceof IntegerStamp && 648 ((IntegerStamp) lessThan2.getY().stamp(view)).isPositive() && 649 sameDestination(trueSuccessor(), ifNode2.falseSuccessor)) { 650 below = graph().unique(new IntegerBelowNode(lessThan2.getX(), lessThan2.getY())); 651 // swap direction 652 AbstractBeginNode tmp = falseSucc; 653 falseSucc = trueSucc; 654 trueSucc = tmp; 655 } else if (lessThan2.getY() == lessThan.getX() && sameDestination(trueSuccessor(), ifNode2.trueSuccessor)) { 656 /* 657 * Convert x >= 0 && x <= positive which is represented as !(x < 0) && 658 * !(<positive> > x), into x <| positive + 1. This can only be done for 659 * constants since there isn't a IntegerBelowEqualThanNode but that doesn't 660 * appear to be interesting. 661 */ 662 JavaConstant positive = lessThan2.getX().asJavaConstant(); 663 if (positive != null && positive.asLong() > 0 && positive.asLong() < positive.getJavaKind().getMaxValue()) { 664 ConstantNode newLimit = ConstantNode.forIntegerStamp(lessThan2.getX().stamp(view), positive.asLong() + 1, graph()); 665 below = graph().unique(new IntegerBelowNode(lessThan.getX(), newLimit)); 666 } 667 } 668 if (below != null) { 669 try (DebugCloseable position = ifNode2.withNodeSourcePosition()) { 670 ifNode2.setTrueSuccessor(null); 671 ifNode2.setFalseSuccessor(null); 672 673 IfNode newIfNode = graph().add(new IfNode(below, falseSucc, trueSucc, 1 - trueSuccessorProbability)); 674 // Remove the < 0 test. 675 tool.deleteBranch(trueSuccessor); 676 graph().removeSplit(this, falseSuccessor); 677 678 // Replace the second test with the new one. 679 ifNode2.predecessor().replaceFirstSuccessor(ifNode2, newIfNode); 680 ifNode2.safeDelete(); 681 return true; 682 } 683 } 684 } 685 } else if (y instanceof PrimitiveConstant && ((PrimitiveConstant) y).asLong() < 0 && falseSuccessor().next() instanceof IfNode) { 686 IfNode ifNode2 = (IfNode) falseSuccessor().next(); 687 AbstractBeginNode falseSucc = ifNode2.falseSuccessor(); 688 AbstractBeginNode trueSucc = ifNode2.trueSuccessor(); 689 IntegerBelowNode below = null; 690 if (ifNode2.condition() instanceof IntegerLessThanNode) { 691 ValueNode x = lessThan.getX(); 692 IntegerLessThanNode lessThan2 = (IntegerLessThanNode) ifNode2.condition(); 693 /* 694 * Convert x >= -C1 && x < C2, represented as !(x < -C1) && x < C2, into an 695 * unsigned compare. This condition is equivalent to x + C1 |<| C1 + C2 if C1 + 696 * C2 does not overflow. 697 */ 698 Constant c2 = lessThan2.getY().stamp(view).asConstant(); 699 if (lessThan2.getX() == x && c2 instanceof PrimitiveConstant && ((PrimitiveConstant) c2).asLong() > 0 && 700 x.stamp(view).isCompatible(lessThan.getY().stamp(view)) && 701 x.stamp(view).isCompatible(lessThan2.getY().stamp(view)) && 702 sameDestination(trueSuccessor(), ifNode2.falseSuccessor)) { 703 long newLimitValue = -((PrimitiveConstant) y).asLong() + ((PrimitiveConstant) c2).asLong(); 704 // Make sure the limit fits into the target type without overflow. 705 if (newLimitValue > 0 && newLimitValue <= CodeUtil.maxValue(PrimitiveStamp.getBits(x.stamp(view)))) { 706 ConstantNode newLimit = ConstantNode.forIntegerStamp(x.stamp(view), newLimitValue, graph()); 707 ConstantNode c1 = ConstantNode.forIntegerStamp(x.stamp(view), -((PrimitiveConstant) y).asLong(), graph()); 708 ValueNode addNode = graph().addOrUniqueWithInputs(AddNode.create(x, c1, view)); 709 below = graph().unique(new IntegerBelowNode(addNode, newLimit)); 710 } 711 } 712 } 713 if (below != null) { 714 try (DebugCloseable position = ifNode2.withNodeSourcePosition()) { 715 ifNode2.setTrueSuccessor(null); 716 ifNode2.setFalseSuccessor(null); 717 718 IfNode newIfNode = graph().add(new IfNode(below, trueSucc, falseSucc, trueSuccessorProbability)); 719 // Remove the < -C1 test. 720 tool.deleteBranch(trueSuccessor); 721 graph().removeSplit(this, falseSuccessor); 722 723 // Replace the second test with the new one. 724 ifNode2.predecessor().replaceFirstSuccessor(ifNode2, newIfNode); 725 ifNode2.safeDelete(); 726 return true; 727 } 728 } 729 } 730 } 731 return false; 732 } 733 734 /** 735 * Check it these two blocks end up at the same place. Meeting at the same merge, or 736 * deoptimizing in the same way. 737 */ 738 private static boolean sameDestination(AbstractBeginNode succ1, AbstractBeginNode succ2) { 739 Node next1 = succ1.next(); 740 Node next2 = succ2.next(); 741 if (next1 instanceof EndNode && next2 instanceof EndNode) { 742 EndNode end1 = (EndNode) next1; 743 EndNode end2 = (EndNode) next2; 744 if (end1.merge() == end2.merge()) { 745 for (PhiNode phi : end1.merge().phis()) { 746 if (phi.valueAt(end1) != phi.valueAt(end2)) { 747 return false; 748 } 749 } 750 // They go to the same MergeNode and merge the same values 751 return true; 752 } 753 } else if (next1 instanceof DeoptimizeNode && next2 instanceof DeoptimizeNode) { 754 DeoptimizeNode deopt1 = (DeoptimizeNode) next1; 755 DeoptimizeNode deopt2 = (DeoptimizeNode) next2; 756 if (deopt1.getReason() == deopt2.getReason() && deopt1.getAction() == deopt2.getAction()) { 757 // Same deoptimization reason and action. 758 return true; 759 } 760 } else if (next1 instanceof LoopExitNode && next2 instanceof LoopExitNode) { 761 LoopExitNode exit1 = (LoopExitNode) next1; 762 LoopExitNode exit2 = (LoopExitNode) next2; 763 if (exit1.loopBegin() == exit2.loopBegin() && exit1.stateAfter() == exit2.stateAfter() && exit1.stateAfter() == null && sameDestination(exit1, exit2)) { 764 // Exit the same loop and end up at the same place. 765 return true; 766 } 767 } else if (next1 instanceof ReturnNode && next2 instanceof ReturnNode) { 768 ReturnNode exit1 = (ReturnNode) next1; 769 ReturnNode exit2 = (ReturnNode) next2; 770 if (exit1.result() == exit2.result()) { 771 // Exit the same loop and end up at the same place. 772 return true; 773 } 774 } 775 return false; 776 } 777 778 private static boolean prepareForSwap(SimplifierTool tool, LogicNode a, LogicNode b) { 779 DebugContext debug = a.getDebug(); 780 if (a instanceof InstanceOfNode) { 781 InstanceOfNode instanceOfA = (InstanceOfNode) a; 782 if (b instanceof IsNullNode) { 783 IsNullNode isNullNode = (IsNullNode) b; 784 if (isNullNode.getValue() == instanceOfA.getValue()) { 785 debug.log("Can swap instanceof and isnull if"); 786 return true; 787 } 788 } else if (b instanceof InstanceOfNode) { 789 InstanceOfNode instanceOfB = (InstanceOfNode) b; 790 if (instanceOfA.getValue() == instanceOfB.getValue() && !instanceOfA.type().getType().isInterface() && !instanceOfB.type().getType().isInterface() && 791 !instanceOfA.type().getType().isAssignableFrom(instanceOfB.type().getType()) && !instanceOfB.type().getType().isAssignableFrom(instanceOfA.type().getType())) { 792 // Two instanceof on the same value with mutually exclusive types. 793 debug.log("Can swap instanceof for types %s and %s", instanceOfA.type(), instanceOfB.type()); 794 return true; 795 } 796 } 797 } else if (a instanceof CompareNode) { 798 CompareNode compareA = (CompareNode) a; 799 Condition conditionA = compareA.condition().asCondition(); 800 if (compareA.unorderedIsTrue()) { 801 return false; 802 } 803 if (b instanceof CompareNode) { 804 CompareNode compareB = (CompareNode) b; 805 if (compareA == compareB) { 806 debug.log("Same conditions => do not swap and leave the work for global value numbering."); 807 return false; 808 } 809 if (compareB.unorderedIsTrue()) { 810 return false; 811 } 812 Condition comparableCondition = null; 813 Condition conditionB = compareB.condition().asCondition(); 814 if (compareB.getX() == compareA.getX() && compareB.getY() == compareA.getY()) { 815 comparableCondition = conditionB; 816 } else if (compareB.getX() == compareA.getY() && compareB.getY() == compareA.getX()) { 817 comparableCondition = conditionB.mirror(); 818 } 819 820 if (comparableCondition != null) { 821 Condition combined = conditionA.join(comparableCondition); 822 if (combined == null) { 823 // The two conditions are disjoint => can reorder. 824 debug.log("Can swap disjoint coditions on same values: %s and %s", conditionA, comparableCondition); 825 return true; 826 } 827 } else if (conditionA == Condition.EQ && conditionB == Condition.EQ) { 828 boolean canSwap = false; 829 if ((compareA.getX() == compareB.getX() && valuesDistinct(tool, compareA.getY(), compareB.getY()))) { 830 canSwap = true; 831 } else if ((compareA.getX() == compareB.getY() && valuesDistinct(tool, compareA.getY(), compareB.getX()))) { 832 canSwap = true; 833 } else if ((compareA.getY() == compareB.getX() && valuesDistinct(tool, compareA.getX(), compareB.getY()))) { 834 canSwap = true; 835 } else if ((compareA.getY() == compareB.getY() && valuesDistinct(tool, compareA.getX(), compareB.getX()))) { 836 canSwap = true; 837 } 838 839 if (canSwap) { 840 debug.log("Can swap equality condition with one shared and one disjoint value."); 841 return true; 842 } 843 } 844 } 845 } 846 847 return false; 848 } 849 850 private static boolean valuesDistinct(SimplifierTool tool, ValueNode a, ValueNode b) { 851 if (a.isConstant() && b.isConstant()) { 852 Boolean equal = tool.getConstantReflection().constantEquals(a.asConstant(), b.asConstant()); 853 if (equal != null) { 854 return !equal.booleanValue(); 855 } 856 } 857 858 NodeView view = NodeView.from(tool); 859 Stamp stampA = a.stamp(view); 860 Stamp stampB = b.stamp(view); 861 return stampA.alwaysDistinct(stampB); 862 } 863 864 /** 865 * Tries to remove an empty if construct or replace an if construct with a materialization. 866 * 867 * @return true if a transformation was made, false otherwise 868 */ 869 private boolean removeOrMaterializeIf(SimplifierTool tool) { 870 assert trueSuccessor().hasNoUsages() && falseSuccessor().hasNoUsages(); 871 if (trueSuccessor().next() instanceof AbstractEndNode && falseSuccessor().next() instanceof AbstractEndNode) { 872 AbstractEndNode trueEnd = (AbstractEndNode) trueSuccessor().next(); 873 AbstractEndNode falseEnd = (AbstractEndNode) falseSuccessor().next(); 874 AbstractMergeNode merge = trueEnd.merge(); 875 if (merge == falseEnd.merge() && trueSuccessor().anchored().isEmpty() && falseSuccessor().anchored().isEmpty()) { 876 PhiNode singlePhi = null; 877 int distinct = 0; 878 for (PhiNode phi : merge.phis()) { 879 ValueNode trueValue = phi.valueAt(trueEnd); 880 ValueNode falseValue = phi.valueAt(falseEnd); 881 if (trueValue != falseValue) { 882 distinct++; 883 singlePhi = phi; 884 } 885 } 886 if (distinct == 0) { 887 /* 888 * Multiple phis but merging same values for true and false, so simply delete 889 * the path 890 */ 891 removeThroughFalseBranch(tool, merge); 892 return true; 893 } else if (distinct == 1) { 894 // Fortify: Suppress Null Dereference false positive 895 assert singlePhi != null; 896 897 ValueNode trueValue = singlePhi.valueAt(trueEnd); 898 ValueNode falseValue = singlePhi.valueAt(falseEnd); 899 ValueNode conditional = canonicalizeConditionalCascade(tool, trueValue, falseValue); 900 if (conditional != null) { 901 conditional = proxyReplacement(conditional); 902 singlePhi.setValueAt(trueEnd, conditional); 903 removeThroughFalseBranch(tool, merge); 904 return true; 905 } 906 } 907 } 908 } 909 if (trueSuccessor().next() instanceof ReturnNode && falseSuccessor().next() instanceof ReturnNode) { 910 ReturnNode trueEnd = (ReturnNode) trueSuccessor().next(); 911 ReturnNode falseEnd = (ReturnNode) falseSuccessor().next(); 912 ValueNode trueValue = trueEnd.result(); 913 ValueNode falseValue = falseEnd.result(); 914 ValueNode value = null; 915 boolean needsProxy = false; 916 if (trueValue != null) { 917 if (trueValue == falseValue) { 918 value = trueValue; 919 } else { 920 value = canonicalizeConditionalCascade(tool, trueValue, falseValue); 921 if (value == null) { 922 return false; 923 } 924 needsProxy = true; 925 } 926 } 927 928 if (trueSuccessor() instanceof LoopExitNode) { 929 LoopBeginNode loopBegin = ((LoopExitNode) trueSuccessor()).loopBegin(); 930 assert loopBegin == ((LoopExitNode) falseSuccessor()).loopBegin(); 931 LoopExitNode loopExitNode = graph().add(new LoopExitNode(loopBegin)); 932 graph().addBeforeFixed(this, loopExitNode); 933 if (graph().hasValueProxies() && needsProxy) { 934 value = graph().addOrUnique(new ValueProxyNode(value, loopExitNode)); 935 } 936 } 937 938 ReturnNode newReturn = graph().add(new ReturnNode(value)); 939 replaceAtPredecessor(newReturn); 940 GraphUtil.killCFG(this); 941 return true; 942 } 943 return false; 944 } 945 946 private ValueNode proxyReplacement(ValueNode replacement) { 947 /* 948 * Special case: Every empty diamond we collapse to a conditional node can potentially 949 * contain loop exit nodes on both branches. See the graph below: The two loop exits 950 * (instanceof begin node) exit the same loop. The resulting phi is defined outside the 951 * loop, but the resulting conditional node will be inside the loop, so we need to proxy the 952 * resulting conditional node. Callers of this method ensure that true and false successor 953 * have no usages, therefore a and b in the graph below can never be proxies themselves. 954 */ 955 // @formatter:off 956 // +--+ 957 // |If| 958 // +--+ +-----+ +-----+ 959 // +----+ +----+ | a | | b | 960 // |Lex | |Lex | +----^+ +^----+ 961 // +----+ +----+ | | 962 // +-------+ +---+ 963 // | Merge +---------+Phi| 964 // +-------+ +---+ 965 // @formatter:on 966 if (this.graph().hasValueProxies()) { 967 if (trueSuccessor instanceof LoopExitNode && falseSuccessor instanceof LoopExitNode) { 968 assert ((LoopExitNode) trueSuccessor).loopBegin() == ((LoopExitNode) falseSuccessor).loopBegin(); 969 /* 970 * we can collapse all proxy nodes on one loop exit, the surviving one, which will 971 * be the true successor 972 */ 973 if (falseSuccessor.anchored().isEmpty() && falseSuccessor.hasUsages()) { 974 for (Node n : falseSuccessor.usages().snapshot()) { 975 assert n instanceof ProxyNode; 976 ((ProxyNode) n).setProxyPoint((LoopExitNode) trueSuccessor); 977 } 978 } 979 /* 980 * The true successor (surviving loop exit) can have usages, namely proxy nodes, the 981 * false successor however, must not have usages any more after the code above 982 */ 983 assert trueSuccessor.anchored().isEmpty() && falseSuccessor.hasNoUsages(); 984 return this.graph().addOrUnique(new ValueProxyNode(replacement, (LoopExitNode) trueSuccessor)); 985 } 986 } 987 return replacement; 988 } 989 990 protected void removeThroughFalseBranch(SimplifierTool tool, AbstractMergeNode merge) { 991 AbstractBeginNode trueBegin = trueSuccessor(); 992 LogicNode conditionNode = condition(); 993 graph().removeSplitPropagate(this, trueBegin); 994 tool.addToWorkList(trueBegin); 995 if (conditionNode != null) { 996 GraphUtil.tryKillUnused(conditionNode); 997 } 998 if (merge.isAlive() && merge.forwardEndCount() > 1) { 999 for (FixedNode end : merge.forwardEnds()) { 1000 Node cur = end; 1001 while (cur != null && cur.predecessor() instanceof BeginNode) { 1002 cur = cur.predecessor(); 1003 } 1004 if (cur != null && cur.predecessor() instanceof IfNode) { 1005 tool.addToWorkList(cur.predecessor()); 1006 } 1007 } 1008 } 1009 } 1010 1011 private ValueNode canonicalizeConditionalViaImplies(ValueNode trueValue, ValueNode falseValue) { 1012 ValueNode collapsedTrue = trueValue; 1013 ValueNode collapsedFalse = falseValue; 1014 boolean simplify = false; 1015 if (trueValue instanceof ConditionalNode) { 1016 TriState result = condition().implies(false, ((ConditionalNode) trueValue).condition()); 1017 if (result.isKnown()) { 1018 simplify = true; 1019 collapsedTrue = result.toBoolean() ? ((ConditionalNode) trueValue).trueValue() : ((ConditionalNode) trueValue).falseValue(); 1020 } 1021 } 1022 if (falseValue instanceof ConditionalNode) { 1023 TriState result = condition().implies(true, ((ConditionalNode) falseValue).condition()); 1024 if (result.isKnown()) { 1025 simplify = true; 1026 collapsedFalse = result.toBoolean() ? ((ConditionalNode) falseValue).trueValue() : ((ConditionalNode) falseValue).falseValue(); 1027 } 1028 } 1029 if (simplify) { 1030 return graph().unique(new ConditionalNode(condition(), collapsedTrue, collapsedFalse)); 1031 } 1032 return null; 1033 } 1034 1035 private ValueNode canonicalizeConditionalCascade(SimplifierTool tool, ValueNode trueValue, ValueNode falseValue) { 1036 if (trueValue.getStackKind() != falseValue.getStackKind()) { 1037 return null; 1038 } 1039 if (trueValue.getStackKind() != JavaKind.Int && trueValue.getStackKind() != JavaKind.Long) { 1040 return null; 1041 } 1042 if (trueValue.isConstant() && falseValue.isConstant()) { 1043 return graph().unique(new ConditionalNode(condition(), trueValue, falseValue)); 1044 } 1045 ValueNode value = canonicalizeConditionalViaImplies(trueValue, falseValue); 1046 if (value != null) { 1047 return value; 1048 } 1049 if (!graph().isAfterExpandLogic()) { 1050 /* 1051 * !isAfterExpandLogic() => Cannot spawn NormalizeCompareNodes after lowering in the 1052 * ExpandLogicPhase. 1053 */ 1054 ConditionalNode conditional = null; 1055 ValueNode constant = null; 1056 boolean negateCondition; 1057 if (trueValue instanceof ConditionalNode && falseValue.isConstant()) { 1058 conditional = (ConditionalNode) trueValue; 1059 constant = falseValue; 1060 negateCondition = true; 1061 } else if (falseValue instanceof ConditionalNode && trueValue.isConstant()) { 1062 conditional = (ConditionalNode) falseValue; 1063 constant = trueValue; 1064 negateCondition = false; 1065 } else { 1066 return null; 1067 } 1068 boolean negateConditionalCondition = false; 1069 ValueNode otherValue = null; 1070 if (constant == conditional.trueValue()) { 1071 otherValue = conditional.falseValue(); 1072 negateConditionalCondition = false; 1073 } else if (constant == conditional.falseValue()) { 1074 otherValue = conditional.trueValue(); 1075 negateConditionalCondition = true; 1076 } 1077 if (otherValue != null && otherValue.isConstant()) { 1078 double shortCutProbability = probability(trueSuccessor()); 1079 LogicNode newCondition = LogicNode.or(condition(), negateCondition, conditional.condition(), negateConditionalCondition, shortCutProbability); 1080 return graph().unique(new ConditionalNode(newCondition, constant, otherValue)); 1081 } 1082 1083 if (constant.isJavaConstant() && conditional.trueValue().isJavaConstant() && conditional.falseValue().isJavaConstant() && condition() instanceof CompareNode && 1084 conditional.condition() instanceof CompareNode) { 1085 1086 CompareNode condition1 = (CompareNode) condition(); 1087 Condition cond1 = condition1.condition().asCondition(); 1088 if (negateCondition) { 1089 cond1 = cond1.negate(); 1090 } 1091 // cond1 is EQ, NE, LT, or GE 1092 CompareNode condition2 = (CompareNode) conditional.condition(); 1093 Condition cond2 = condition2.condition().asCondition(); 1094 ValueNode x = condition1.getX(); 1095 ValueNode y = condition1.getY(); 1096 ValueNode x2 = condition2.getX(); 1097 ValueNode y2 = condition2.getY(); 1098 // `x cond1 y ? c1 : (x2 cond2 y2 ? c2 : c3)` 1099 boolean sameVars = x == x2 && y == y2; 1100 if (!sameVars && x == y2 && y == x2) { 1101 sameVars = true; 1102 cond2 = cond2.mirror(); 1103 } 1104 if (sameVars) { 1105 1106 JavaKind stackKind = conditional.trueValue().stamp(NodeView.from(tool)).getStackKind(); 1107 assert !stackKind.isNumericFloat(); 1108 1109 long c1 = constant.asJavaConstant().asLong(); 1110 long c2 = conditional.trueValue().asJavaConstant().asLong(); 1111 long c3 = conditional.falseValue().asJavaConstant().asLong(); 1112 1113 // canonicalize cond2 1114 cond2 = cond2.join(cond1.negate()); 1115 if (cond2 == null) { 1116 // mixing signed and unsigned cases, or useless combination of conditions 1117 return null; 1118 } 1119 // derive cond3 from cond1 and cond2 1120 Condition cond3 = cond1.negate().join(cond2.negate()); 1121 if (cond3 == null) { 1122 // mixing signed and unsigned cases, or useless combination of conditions 1123 return null; 1124 } 1125 boolean unsigned = cond1.isUnsigned() || cond2.isUnsigned(); 1126 boolean floatingPoint = x.stamp(NodeView.from(tool)) instanceof FloatStamp; 1127 assert !floatingPoint || y.stamp(NodeView.from(tool)) instanceof FloatStamp; 1128 assert !(floatingPoint && unsigned); 1129 1130 long expected1 = expectedConstantForNormalize(cond1); 1131 long expected2 = expectedConstantForNormalize(cond2); 1132 long expected3 = expectedConstantForNormalize(cond3); 1133 1134 if (c1 == expected1 && c2 == expected2 && c3 == expected3) { 1135 // normal order 1136 } else if (c1 == 0 - expected1 && c2 == 0 - expected2 && c3 == 0 - expected3) { 1137 // reverse order 1138 ValueNode tmp = x; 1139 x = y; 1140 y = tmp; 1141 } else { 1142 // cannot be expressed by NormalizeCompareNode 1143 return null; 1144 } 1145 if (floatingPoint) { 1146 boolean unorderedLess = false; 1147 if (((FloatStamp) x.stamp).canBeNaN() || ((FloatStamp) y.stamp).canBeNaN()) { 1148 // we may encounter NaNs, check the unordered value 1149 // (following the original condition's "unorderedIsTrue" path) 1150 long unorderedValue = condition1.unorderedIsTrue() ? c1 : condition2.unorderedIsTrue() ? c2 : c3; 1151 if (unorderedValue == 0) { 1152 // returning "0" for unordered is not possible 1153 return null; 1154 } 1155 unorderedLess = unorderedValue == -1; 1156 } 1157 return graph().unique(new FloatNormalizeCompareNode(x, y, stackKind, unorderedLess)); 1158 } else { 1159 return graph().unique(new IntegerNormalizeCompareNode(x, y, stackKind, unsigned)); 1160 } 1161 } 1162 } 1163 } 1164 return null; 1165 } 1166 1167 private static long expectedConstantForNormalize(Condition condition) { 1168 if (condition == Condition.EQ) { 1169 return 0; 1170 } else if (condition == Condition.LT || condition == Condition.BT) { 1171 return -1; 1172 } else { 1173 assert condition == Condition.GT || condition == Condition.AT; 1174 return 1; 1175 } 1176 } 1177 1178 public enum NodeColor { 1179 NONE, 1180 CONDITION_USAGE, 1181 TRUE_BRANCH, 1182 FALSE_BRANCH, 1183 PHI_MIXED, 1184 MIXED 1185 } 1186 1187 /** 1188 * Take an if that is immediately dominated by a merge with a single phi and split off any paths 1189 * where the test would be statically decidable creating a new merge below the appropriate side 1190 * of the IfNode. Any undecidable tests will continue to use the original IfNode. 1191 * 1192 * @param tool 1193 */ 1194 @SuppressWarnings("try") 1195 private boolean splitIfAtPhi(SimplifierTool tool) { 1196 if (!(predecessor() instanceof MergeNode)) { 1197 return false; 1198 } 1199 MergeNode merge = (MergeNode) predecessor(); 1200 if (merge.forwardEndCount() == 1) { 1201 // Don't bother. 1202 return false; 1203 } 1204 if (merge.getUsageCount() != 1 || merge.phis().count() != 1) { 1205 // Don't trigger with multiple phis. Would require more rewiring. 1206 // Most of the time the additional phis are memory phis that are removed after 1207 // fixed read phase. 1208 return false; 1209 } 1210 if (graph().getGuardsStage().areFrameStatesAtSideEffects() && merge.stateAfter() == null) { 1211 return false; 1212 } 1213 1214 PhiNode generalPhi = merge.phis().first(); 1215 if (!(generalPhi instanceof ValuePhiNode)) { 1216 return false; 1217 } 1218 1219 ValuePhiNode phi = (ValuePhiNode) generalPhi; 1220 1221 EconomicMap<Node, NodeColor> coloredNodes = EconomicMap.create(Equivalence.IDENTITY, 8); 1222 1223 /* 1224 * Check that the condition uses the phi and that there is only one user of the condition 1225 * expression. 1226 */ 1227 if (!conditionUses(condition(), phi, coloredNodes)) { 1228 return false; 1229 } 1230 1231 if (!mayRemoveSplit(merge)) { 1232 return false; 1233 } 1234 1235 LogicNode[] results = new LogicNode[merge.forwardEndCount()]; 1236 boolean success = false; 1237 for (int i = 0; i < results.length; ++i) { 1238 ValueNode value = phi.valueAt(i); 1239 LogicNode curResult = computeCondition(tool, condition, phi, value); 1240 if (curResult != condition) { 1241 for (Node n : curResult.inputs()) { 1242 if (n instanceof ConstantNode || n instanceof ParameterNode || n instanceof FixedNode) { 1243 // Constant inputs or parameters or fixed nodes are OK. 1244 } else if (n == value) { 1245 // References to the value itself are also OK. 1246 } else { 1247 // Input may cause scheduling issues. 1248 curResult = condition; 1249 break; 1250 } 1251 } 1252 success = true; 1253 } 1254 results[i] = curResult; 1255 } 1256 1257 if (!success) { 1258 return false; 1259 } 1260 1261 for (Node usage : phi.usages()) { 1262 if (usage == merge.stateAfter()) { 1263 // This usage can be ignored, because it is directly in the state after. 1264 } else { 1265 NodeColor color = colorUsage(coloredNodes, usage, merge, this.trueSuccessor(), this.falseSuccessor()); 1266 if (color == NodeColor.MIXED) { 1267 return false; 1268 } 1269 } 1270 } 1271 1272 /* 1273 * We could additionally filter for the case that at least some of the Phi inputs or one of 1274 * the condition inputs are constants but there are cases where a non-constant is 1275 * simplifiable, usually where the stamp allows the question to be answered. 1276 */ 1277 1278 /* Each successor of the if gets a new merge if needed. */ 1279 MergeNode trueMerge = null; 1280 MergeNode falseMerge = null; 1281 int i = 0; 1282 for (EndNode end : merge.forwardEnds().snapshot()) { 1283 ValueNode value = phi.valueAt(end); 1284 LogicNode result = results[i++]; 1285 if (result instanceof LogicConstantNode) { 1286 if (((LogicConstantNode) result).getValue()) { 1287 if (trueMerge == null) { 1288 trueMerge = insertMerge(trueSuccessor(), phi, merge.stateAfter(), tool); 1289 replaceNodesInBranch(coloredNodes, NodeColor.TRUE_BRANCH, phi, trueMerge.phis().first()); 1290 } 1291 trueMerge.phis().first().addInput(value); 1292 trueMerge.addForwardEnd(end); 1293 } else { 1294 if (falseMerge == null) { 1295 falseMerge = insertMerge(falseSuccessor(), phi, merge.stateAfter(), tool); 1296 replaceNodesInBranch(coloredNodes, NodeColor.FALSE_BRANCH, phi, falseMerge.phis().first()); 1297 } 1298 falseMerge.phis().first().addInput(value); 1299 falseMerge.addForwardEnd(end); 1300 } 1301 merge.removeEnd(end); 1302 } else if (result != condition) { 1303 // Build a new IfNode using the new condition 1304 BeginNode trueBegin = graph().add(new BeginNode()); 1305 trueBegin.setNodeSourcePosition(trueSuccessor().getNodeSourcePosition()); 1306 BeginNode falseBegin = graph().add(new BeginNode()); 1307 falseBegin.setNodeSourcePosition(falseSuccessor().getNodeSourcePosition()); 1308 1309 if (result.graph() == null) { 1310 result = graph().addOrUniqueWithInputs(result); 1311 result.setNodeSourcePosition(condition.getNodeSourcePosition()); 1312 } 1313 IfNode newIfNode = graph().add(new IfNode(result, trueBegin, falseBegin, trueSuccessorProbability)); 1314 newIfNode.setNodeSourcePosition(getNodeSourcePosition()); 1315 1316 if (trueMerge == null) { 1317 trueMerge = insertMerge(trueSuccessor(), phi, merge.stateAfter(), tool); 1318 replaceNodesInBranch(coloredNodes, NodeColor.TRUE_BRANCH, phi, trueMerge.phis().first()); 1319 } 1320 trueMerge.phis().first().addInput(value); 1321 trueBegin.setNext(graph().add(new EndNode())); 1322 trueMerge.addForwardEnd((EndNode) trueBegin.next()); 1323 1324 if (falseMerge == null) { 1325 falseMerge = insertMerge(falseSuccessor(), phi, merge.stateAfter(), tool); 1326 replaceNodesInBranch(coloredNodes, NodeColor.FALSE_BRANCH, phi, falseMerge.phis().first()); 1327 } 1328 falseMerge.phis().first().addInput(value); 1329 falseBegin.setNext(graph().add(new EndNode())); 1330 falseMerge.addForwardEnd((EndNode) falseBegin.next()); 1331 1332 merge.removeEnd(end); 1333 ((FixedWithNextNode) end.predecessor()).setNext(newIfNode); 1334 end.safeDelete(); 1335 } 1336 } 1337 1338 cleanupMerge(merge); 1339 cleanupMerge(trueMerge); 1340 cleanupMerge(falseMerge); 1341 1342 return true; 1343 } 1344 1345 private static void replaceNodesInBranch(EconomicMap<Node, NodeColor> coloredNodes, NodeColor branch, ValuePhiNode phi, ValueNode newValue) { 1346 for (Node n : phi.usages().snapshot()) { 1347 if (coloredNodes.get(n) == branch) { 1348 n.replaceAllInputs(phi, newValue); 1349 } else if (coloredNodes.get(n) == NodeColor.PHI_MIXED) { 1350 assert n instanceof PhiNode; 1351 PhiNode phiNode = (PhiNode) n; 1352 AbstractMergeNode merge = phiNode.merge(); 1353 for (int i = 0; i < merge.forwardEndCount(); ++i) { 1354 if (phiNode.valueAt(i) == phi && coloredNodes.get(merge.forwardEndAt(i)) == branch) { 1355 phiNode.setValueAt(i, newValue); 1356 } 1357 } 1358 } 1359 } 1360 } 1361 1362 private NodeColor colorUsage(EconomicMap<Node, NodeColor> coloredNodes, Node node, MergeNode merge, AbstractBeginNode trueSucc, AbstractBeginNode falseSucc) { 1363 NodeColor color = coloredNodes.get(node); 1364 if (color == null) { 1365 1366 if (coloredNodes.size() >= MAX_USAGE_COLOR_SET_SIZE) { 1367 return NodeColor.MIXED; 1368 } 1369 1370 coloredNodes.put(node, NodeColor.MIXED); 1371 1372 if (node == merge) { 1373 color = NodeColor.MIXED; 1374 } else if (node == trueSucc) { 1375 color = NodeColor.TRUE_BRANCH; 1376 } else if (node == falseSucc) { 1377 color = NodeColor.FALSE_BRANCH; 1378 } else { 1379 if (node instanceof AbstractMergeNode) { 1380 AbstractMergeNode mergeNode = (AbstractMergeNode) node; 1381 NodeColor combinedColor = null; 1382 for (int i = 0; i < mergeNode.forwardEndCount(); ++i) { 1383 NodeColor curColor = colorUsage(coloredNodes, mergeNode.forwardEndAt(i), merge, trueSucc, falseSucc); 1384 if (combinedColor == null) { 1385 combinedColor = curColor; 1386 } else if (combinedColor != curColor) { 1387 combinedColor = NodeColor.MIXED; 1388 break; 1389 } 1390 } 1391 color = combinedColor; 1392 } else if (node instanceof StartNode) { 1393 color = NodeColor.MIXED; 1394 } else if (node instanceof FixedNode) { 1395 FixedNode fixedNode = (FixedNode) node; 1396 Node predecessor = fixedNode.predecessor(); 1397 assert predecessor != null : fixedNode; 1398 color = colorUsage(coloredNodes, predecessor, merge, trueSucc, falseSucc); 1399 } else if (node instanceof PhiNode) { 1400 PhiNode phiNode = (PhiNode) node; 1401 AbstractMergeNode phiMerge = phiNode.merge(); 1402 1403 if (phiMerge instanceof LoopBeginNode) { 1404 color = colorUsage(coloredNodes, phiMerge, merge, trueSucc, falseSucc); 1405 } else { 1406 1407 for (int i = 0; i < phiMerge.forwardEndCount(); ++i) { 1408 NodeColor curColor = colorUsage(coloredNodes, phiMerge.forwardEndAt(i), merge, trueSucc, falseSucc); 1409 if (curColor != NodeColor.TRUE_BRANCH && curColor != NodeColor.FALSE_BRANCH) { 1410 color = NodeColor.MIXED; 1411 break; 1412 } 1413 } 1414 1415 if (color == null) { 1416 // Each of the inputs to the phi are either coming unambigously from 1417 // true or false branch. 1418 color = NodeColor.PHI_MIXED; 1419 assert node instanceof PhiNode; 1420 } 1421 } 1422 } else { 1423 NodeColor combinedColor = null; 1424 for (Node n : node.usages()) { 1425 if (n != node) { 1426 NodeColor curColor = colorUsage(coloredNodes, n, merge, trueSucc, falseSucc); 1427 if (combinedColor == null) { 1428 combinedColor = curColor; 1429 } else if (combinedColor != curColor) { 1430 combinedColor = NodeColor.MIXED; 1431 break; 1432 } 1433 } 1434 } 1435 if (combinedColor == NodeColor.PHI_MIXED) { 1436 combinedColor = NodeColor.MIXED; 1437 } 1438 if (combinedColor == null) { 1439 // Floating node without usages => association unclear. 1440 combinedColor = NodeColor.MIXED; 1441 } 1442 color = combinedColor; 1443 } 1444 } 1445 1446 assert color != null : node; 1447 coloredNodes.put(node, color); 1448 } 1449 return color; 1450 } 1451 1452 /** 1453 * @param condition 1454 * @param phi 1455 * @param coloredNodes 1456 * @return true if the passed in {@code condition} uses {@code phi} and the condition is only 1457 * used once. Since the phi will go dead the condition using it will also have to be 1458 * dead after the optimization. 1459 */ 1460 private static boolean conditionUses(LogicNode condition, PhiNode phi, EconomicMap<Node, NodeColor> coloredNodes) { 1461 if (!condition.hasExactlyOneUsage()) { 1462 return false; 1463 } 1464 if (condition instanceof ShortCircuitOrNode) { 1465 if (condition.graph().getGuardsStage().areDeoptsFixed()) { 1466 /* 1467 * It can be unsafe to simplify a ShortCircuitOr before deopts are fixed because 1468 * conversion to guards assumes that all the required conditions are being tested. 1469 * Simplfying the condition based on context before this happens may lose a 1470 * condition. 1471 */ 1472 ShortCircuitOrNode orNode = (ShortCircuitOrNode) condition; 1473 return (conditionUses(orNode.x, phi, coloredNodes) || conditionUses(orNode.y, phi, coloredNodes)); 1474 } 1475 } else if (condition instanceof Canonicalizable.Unary<?>) { 1476 Canonicalizable.Unary<?> unary = (Canonicalizable.Unary<?>) condition; 1477 if (unary.getValue() == phi) { 1478 coloredNodes.put(condition, NodeColor.CONDITION_USAGE); 1479 return true; 1480 } 1481 } else if (condition instanceof Canonicalizable.Binary<?>) { 1482 Canonicalizable.Binary<?> binary = (Canonicalizable.Binary<?>) condition; 1483 if (binary.getX() == phi || binary.getY() == phi) { 1484 coloredNodes.put(condition, NodeColor.CONDITION_USAGE); 1485 return true; 1486 } 1487 } 1488 return false; 1489 } 1490 1491 /** 1492 * Canonicalize {@code} condition using {@code value} in place of {@code phi}. 1493 * 1494 * @param tool 1495 * @param condition 1496 * @param phi 1497 * @param value 1498 * @return an improved LogicNode or the original condition 1499 */ 1500 @SuppressWarnings("unchecked") 1501 private static LogicNode computeCondition(SimplifierTool tool, LogicNode condition, PhiNode phi, Node value) { 1502 if (condition instanceof ShortCircuitOrNode) { 1503 if (condition.graph().getGuardsStage().areDeoptsFixed() && !condition.graph().isAfterExpandLogic()) { 1504 ShortCircuitOrNode orNode = (ShortCircuitOrNode) condition; 1505 LogicNode resultX = computeCondition(tool, orNode.x, phi, value); 1506 LogicNode resultY = computeCondition(tool, orNode.y, phi, value); 1507 if (resultX != orNode.x || resultY != orNode.y) { 1508 LogicNode result = orNode.canonical(tool, resultX, resultY); 1509 if (result != orNode) { 1510 return result; 1511 } 1512 /* 1513 * Create a new node to carry the optimized inputs. 1514 */ 1515 ShortCircuitOrNode newOr = new ShortCircuitOrNode(resultX, orNode.xNegated, resultY, 1516 orNode.yNegated, orNode.getShortCircuitProbability()); 1517 return newOr.canonical(tool); 1518 } 1519 return orNode; 1520 } 1521 } else if (condition instanceof Canonicalizable.Binary<?>) { 1522 Canonicalizable.Binary<Node> compare = (Canonicalizable.Binary<Node>) condition; 1523 if (compare.getX() == phi || compare.getY() == phi) { 1524 return (LogicNode) compare.canonical(tool, compare.getX() == phi ? value : compare.getX(), compare.getY() == phi ? value : compare.getY()); 1525 } 1526 } else if (condition instanceof Canonicalizable.Unary<?>) { 1527 Canonicalizable.Unary<Node> compare = (Canonicalizable.Unary<Node>) condition; 1528 if (compare.getValue() == phi) { 1529 return (LogicNode) compare.canonical(tool, value); 1530 } 1531 } 1532 if (condition instanceof Canonicalizable) { 1533 return (LogicNode) ((Canonicalizable) condition).canonical(tool); 1534 } 1535 return condition; 1536 } 1537 1538 private void cleanupMerge(MergeNode merge) { 1539 if (merge != null && merge.isAlive()) { 1540 if (merge.forwardEndCount() == 0) { 1541 GraphUtil.killCFG(merge); 1542 } else if (merge.forwardEndCount() == 1) { 1543 graph().reduceTrivialMerge(merge); 1544 } 1545 } 1546 } 1547 1548 @SuppressWarnings("try") 1549 private MergeNode insertMerge(AbstractBeginNode begin, ValuePhiNode oldPhi, FrameState stateAfter, SimplifierTool tool) { 1550 MergeNode merge = graph().add(new MergeNode()); 1551 1552 AbstractBeginNode newBegin; 1553 try (DebugCloseable position = begin.withNodeSourcePosition()) { 1554 newBegin = graph().add(new BeginNode()); 1555 begin.replaceAtPredecessor(newBegin); 1556 newBegin.setNext(begin); 1557 } 1558 1559 FixedNode next = newBegin.next(); 1560 next.replaceAtPredecessor(merge); 1561 newBegin.setNext(graph().add(new EndNode())); 1562 merge.addForwardEnd((EndNode) newBegin.next()); 1563 1564 ValuePhiNode phi = begin.graph().addOrUnique(new ValuePhiNode(oldPhi.stamp(NodeView.DEFAULT), merge)); 1565 phi.addInput(oldPhi); 1566 1567 if (stateAfter != null) { 1568 FrameState newState = stateAfter.duplicate(); 1569 newState.replaceAllInputs(oldPhi, phi); 1570 merge.setStateAfter(newState); 1571 } 1572 merge.setNext(next); 1573 tool.addToWorkList(begin); 1574 return merge; 1575 } 1576 1577 /** 1578 * Tries to connect code that initializes a variable directly with the successors of an if 1579 * construct that switches on the variable. For example, the pseudo code below: 1580 * 1581 * <pre> 1582 * contains(list, e, yes, no) { 1583 * if (list == null || e == null) { 1584 * condition = false; 1585 * } else { 1586 * condition = false; 1587 * for (i in list) { 1588 * if (i.equals(e)) { 1589 * condition = true; 1590 * break; 1591 * } 1592 * } 1593 * } 1594 * if (condition) { 1595 * return yes; 1596 * } else { 1597 * return no; 1598 * } 1599 * } 1600 * </pre> 1601 * 1602 * will be transformed into: 1603 * 1604 * <pre> 1605 * contains(list, e, yes, no) { 1606 * if (list == null || e == null) { 1607 * return no; 1608 * } else { 1609 * condition = false; 1610 * for (i in list) { 1611 * if (i.equals(e)) { 1612 * return yes; 1613 * } 1614 * } 1615 * return no; 1616 * } 1617 * } 1618 * </pre> 1619 * 1620 * @return true if a transformation was made, false otherwise 1621 */ 1622 private boolean removeIntermediateMaterialization(SimplifierTool tool) { 1623 if (!(predecessor() instanceof AbstractMergeNode) || predecessor() instanceof LoopBeginNode) { 1624 return false; 1625 } 1626 AbstractMergeNode merge = (AbstractMergeNode) predecessor(); 1627 1628 if (!(condition() instanceof CompareNode)) { 1629 return false; 1630 } 1631 1632 CompareNode compare = (CompareNode) condition(); 1633 if (compare.getUsageCount() != 1) { 1634 return false; 1635 } 1636 1637 // Only consider merges with a single usage that is both a phi and an operand of the 1638 // comparison 1639 NodeIterable<Node> mergeUsages = merge.usages(); 1640 if (mergeUsages.count() != 1) { 1641 return false; 1642 } 1643 Node singleUsage = mergeUsages.first(); 1644 if (!(singleUsage instanceof ValuePhiNode) || (singleUsage != compare.getX() && singleUsage != compare.getY())) { 1645 return false; 1646 } 1647 1648 // Ensure phi is used by at most the comparison and the merge's frame state (if any) 1649 ValuePhiNode phi = (ValuePhiNode) singleUsage; 1650 NodeIterable<Node> phiUsages = phi.usages(); 1651 for (Node usage : phiUsages) { 1652 if (usage == compare) { 1653 continue; 1654 } 1655 if (usage == merge.stateAfter()) { 1656 continue; 1657 } 1658 // Checkstyle: stop 1659 // @formatter:off 1660 // 1661 // We also want to allow the usage to be on the loop-proxy if one of the branches is a 1662 // loop exit. 1663 // 1664 // This pattern: 1665 // 1666 // if------->cond 1667 // / \ 1668 // begin begin 1669 // | | 1670 // end end C1 V2 1671 // \ / \ / 1672 // merge---------->phi<------ C1 1673 // | ^ \ / 1674 // if-------------|-------->== 1675 // / \ | 1676 // A B<--------Proxy 1677 // 1678 // Must be simplified to: 1679 // 1680 // if---------------------->cond 1681 // / \ 1682 // A B<--------Proxy------>V2 1683 // 1684 // @formatter:on 1685 // Checkstyle: resume 1686 if (usage instanceof ValueProxyNode) { 1687 ValueProxyNode proxy = (ValueProxyNode) usage; 1688 if (proxy.proxyPoint() == trueSuccessor || proxy.proxyPoint() == falseSuccessor) { 1689 continue; 1690 } 1691 } 1692 return false; 1693 } 1694 1695 List<EndNode> mergePredecessors = merge.cfgPredecessors().snapshot(); 1696 assert phi.valueCount() == merge.forwardEndCount(); 1697 1698 Constant[] xs = constantValues(compare.getX(), merge, false); 1699 Constant[] ys = constantValues(compare.getY(), merge, false); 1700 if (xs == null || ys == null) { 1701 return false; 1702 } 1703 1704 if (!mayRemoveSplit(merge)) { 1705 return false; 1706 } 1707 1708 List<EndNode> falseEnds = new ArrayList<>(mergePredecessors.size()); 1709 List<EndNode> trueEnds = new ArrayList<>(mergePredecessors.size()); 1710 EconomicMap<AbstractEndNode, ValueNode> phiValues = EconomicMap.create(Equivalence.IDENTITY, mergePredecessors.size()); 1711 1712 AbstractBeginNode oldFalseSuccessor = falseSuccessor(); 1713 AbstractBeginNode oldTrueSuccessor = trueSuccessor(); 1714 1715 setFalseSuccessor(null); 1716 setTrueSuccessor(null); 1717 1718 Iterator<EndNode> ends = mergePredecessors.iterator(); 1719 for (int i = 0; i < xs.length; i++) { 1720 EndNode end = ends.next(); 1721 phiValues.put(end, phi.valueAt(end)); 1722 if (compare.condition().foldCondition(xs[i], ys[i], tool.getConstantReflection(), compare.unorderedIsTrue())) { 1723 trueEnds.add(end); 1724 } else { 1725 falseEnds.add(end); 1726 } 1727 } 1728 assert !ends.hasNext(); 1729 assert falseEnds.size() + trueEnds.size() == xs.length; 1730 1731 connectEnds(falseEnds, phi, phiValues, oldFalseSuccessor, merge, tool); 1732 connectEnds(trueEnds, phi, phiValues, oldTrueSuccessor, merge, tool); 1733 1734 if (this.trueSuccessorProbability == 0.0) { 1735 for (AbstractEndNode endNode : trueEnds) { 1736 propagateZeroProbability(endNode); 1737 } 1738 } 1739 1740 if (this.trueSuccessorProbability == 1.0) { 1741 for (AbstractEndNode endNode : falseEnds) { 1742 propagateZeroProbability(endNode); 1743 } 1744 } 1745 1746 /* 1747 * Remove obsolete ends only after processing all ends, otherwise oldTrueSuccessor or 1748 * oldFalseSuccessor might have been removed if it is a LoopExitNode. 1749 */ 1750 if (falseEnds.isEmpty()) { 1751 GraphUtil.killCFG(oldFalseSuccessor); 1752 } 1753 if (trueEnds.isEmpty()) { 1754 GraphUtil.killCFG(oldTrueSuccessor); 1755 } 1756 GraphUtil.killCFG(merge); 1757 1758 assert !merge.isAlive() : merge; 1759 assert !phi.isAlive() : phi; 1760 assert !compare.isAlive() : compare; 1761 assert !this.isAlive() : this; 1762 1763 return true; 1764 } 1765 1766 private boolean mayRemoveSplit(AbstractMergeNode merge) { 1767 1768 if (merge.stateAfter() != null && (!checkFrameState(trueSuccessor, MAX_FRAMESTATE_SEARCH_DEPTH) || !checkFrameState(trueSuccessor, MAX_FRAMESTATE_SEARCH_DEPTH))) { 1769 return false; 1770 } 1771 1772 return true; 1773 } 1774 1775 private static void propagateZeroProbability(FixedNode startNode) { 1776 Node prev = null; 1777 for (FixedNode node : GraphUtil.predecessorIterable(startNode)) { 1778 if (node instanceof IfNode) { 1779 IfNode ifNode = (IfNode) node; 1780 if (ifNode.trueSuccessor() == prev) { 1781 if (ifNode.trueSuccessorProbability == 0.0) { 1782 return; 1783 } else if (ifNode.trueSuccessorProbability == 1.0) { 1784 continue; 1785 } else { 1786 ifNode.setTrueSuccessorProbability(0.0); 1787 return; 1788 } 1789 } else if (ifNode.falseSuccessor() == prev) { 1790 if (ifNode.trueSuccessorProbability == 1.0) { 1791 return; 1792 } else if (ifNode.trueSuccessorProbability == 0.0) { 1793 continue; 1794 } else { 1795 ifNode.setTrueSuccessorProbability(1.0); 1796 return; 1797 } 1798 } else { 1799 throw new GraalError("Illegal state"); 1800 } 1801 } else if (node instanceof AbstractMergeNode && !(node instanceof LoopBeginNode)) { 1802 for (AbstractEndNode endNode : ((AbstractMergeNode) node).cfgPredecessors()) { 1803 propagateZeroProbability(endNode); 1804 } 1805 return; 1806 } 1807 prev = node; 1808 } 1809 } 1810 1811 /** 1812 * Snippet lowerings may produce patterns without a frame state on the merge. We need to take 1813 * extra care when optimizing these patterns. 1814 */ 1815 private static boolean checkFrameState(FixedNode start, int maxDepth) { 1816 if (maxDepth == 0) { 1817 return false; 1818 } 1819 FixedNode node = start; 1820 while (true) { 1821 if (node instanceof AbstractMergeNode) { 1822 AbstractMergeNode mergeNode = (AbstractMergeNode) node; 1823 if (mergeNode.stateAfter() == null) { 1824 return false; 1825 } else { 1826 return true; 1827 } 1828 } else if (node instanceof StateSplit) { 1829 StateSplit stateSplitNode = (StateSplit) node; 1830 if (stateSplitNode.stateAfter() != null) { 1831 return true; 1832 } 1833 } 1834 1835 if (node instanceof ControlSplitNode) { 1836 ControlSplitNode controlSplitNode = (ControlSplitNode) node; 1837 for (Node succ : controlSplitNode.cfgSuccessors()) { 1838 if (checkFrameState((FixedNode) succ, maxDepth - 1)) { 1839 return true; 1840 } 1841 } 1842 return false; 1843 } else if (node instanceof FixedWithNextNode) { 1844 FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) node; 1845 node = fixedWithNextNode.next(); 1846 } else if (node instanceof AbstractEndNode) { 1847 AbstractEndNode endNode = (AbstractEndNode) node; 1848 node = endNode.merge(); 1849 } else if (node instanceof ControlSinkNode) { 1850 return true; 1851 } else { 1852 assert false : "unexpected node"; 1853 return false; 1854 } 1855 } 1856 } 1857 1858 /** 1859 * Connects a set of ends to a given successor, inserting a merge node if there is more than one 1860 * end. If {@code ends} is not empty, then {@code successor} is added to {@code tool}'s 1861 * {@linkplain SimplifierTool#addToWorkList(org.graalvm.compiler.graph.Node) work list}. 1862 * 1863 * @param phi the original single-usage phi of the preceding merge 1864 * @param phiValues the values of the phi at the merge, keyed by the merge ends 1865 * @param oldMerge the merge being removed 1866 */ 1867 private void connectEnds(List<EndNode> ends, ValuePhiNode phi, EconomicMap<AbstractEndNode, ValueNode> phiValues, AbstractBeginNode successor, AbstractMergeNode oldMerge, SimplifierTool tool) { 1868 if (!ends.isEmpty()) { 1869 // If there was a value proxy usage, then the proxy needs a new value. 1870 ValueProxyNode valueProxy = null; 1871 if (successor instanceof LoopExitNode) { 1872 for (Node usage : phi.usages()) { 1873 if (usage instanceof ValueProxyNode && ((ValueProxyNode) usage).proxyPoint() == successor) { 1874 valueProxy = (ValueProxyNode) usage; 1875 } 1876 } 1877 } 1878 final ValueProxyNode proxy = valueProxy; 1879 if (ends.size() == 1) { 1880 AbstractEndNode end = ends.get(0); 1881 if (proxy != null) { 1882 phi.replaceAtUsages(phiValues.get(end), n -> n == proxy); 1883 } 1884 ((FixedWithNextNode) end.predecessor()).setNext(successor); 1885 oldMerge.removeEnd(end); 1886 GraphUtil.killCFG(end); 1887 } else { 1888 // Need a new phi in case the frame state is used by more than the merge being 1889 // removed. 1890 NodeView view = NodeView.from(tool); 1891 AbstractMergeNode newMerge = graph().add(new MergeNode()); 1892 PhiNode oldPhi = (PhiNode) oldMerge.usages().first(); 1893 PhiNode newPhi = graph().addWithoutUnique(new ValuePhiNode(oldPhi.stamp(view), newMerge)); 1894 1895 if (proxy != null) { 1896 phi.replaceAtUsages(newPhi, n -> n == proxy); 1897 } 1898 1899 for (EndNode end : ends) { 1900 newPhi.addInput(phiValues.get(end)); 1901 newMerge.addForwardEnd(end); 1902 } 1903 1904 FrameState stateAfter = oldMerge.stateAfter(); 1905 if (stateAfter != null) { 1906 stateAfter = stateAfter.duplicate(); 1907 stateAfter.replaceFirstInput(oldPhi, newPhi); 1908 newMerge.setStateAfter(stateAfter); 1909 } 1910 1911 newMerge.setNext(successor); 1912 } 1913 tool.addToWorkList(successor); 1914 } 1915 } 1916 1917 /** 1918 * Gets an array of constants derived from a node that is either a {@link ConstantNode} or a 1919 * {@link PhiNode} whose input values are all constants. The length of the returned array is 1920 * equal to the number of ends terminating in a given merge node. 1921 * 1922 * @return null if {@code node} is neither a {@link ConstantNode} nor a {@link PhiNode} whose 1923 * input values are all constants 1924 */ 1925 public static Constant[] constantValues(ValueNode node, AbstractMergeNode merge, boolean allowNull) { 1926 if (node.isConstant()) { 1927 Constant[] result = new Constant[merge.forwardEndCount()]; 1928 Arrays.fill(result, node.asConstant()); 1929 return result; 1930 } 1931 1932 if (node instanceof PhiNode) { 1933 PhiNode phi = (PhiNode) node; 1934 if (phi.merge() == merge && phi instanceof ValuePhiNode && phi.valueCount() == merge.forwardEndCount()) { 1935 Constant[] result = new Constant[merge.forwardEndCount()]; 1936 int i = 0; 1937 for (ValueNode n : phi.values()) { 1938 if (!allowNull && !n.isConstant()) { 1939 return null; 1940 } 1941 result[i++] = n.asConstant(); 1942 } 1943 return result; 1944 } 1945 } 1946 1947 return null; 1948 } 1949 1950 @Override 1951 public AbstractBeginNode getPrimarySuccessor() { 1952 return null; 1953 } 1954 1955 public AbstractBeginNode getSuccessor(boolean result) { 1956 return result ? this.trueSuccessor() : this.falseSuccessor(); 1957 } 1958 1959 @Override 1960 public boolean setProbability(AbstractBeginNode successor, double value) { 1961 if (successor == this.trueSuccessor()) { 1962 this.setTrueSuccessorProbability(value); 1963 return true; 1964 } else if (successor == this.falseSuccessor()) { 1965 this.setTrueSuccessorProbability(1.0 - value); 1966 return true; 1967 } 1968 return false; 1969 } 1970 1971 @Override 1972 public int getSuccessorCount() { 1973 return 2; 1974 } 1975 }