1 /* 2 * Copyright (c) 2015, 2016, 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.phases.common; 24 25 import java.util.ArrayDeque; 26 import java.util.ArrayList; 27 import java.util.Collections; 28 import java.util.Deque; 29 import java.util.Iterator; 30 import java.util.List; 31 import java.util.function.Function; 32 33 import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph; 34 import org.graalvm.compiler.core.common.cfg.BlockMap; 35 import org.graalvm.compiler.core.common.type.ArithmeticOpTable; 36 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp; 37 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.And; 38 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Or; 39 import org.graalvm.compiler.core.common.type.IntegerStamp; 40 import org.graalvm.compiler.core.common.type.ObjectStamp; 41 import org.graalvm.compiler.core.common.type.Stamp; 42 import org.graalvm.compiler.core.common.type.StampFactory; 43 import org.graalvm.compiler.core.common.type.TypeReference; 44 import org.graalvm.compiler.debug.Debug; 45 import org.graalvm.compiler.debug.DebugCounter; 46 import org.graalvm.compiler.graph.Node; 47 import org.graalvm.compiler.graph.NodeMap; 48 import org.graalvm.compiler.graph.spi.CanonicalizerTool; 49 import org.graalvm.compiler.nodeinfo.InputType; 50 import org.graalvm.compiler.nodes.AbstractBeginNode; 51 import org.graalvm.compiler.nodes.BeginNode; 52 import org.graalvm.compiler.nodes.BinaryOpLogicNode; 53 import org.graalvm.compiler.nodes.ConditionAnchorNode; 54 import org.graalvm.compiler.nodes.ConstantNode; 55 import org.graalvm.compiler.nodes.DeoptimizeNode; 56 import org.graalvm.compiler.nodes.DeoptimizingGuard; 57 import org.graalvm.compiler.nodes.FixedGuardNode; 58 import org.graalvm.compiler.nodes.FixedNode; 59 import org.graalvm.compiler.nodes.GuardNode; 60 import org.graalvm.compiler.nodes.GuardProxyNode; 61 import org.graalvm.compiler.nodes.IfNode; 62 import org.graalvm.compiler.nodes.LogicNode; 63 import org.graalvm.compiler.nodes.LoopExitNode; 64 import org.graalvm.compiler.nodes.ParameterNode; 65 import org.graalvm.compiler.nodes.ShortCircuitOrNode; 66 import org.graalvm.compiler.nodes.StructuredGraph; 67 import org.graalvm.compiler.nodes.UnaryOpLogicNode; 68 import org.graalvm.compiler.nodes.ValueNode; 69 import org.graalvm.compiler.nodes.calc.AndNode; 70 import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode; 71 import org.graalvm.compiler.nodes.calc.BinaryNode; 72 import org.graalvm.compiler.nodes.calc.IntegerEqualsNode; 73 import org.graalvm.compiler.nodes.calc.ObjectEqualsNode; 74 import org.graalvm.compiler.nodes.calc.PointerEqualsNode; 75 import org.graalvm.compiler.nodes.calc.UnaryNode; 76 import org.graalvm.compiler.nodes.cfg.Block; 77 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; 78 import org.graalvm.compiler.nodes.extended.GuardingNode; 79 import org.graalvm.compiler.nodes.extended.IntegerSwitchNode; 80 import org.graalvm.compiler.nodes.extended.LoadHubNode; 81 import org.graalvm.compiler.nodes.extended.ValueAnchorNode; 82 import org.graalvm.compiler.nodes.java.LoadFieldNode; 83 import org.graalvm.compiler.nodes.java.TypeSwitchNode; 84 import org.graalvm.compiler.nodes.spi.NodeWithState; 85 import org.graalvm.compiler.nodes.util.GraphUtil; 86 import org.graalvm.compiler.phases.BasePhase; 87 import org.graalvm.compiler.phases.common.LoweringPhase.Frame; 88 import org.graalvm.compiler.phases.schedule.SchedulePhase; 89 import org.graalvm.compiler.phases.tiers.PhaseContext; 90 91 import jdk.vm.ci.meta.JavaConstant; 92 import jdk.vm.ci.meta.TriState; 93 94 public class DominatorConditionalEliminationPhase extends BasePhase<PhaseContext> { 95 96 private static final DebugCounter counterStampsRegistered = Debug.counter("StampsRegistered"); 97 private static final DebugCounter counterStampsFound = Debug.counter("StampsFound"); 98 private static final DebugCounter counterIfsKilled = Debug.counter("CE_KilledIfs"); 99 private static final DebugCounter counterLFFolded = Debug.counter("ConstantLFFolded"); 100 private final boolean fullSchedule; 101 102 public DominatorConditionalEliminationPhase(boolean fullSchedule) { 103 this.fullSchedule = fullSchedule; 104 } 105 106 @Override 107 @SuppressWarnings("try") 108 protected void run(StructuredGraph graph, PhaseContext context) { 109 try (Debug.Scope s = Debug.scope("DominatorConditionalElimination")) { 110 Function<Block, Iterable<? extends Node>> blockToNodes; 111 Function<Node, Block> nodeToBlock; 112 Block startBlock; 113 114 if (fullSchedule) { 115 SchedulePhase schedule = new SchedulePhase(SchedulePhase.SchedulingStrategy.EARLIEST); 116 schedule.apply(graph); 117 ControlFlowGraph cfg = graph.getLastSchedule().getCFG(); 118 cfg.computePostdominators(); 119 blockToNodes = b -> graph.getLastSchedule().getBlockToNodesMap().get(b); 120 nodeToBlock = n -> graph.getLastSchedule().getNodeToBlockMap().get(n); 121 startBlock = cfg.getStartBlock(); 122 } else { 123 ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, true, true); 124 BlockMap<List<FixedNode>> nodes = new BlockMap<>(cfg); 125 for (Block b : cfg.getBlocks()) { 126 ArrayList<FixedNode> curNodes = new ArrayList<>(); 127 for (FixedNode node : b.getNodes()) { 128 if (node instanceof AbstractBeginNode || node instanceof FixedGuardNode || node instanceof ConditionAnchorNode || node instanceof IfNode) { 129 curNodes.add(node); 130 } 131 } 132 nodes.put(b, curNodes); 133 } 134 blockToNodes = b -> nodes.get(b); 135 nodeToBlock = n -> cfg.blockFor(n); 136 startBlock = cfg.getStartBlock(); 137 } 138 new Instance(graph, blockToNodes, nodeToBlock, context).processBlock(startBlock); 139 } 140 } 141 142 public static class Instance { 143 protected NodeMap<Info> map; 144 protected Deque<LoopExitNode> loopExits; 145 protected final Function<Block, Iterable<? extends Node>> blockToNodes; 146 protected final Function<Node, Block> nodeToBlock; 147 protected final CanonicalizerTool tool; 148 /** 149 * Tests which may be eliminated because post dominating tests to prove a broader condition. 150 */ 151 private Deque<PendingTest> pendingTests; 152 153 public Instance(StructuredGraph graph, Function<Block, Iterable<? extends Node>> blockToNodes, 154 Function<Node, Block> nodeToBlock, PhaseContext context) { 155 map = graph.createNodeMap(); 156 loopExits = new ArrayDeque<>(); 157 this.blockToNodes = blockToNodes; 158 this.nodeToBlock = nodeToBlock; 159 pendingTests = new ArrayDeque<>(); 160 tool = GraphUtil.getDefaultSimplifier(context.getMetaAccess(), context.getConstantReflection(), context.getConstantFieldProvider(), false, graph.getAssumptions(), context.getLowerer()); 161 } 162 163 public void processBlock(Block startBlock) { 164 LoweringPhase.processBlock(new InstanceFrame(startBlock, null)); 165 } 166 167 public class InstanceFrame extends LoweringPhase.Frame<InstanceFrame> { 168 protected List<Runnable> undoOperations = new ArrayList<>(); 169 170 public InstanceFrame(Block block, InstanceFrame parent) { 171 super(block, parent); 172 } 173 174 @Override 175 public Frame<?> enter(Block b) { 176 return new InstanceFrame(b, this); 177 } 178 179 @Override 180 public void postprocess() { 181 Debug.log("[Post Processing block %s]", block); 182 undoOperations.forEach(x -> x.run()); 183 } 184 185 protected void processConditionAnchor(ConditionAnchorNode node) { 186 tryProveCondition(node.condition(), (guard, result) -> { 187 if (result != node.isNegated()) { 188 node.replaceAtUsages(guard); 189 GraphUtil.unlinkFixedNode(node); 190 GraphUtil.killWithUnusedFloatingInputs(node); 191 } else { 192 ValueAnchorNode valueAnchor = node.graph().add(new ValueAnchorNode(null)); 193 node.replaceAtUsages(valueAnchor); 194 node.graph().replaceFixedWithFixed(node, valueAnchor); 195 } 196 return true; 197 }); 198 } 199 200 protected void processGuard(GuardNode node) { 201 if (!tryProveGuardCondition(node, node.getCondition(), (guard, result) -> { 202 if (result != node.isNegated()) { 203 node.replaceAndDelete(guard); 204 } else { 205 DeoptimizeNode deopt = node.graph().add(new DeoptimizeNode(node.getAction(), node.getReason(), node.getSpeculation())); 206 AbstractBeginNode beginNode = (AbstractBeginNode) node.getAnchor(); 207 FixedNode next = beginNode.next(); 208 beginNode.setNext(deopt); 209 GraphUtil.killCFG(next); 210 } 211 return true; 212 })) { 213 registerNewCondition(node.getCondition(), node.isNegated(), node); 214 } 215 } 216 217 protected void processFixedGuard(FixedGuardNode node) { 218 if (!tryProveGuardCondition(node, node.condition(), (guard, result) -> { 219 if (result != node.isNegated()) { 220 node.replaceAtUsages(guard); 221 GraphUtil.unlinkFixedNode(node); 222 GraphUtil.killWithUnusedFloatingInputs(node); 223 } else { 224 DeoptimizeNode deopt = node.graph().add(new DeoptimizeNode(node.getAction(), node.getReason(), node.getSpeculation())); 225 deopt.setStateBefore(node.stateBefore()); 226 node.replaceAtPredecessor(deopt); 227 GraphUtil.killCFG(node); 228 } 229 Debug.log("Kill fixed guard guard"); 230 return true; 231 })) { 232 registerNewCondition(node.condition(), node.isNegated(), node); 233 } 234 } 235 236 protected void processIf(IfNode node) { 237 tryProveCondition(node.condition(), (guard, result) -> { 238 AbstractBeginNode survivingSuccessor = node.getSuccessor(result); 239 survivingSuccessor.replaceAtUsages(InputType.Guard, guard); 240 survivingSuccessor.replaceAtPredecessor(null); 241 node.replaceAtPredecessor(survivingSuccessor); 242 GraphUtil.killCFG(node); 243 if (survivingSuccessor instanceof BeginNode) { 244 undoOperations.add(() -> { 245 if (survivingSuccessor.isAlive()) { 246 ((BeginNode) survivingSuccessor).trySimplify(); 247 } 248 }); 249 } 250 Debug.log("Kill if"); 251 counterIfsKilled.increment(); 252 return true; 253 }); 254 } 255 256 @Override 257 public void preprocess() { 258 Debug.log("[Pre Processing block %s]", block); 259 AbstractBeginNode beginNode = block.getBeginNode(); 260 if (beginNode instanceof LoopExitNode && beginNode.isAlive()) { 261 LoopExitNode loopExitNode = (LoopExitNode) beginNode; 262 Instance.this.loopExits.push(loopExitNode); 263 undoOperations.add(() -> loopExits.pop()); 264 } else if (block.getDominator() != null && (block.getDominator().getLoopDepth() > block.getLoopDepth() || 265 (block.getDominator().getLoopDepth() == block.getLoopDepth() && block.getDominator().getLoop() != block.getLoop()))) { 266 // We are exiting the loop, but there is not a single loop exit block along our 267 // dominator tree (e.g., we are a merge of two loop exits). 268 final NodeMap<Info> oldMap = map; 269 final Deque<LoopExitNode> oldLoopExits = loopExits; 270 map = map.graph().createNodeMap(); 271 loopExits = new ArrayDeque<>(); 272 undoOperations.add(() -> { 273 map = oldMap; 274 loopExits = oldLoopExits; 275 }); 276 } 277 278 // For now conservatively collect guards only within the same block. 279 pendingTests.clear(); 280 for (Node n : blockToNodes.apply(block)) { 281 if (n.isAlive()) { 282 processNode(n); 283 } 284 } 285 } 286 287 protected void processNode(Node node) { 288 if (node instanceof NodeWithState && !(node instanceof GuardingNode)) { 289 pendingTests.clear(); 290 } 291 if (node instanceof AbstractBeginNode) { 292 processAbstractBegin((AbstractBeginNode) node); 293 } else if (node instanceof FixedGuardNode) { 294 processFixedGuard((FixedGuardNode) node); 295 } else if (node instanceof GuardNode) { 296 processGuard((GuardNode) node); 297 } else if (node instanceof ConditionAnchorNode) { 298 processConditionAnchor((ConditionAnchorNode) node); 299 } else if (node instanceof IfNode) { 300 processIf((IfNode) node); 301 } else { 302 return; 303 } 304 } 305 306 protected void registerNewCondition(LogicNode condition, boolean negated, ValueNode guard) { 307 if (!negated && condition instanceof PointerEqualsNode) { 308 PointerEqualsNode pe = (PointerEqualsNode) condition; 309 ValueNode x = pe.getX(); 310 ValueNode y = pe.getY(); 311 if (y.isConstant()) { 312 JavaConstant constant = y.asJavaConstant(); 313 Stamp succeeding = pe.getSucceedingStampForX(negated); 314 if (succeeding == null && pe instanceof ObjectEqualsNode && guard instanceof FixedGuardNode) { 315 succeeding = y.stamp(); 316 } 317 if (succeeding != null) { 318 if (y.stamp() instanceof ObjectStamp) { 319 GuardedConstantStamp cos = new GuardedConstantStamp(constant, (ObjectStamp) succeeding); 320 registerNewStamp(x, cos, guard); 321 return; 322 } 323 } 324 } 325 } 326 if (condition instanceof UnaryOpLogicNode) { 327 UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) condition; 328 Stamp newStamp = unaryLogicNode.getSucceedingStampForValue(negated); 329 registerNewStamp(unaryLogicNode.getValue(), newStamp, guard); 330 } else if (condition instanceof BinaryOpLogicNode) { 331 BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) condition; 332 ValueNode x = binaryOpLogicNode.getX(); 333 if (!x.isConstant()) { 334 Stamp newStampX = binaryOpLogicNode.getSucceedingStampForX(negated); 335 registerNewStamp(x, newStampX, guard); 336 } 337 338 ValueNode y = binaryOpLogicNode.getY(); 339 if (!y.isConstant()) { 340 Stamp newStampY = binaryOpLogicNode.getSucceedingStampForY(negated); 341 registerNewStamp(y, newStampY, guard); 342 } 343 if (condition instanceof IntegerEqualsNode && guard instanceof DeoptimizingGuard && !negated) { 344 if (y.isConstant() && x instanceof AndNode) { 345 AndNode and = (AndNode) x; 346 if (and.getY() == y) { 347 /* 348 * This 'and' proves something about some of the bits in and.getX(). 349 * It's equivalent to or'ing in the mask value since those values 350 * are known to be set. 351 */ 352 BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp()).getOr(); 353 IntegerStamp newStampX = (IntegerStamp) op.foldStamp(and.getX().stamp(), y.stamp()); 354 registerNewStamp(and.getX(), newStampX, guard); 355 } 356 } 357 } 358 } 359 if (guard instanceof DeoptimizingGuard) { 360 pendingTests.push(new PendingTest(condition, (DeoptimizingGuard) guard)); 361 } 362 registerCondition(condition, negated, guard); 363 } 364 365 @SuppressWarnings("try") 366 protected Pair<InfoElement, Stamp> foldFromConstLoadField(LoadFieldNode loadFieldNode, InfoElementProvider info) { 367 ValueNode object = loadFieldNode.object(); 368 if (!loadFieldNode.field().isStatic()) { 369 // look if we got stamp info for the object and return the constant stamp 370 Pair<InfoElement, Stamp> pair = getConstantObjectStamp(info, object); 371 if (pair == null) { 372 pair = recursiveFoldStamp(object, info); 373 } 374 if (pair != null) { 375 Stamp s = pair.second; 376 if (s instanceof GuardedConstantStamp) { 377 ConstantNode c = tryFoldFromLoadField(loadFieldNode, pair.second); 378 if (c != null) { 379 counterLFFolded.increment(); 380 if (c.stamp() instanceof ObjectStamp) { 381 GuardedConstantStamp cos = new GuardedConstantStamp(c.asJavaConstant(), (ObjectStamp) c.stamp()); 382 return new Pair<>(pair.first, cos); 383 } 384 return new Pair<>(pair.first, c.stamp()); 385 } 386 } 387 } 388 } 389 return null; 390 } 391 392 private ConstantNode tryFoldFromLoadField(LoadFieldNode lf, Stamp x) { 393 GuardedConstantStamp cos = (GuardedConstantStamp) x; 394 return lf.asConstant(tool, cos.objectConstant); 395 } 396 397 private Pair<InfoElement, Stamp> getConstantObjectStamp(InfoElementProvider infos, ValueNode n) { 398 for (InfoElement infoElement : infos.getInfoElements(n)) { 399 Stamp s = infoElement.getStamp(); 400 if (s instanceof GuardedConstantStamp) { 401 return new Pair<>(infoElement, s); 402 } 403 } 404 return null; 405 } 406 407 Pair<InfoElement, Stamp> recursiveFoldStamp(Node node, InfoElementProvider info) { 408 if (node instanceof LoadFieldNode) { 409 Pair<InfoElement, Stamp> pair = foldFromConstLoadField((LoadFieldNode) node, info); 410 if (pair != null) { 411 return pair; 412 } 413 } 414 if (node instanceof UnaryNode) { 415 UnaryNode unary = (UnaryNode) node; 416 ValueNode value = unary.getValue(); 417 for (InfoElement infoElement : info.getInfoElements(value)) { 418 Stamp result = unary.foldStamp(infoElement.getStamp()); 419 if (result != null) { 420 return new Pair<>(infoElement, result); 421 } 422 } 423 Pair<InfoElement, Stamp> foldResult = recursiveFoldStamp(value, info); 424 if (foldResult != null) { 425 Stamp result = unary.foldStamp(foldResult.second); 426 if (result != null) { 427 return new Pair<>(foldResult.first, result); 428 } 429 } 430 } else if (node instanceof BinaryNode) { 431 BinaryNode binary = (BinaryNode) node; 432 ValueNode y = binary.getY(); 433 ValueNode x = binary.getX(); 434 if (y.isConstant()) { 435 for (InfoElement infoElement : info.getInfoElements(x)) { 436 Stamp result = binary.foldStamp(infoElement.stamp, y.stamp()); 437 if (result != null) { 438 return new Pair<>(infoElement, result); 439 } 440 } 441 Pair<InfoElement, Stamp> foldResult = recursiveFoldStamp(x, info); 442 if (foldResult != null) { 443 Stamp result = binary.foldStamp(foldResult.second, y.stamp()); 444 if (result != null) { 445 return new Pair<>(foldResult.first, result); 446 } 447 } 448 } else if (x instanceof LoadFieldNode || y instanceof LoadFieldNode) { 449 boolean useX = x instanceof LoadFieldNode; 450 Pair<InfoElement, Stamp> foldResult = recursiveFoldStamp(useX ? x : y, info); 451 if (foldResult != null) { 452 Stamp result = binary.foldStamp(useX ? foldResult.second : x.stamp(), useX ? y.stamp() : foldResult.second); 453 if (result != null) { 454 return new Pair<>(foldResult.first, result); 455 } 456 } 457 } 458 } 459 return null; 460 } 461 462 /** 463 * Recursively try to fold stamps within this expression using information from 464 * {@link #getInfoElements(ValueNode)}. It's only safe to use constants and one 465 * {@link InfoElement} otherwise more than one guard would be required. 466 * 467 * @param node 468 * @return the pair of the @{link InfoElement} used and the stamp produced for the whole 469 * expression 470 */ 471 Pair<InfoElement, Stamp> recursiveFoldStampFromInfo(Node node) { 472 return recursiveFoldStamp(node, (value) -> getInfoElements(value)); 473 } 474 475 /** 476 * Recursively try to fold stamps within this expression using {@code newStamp} if the 477 * node {@code original} is encountered in the expression. It's only safe to use 478 * constants and the passed in stamp otherwise more than one guard would be required. 479 * 480 * @param node 481 * @param original 482 * @param newStamp 483 * @return the improved stamp or null is nothing could be done 484 */ 485 @SuppressWarnings("unchecked") 486 Stamp recursiveFoldStamp(Node node, ValueNode original, Stamp newStamp) { 487 Debug.log("Recursively fold stamp for node %s original %s stamp %s", node, original, newStamp); 488 InfoElement element = new InfoElement(newStamp, original); 489 Pair<InfoElement, Stamp> result = recursiveFoldStamp(node, (value) -> value == original ? Collections.singleton(element) : Collections.EMPTY_LIST); 490 if (result != null) { 491 return result.second; 492 } 493 return null; 494 } 495 496 protected boolean foldPendingTest(DeoptimizingGuard thisGuard, ValueNode original, Stamp newStamp, GuardRewirer rewireGuardFunction) { 497 for (PendingTest pending : pendingTests) { 498 TriState result = TriState.UNKNOWN; 499 if (pending.condition instanceof UnaryOpLogicNode) { 500 UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) pending.condition; 501 if (unaryLogicNode.getValue() == original) { 502 result = unaryLogicNode.tryFold(newStamp); 503 } 504 if (!result.isKnown()) { 505 Stamp foldResult = recursiveFoldStamp(unaryLogicNode.getValue(), original, newStamp); 506 if (foldResult != null) { 507 result = unaryLogicNode.tryFold(foldResult); 508 } 509 } 510 } else if (pending.condition instanceof BinaryOpLogicNode) { 511 BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) pending.condition; 512 ValueNode x = binaryOpLogicNode.getX(); 513 ValueNode y = binaryOpLogicNode.getY(); 514 if (binaryOpLogicNode.getX() == original) { 515 result = binaryOpLogicNode.tryFold(newStamp, binaryOpLogicNode.getY().stamp()); 516 } else if (binaryOpLogicNode instanceof IntegerEqualsNode && y.isConstant() && x instanceof AndNode) { 517 AndNode and = (AndNode) x; 518 if (and.getY() == y && and.getX() == original) { 519 BinaryOp<And> andOp = ArithmeticOpTable.forStamp(newStamp).getAnd(); 520 result = binaryOpLogicNode.tryFold(andOp.foldStamp(newStamp, y.stamp()), y.stamp()); 521 } 522 } 523 if (!result.isKnown() && y.isConstant()) { 524 Stamp foldResult = recursiveFoldStamp(x, original, newStamp); 525 if (foldResult != null) { 526 result = binaryOpLogicNode.tryFold(foldResult, y.stamp()); 527 } 528 } 529 } 530 if (result.isKnown()) { 531 /* 532 * The test case be folded using the information available but the test can 533 * only be moved up if we're sure there's no schedule dependence. For now 534 * limit it to the original node and constants. 535 */ 536 InputFilter v = new InputFilter(original); 537 thisGuard.getCondition().applyInputs(v); 538 if (v.ok && foldGuard(thisGuard, pending.guard, rewireGuardFunction)) { 539 return true; 540 } 541 } 542 } 543 return false; 544 } 545 546 protected boolean foldGuard(DeoptimizingGuard thisGuard, DeoptimizingGuard otherGuard, GuardRewirer rewireGuardFunction) { 547 if (otherGuard.getAction() == thisGuard.getAction() && otherGuard.getReason() == thisGuard.getReason() && otherGuard.getSpeculation() == thisGuard.getSpeculation()) { 548 LogicNode condition = (LogicNode) thisGuard.getCondition().copyWithInputs(); 549 GuardRewirer rewirer = (guard, result) -> { 550 if (rewireGuardFunction.rewire(guard, result)) { 551 otherGuard.setCondition(condition, thisGuard.isNegated()); 552 return true; 553 } 554 condition.safeDelete(); 555 return false; 556 }; 557 // Move the later test up 558 return rewireGuards(otherGuard.asNode(), !thisGuard.isNegated(), rewirer); 559 } 560 return false; 561 } 562 563 protected void registerCondition(LogicNode condition, boolean negated, ValueNode guard) { 564 registerNewStamp(condition, negated ? StampFactory.contradiction() : StampFactory.tautology(), guard); 565 } 566 567 protected Iterable<InfoElement> getInfoElements(ValueNode proxiedValue) { 568 ValueNode value = GraphUtil.unproxify(proxiedValue); 569 if (value == null) { 570 return Collections.emptyList(); 571 } 572 Info info = map.get(value); 573 if (info == null) { 574 return Collections.emptyList(); 575 } else { 576 return info.getElements(); 577 } 578 } 579 580 protected boolean rewireGuards(ValueNode guard, boolean result, GuardRewirer rewireGuardFunction) { 581 assert guard instanceof GuardingNode; 582 counterStampsFound.increment(); 583 ValueNode proxiedGuard = proxyGuard(guard); 584 return rewireGuardFunction.rewire(proxiedGuard, result); 585 } 586 587 protected ValueNode proxyGuard(ValueNode guard) { 588 ValueNode proxiedGuard = guard; 589 if (!Instance.this.loopExits.isEmpty()) { 590 while (proxiedGuard instanceof GuardProxyNode) { 591 proxiedGuard = ((GuardProxyNode) proxiedGuard).value(); 592 } 593 Block guardBlock = nodeToBlock.apply(proxiedGuard); 594 assert guardBlock != null; 595 for (Iterator<LoopExitNode> iter = loopExits.descendingIterator(); iter.hasNext();) { 596 LoopExitNode loopExitNode = iter.next(); 597 Block loopExitBlock = nodeToBlock.apply(loopExitNode); 598 if (guardBlock != loopExitBlock && AbstractControlFlowGraph.dominates(guardBlock, loopExitBlock)) { 599 Block loopBeginBlock = nodeToBlock.apply(loopExitNode.loopBegin()); 600 if (!AbstractControlFlowGraph.dominates(guardBlock, loopBeginBlock) || guardBlock == loopBeginBlock) { 601 proxiedGuard = proxiedGuard.graph().unique(new GuardProxyNode((GuardingNode) proxiedGuard, loopExitNode)); 602 } 603 } 604 } 605 } 606 return proxiedGuard; 607 } 608 609 protected boolean tryProveCondition(LogicNode node, GuardRewirer rewireGuardFunction) { 610 return tryProveGuardCondition(null, node, rewireGuardFunction); 611 } 612 613 protected boolean tryProveGuardCondition(DeoptimizingGuard thisGuard, LogicNode node, GuardRewirer rewireGuardFunction) { 614 for (InfoElement infoElement : getInfoElements(node)) { 615 Stamp stamp = infoElement.getStamp(); 616 JavaConstant constant = (JavaConstant) stamp.asConstant(); 617 if (constant != null) { 618 return rewireGuards(infoElement.getGuard(), constant.asBoolean(), rewireGuardFunction); 619 } 620 } 621 if (node instanceof UnaryOpLogicNode) { 622 UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) node; 623 ValueNode value = unaryLogicNode.getValue(); 624 for (InfoElement infoElement : getInfoElements(value)) { 625 Stamp stamp = infoElement.getStamp(); 626 TriState result = unaryLogicNode.tryFold(stamp); 627 if (result.isKnown()) { 628 return rewireGuards(infoElement.getGuard(), result.toBoolean(), rewireGuardFunction); 629 } 630 } 631 Pair<InfoElement, Stamp> foldResult = recursiveFoldStampFromInfo(value); 632 if (foldResult != null) { 633 TriState result = unaryLogicNode.tryFold(foldResult.second); 634 if (result.isKnown()) { 635 return rewireGuards(foldResult.first.getGuard(), result.toBoolean(), rewireGuardFunction); 636 } 637 } 638 if (thisGuard != null) { 639 Stamp newStamp = unaryLogicNode.getSucceedingStampForValue(thisGuard.isNegated()); 640 if (newStamp != null && foldPendingTest(thisGuard, value, newStamp, rewireGuardFunction)) { 641 return true; 642 } 643 644 } 645 } else if (node instanceof BinaryOpLogicNode) { 646 BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) node; 647 for (InfoElement infoElement : getInfoElements(binaryOpLogicNode)) { 648 if (infoElement.getStamp().equals(StampFactory.contradiction())) { 649 return rewireGuards(infoElement.getGuard(), false, rewireGuardFunction); 650 } else if (infoElement.getStamp().equals(StampFactory.tautology())) { 651 return rewireGuards(infoElement.getGuard(), true, rewireGuardFunction); 652 } 653 } 654 655 ValueNode x = binaryOpLogicNode.getX(); 656 ValueNode y = binaryOpLogicNode.getY(); 657 for (InfoElement infoElement : getInfoElements(x)) { 658 TriState result = binaryOpLogicNode.tryFold(infoElement.getStamp(), y.stamp()); 659 if (result.isKnown()) { 660 return rewireGuards(infoElement.getGuard(), result.toBoolean(), rewireGuardFunction); 661 } 662 } 663 664 if (y.isConstant()) { 665 Pair<InfoElement, Stamp> foldResult = recursiveFoldStampFromInfo(x); 666 if (foldResult != null) { 667 TriState result = binaryOpLogicNode.tryFold(foldResult.second, y.stamp()); 668 if (result.isKnown()) { 669 return rewireGuards(foldResult.first.getGuard(), result.toBoolean(), rewireGuardFunction); 670 } 671 } 672 } else { 673 for (InfoElement infoElement : getInfoElements(y)) { 674 TriState result = binaryOpLogicNode.tryFold(x.stamp(), infoElement.getStamp()); 675 if (result.isKnown()) { 676 return rewireGuards(infoElement.getGuard(), result.toBoolean(), rewireGuardFunction); 677 } 678 } 679 } 680 681 /* 682 * For complex expressions involving constants, see if it's possible to fold the 683 * tests by using stamps one level up in the expression. For instance, (x + n < 684 * y) might fold if something is known about x and all other values are 685 * constants. The reason for the constant restriction is that if more than 1 686 * real value is involved the code might need to adopt multiple guards to have 687 * proper dependences. 688 */ 689 if (x instanceof BinaryArithmeticNode<?> && y.isConstant()) { 690 BinaryArithmeticNode<?> binary = (BinaryArithmeticNode<?>) x; 691 if (binary.getY().isConstant()) { 692 for (InfoElement infoElement : getInfoElements(binary.getX())) { 693 Stamp newStampX = binary.foldStamp(infoElement.getStamp(), binary.getY().stamp()); 694 TriState result = binaryOpLogicNode.tryFold(newStampX, y.stamp()); 695 if (result.isKnown()) { 696 return rewireGuards(infoElement.getGuard(), result.toBoolean(), rewireGuardFunction); 697 } 698 } 699 } 700 } 701 if (thisGuard != null && binaryOpLogicNode instanceof IntegerEqualsNode && !thisGuard.isNegated()) { 702 if (y.isConstant() && x instanceof AndNode) { 703 AndNode and = (AndNode) x; 704 if (and.getY() == y) { 705 /* 706 * This 'and' proves something about some of the bits in and.getX(). 707 * It's equivalent to or'ing in the mask value since those values 708 * are known to be set. 709 */ 710 BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp()).getOr(); 711 IntegerStamp newStampX = (IntegerStamp) op.foldStamp(and.getX().stamp(), y.stamp()); 712 if (foldPendingTest(thisGuard, and.getX(), newStampX, rewireGuardFunction)) { 713 return true; 714 } 715 } 716 } 717 } 718 if (thisGuard != null) { 719 if (!x.isConstant()) { 720 Stamp newStampX = binaryOpLogicNode.getSucceedingStampForX(thisGuard.isNegated()); 721 if (newStampX != null && foldPendingTest(thisGuard, x, newStampX, rewireGuardFunction)) { 722 return true; 723 } 724 } 725 if (!y.isConstant()) { 726 Stamp newStampY = binaryOpLogicNode.getSucceedingStampForY(thisGuard.isNegated()); 727 if (newStampY != null && foldPendingTest(thisGuard, y, newStampY, rewireGuardFunction)) { 728 return true; 729 } 730 } 731 } 732 } else if (node instanceof ShortCircuitOrNode) { 733 final ShortCircuitOrNode shortCircuitOrNode = (ShortCircuitOrNode) node; 734 if (Instance.this.loopExits.isEmpty()) { 735 return tryProveCondition(shortCircuitOrNode.getX(), (guard, result) -> { 736 if (result == !shortCircuitOrNode.isXNegated()) { 737 return rewireGuards(guard, true, rewireGuardFunction); 738 } else { 739 return tryProveCondition(shortCircuitOrNode.getY(), (innerGuard, innerResult) -> { 740 if (innerGuard == guard) { 741 return rewireGuards(guard, innerResult ^ shortCircuitOrNode.isYNegated(), rewireGuardFunction); 742 } 743 return false; 744 }); 745 } 746 }); 747 } 748 } 749 750 return false; 751 } 752 753 protected void registerNewStamp(ValueNode proxiedValue, Stamp newStamp, ValueNode guard) { 754 assert proxiedValue != null; 755 assert guard != null; 756 if (newStamp != null) { 757 ValueNode value = GraphUtil.unproxify(proxiedValue); 758 Info info = map.get(value); 759 if (info == null) { 760 info = new Info(); 761 map.set(value, info); 762 } 763 counterStampsRegistered.increment(); 764 final Info finalInfo = info; 765 Debug.log("\t Saving stamp for node %s stamp %s guarded by %s", value, newStamp, guard == null ? "null" : guard); 766 finalInfo.pushElement(new InfoElement(newStamp, guard)); 767 undoOperations.add(() -> finalInfo.popElement()); 768 } 769 } 770 771 protected void processAbstractBegin(AbstractBeginNode beginNode) { 772 Node predecessor = beginNode.predecessor(); 773 if (predecessor instanceof IfNode) { 774 IfNode ifNode = (IfNode) predecessor; 775 boolean negated = (ifNode.falseSuccessor() == beginNode); 776 LogicNode condition = ifNode.condition(); 777 registerNewCondition(condition, negated, beginNode); 778 } else if (predecessor instanceof TypeSwitchNode) { 779 TypeSwitchNode typeSwitch = (TypeSwitchNode) predecessor; 780 processTypeSwitch(beginNode, predecessor, typeSwitch); 781 } else if (predecessor instanceof IntegerSwitchNode) { 782 IntegerSwitchNode integerSwitchNode = (IntegerSwitchNode) predecessor; 783 processIntegerSwitch(beginNode, predecessor, integerSwitchNode); 784 } 785 } 786 787 protected void processIntegerSwitch(AbstractBeginNode beginNode, Node predecessor, IntegerSwitchNode integerSwitchNode) { 788 Stamp stamp = null; 789 for (int i = 0; i < integerSwitchNode.keyCount(); i++) { 790 if (integerSwitchNode.keySuccessor(i) == predecessor) { 791 if (stamp == null) { 792 stamp = StampFactory.forConstant(integerSwitchNode.keyAt(i)); 793 } else { 794 stamp = stamp.meet(StampFactory.forConstant(integerSwitchNode.keyAt(i))); 795 } 796 } 797 } 798 799 if (stamp != null) { 800 registerNewStamp(integerSwitchNode.value(), stamp, beginNode); 801 } 802 } 803 804 protected void processTypeSwitch(AbstractBeginNode beginNode, Node predecessor, TypeSwitchNode typeSwitch) { 805 ValueNode hub = typeSwitch.value(); 806 if (hub instanceof LoadHubNode) { 807 LoadHubNode loadHub = (LoadHubNode) hub; 808 Stamp stamp = null; 809 for (int i = 0; i < typeSwitch.keyCount(); i++) { 810 if (typeSwitch.keySuccessor(i) == predecessor) { 811 if (stamp == null) { 812 stamp = StampFactory.objectNonNull(TypeReference.createExactTrusted(typeSwitch.typeAt(i))); 813 } else { 814 stamp = stamp.meet(StampFactory.objectNonNull(TypeReference.createExactTrusted(typeSwitch.typeAt(i)))); 815 } 816 } 817 } 818 if (stamp != null) { 819 registerNewStamp(loadHub.getValue(), stamp, beginNode); 820 } 821 } 822 } 823 } 824 } 825 826 protected static class Pair<F, S> { 827 public final F first; 828 public final S second; 829 830 Pair(F first, S second) { 831 this.first = first; 832 this.second = second; 833 } 834 835 @Override 836 public int hashCode() { 837 return first.hashCode() * 31 ^ second.hashCode(); 838 } 839 840 @Override 841 public boolean equals(Object obj) { 842 if (obj instanceof Pair<?, ?>) { 843 Pair<?, ?> other = (Pair<?, ?>) obj; 844 return this.first.equals(other.first) && this.second.equals(other.second); 845 } 846 return false; 847 } 848 } 849 850 @FunctionalInterface 851 protected interface InfoElementProvider { 852 Iterable<InfoElement> getInfoElements(ValueNode value); 853 } 854 855 /** 856 * Checks for safe nodes when moving pending tests up. 857 */ 858 static class InputFilter extends Node.EdgeVisitor { 859 boolean ok; 860 private ValueNode value; 861 862 InputFilter(ValueNode value) { 863 this.value = value; 864 this.ok = true; 865 } 866 867 @Override 868 public Node apply(Node node, Node curNode) { 869 if (!(curNode instanceof ValueNode)) { 870 ok = false; 871 return curNode; 872 } 873 ValueNode curValue = (ValueNode) curNode; 874 if (curValue.isConstant() || curValue == value || curValue instanceof ParameterNode) { 875 return curNode; 876 } 877 if (curValue instanceof BinaryNode || curValue instanceof UnaryNode) { 878 curValue.applyInputs(this); 879 } else { 880 ok = false; 881 } 882 return curNode; 883 } 884 } 885 886 @FunctionalInterface 887 protected interface GuardRewirer { 888 /** 889 * Called if the condition could be proven to have a constant value ({@code result}) under 890 * {@code guard}. 891 * 892 * Return whether a transformation could be applied. 893 */ 894 boolean rewire(ValueNode guard, boolean result); 895 } 896 897 protected static class PendingTest { 898 private final LogicNode condition; 899 private final DeoptimizingGuard guard; 900 901 public PendingTest(LogicNode condition, DeoptimizingGuard guard) { 902 this.condition = condition; 903 this.guard = guard; 904 } 905 } 906 907 protected static final class InfoElement { 908 private final Stamp stamp; 909 private final ValueNode guard; 910 911 public InfoElement(Stamp stamp, ValueNode guard) { 912 this.stamp = stamp; 913 this.guard = guard; 914 } 915 916 public Stamp getStamp() { 917 return stamp; 918 } 919 920 public ValueNode getGuard() { 921 return guard; 922 } 923 924 @Override 925 public String toString() { 926 return stamp + " -> " + guard; 927 } 928 } 929 930 protected static final class Info { 931 private final ArrayList<InfoElement> infos; 932 933 public Info() { 934 infos = new ArrayList<>(); 935 } 936 937 public Iterable<InfoElement> getElements() { 938 return infos; 939 } 940 941 public void pushElement(InfoElement element) { 942 Debug.log(Debug.VERBOSE_LOG_LEVEL, "Pushing an info element:%s", element); 943 infos.add(element); 944 } 945 946 public void popElement() { 947 infos.remove(infos.size() - 1); 948 } 949 } 950 951 private static class GuardedConstantStamp extends ObjectStamp { 952 private final JavaConstant objectConstant; 953 954 GuardedConstantStamp(JavaConstant objectConstant, ObjectStamp succeedingStamp) { 955 super(succeedingStamp.type(), succeedingStamp.isExactType(), succeedingStamp.nonNull(), succeedingStamp.alwaysNull()); 956 this.objectConstant = objectConstant; 957 } 958 959 } 960 961 @Override 962 public float codeSizeIncrease() { 963 return 1.5f; 964 } 965 }