1 /*
   2  * Copyright (c) 2015, 2018, 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.phases.common;
  26 
  27 import static org.graalvm.compiler.nodes.StaticDeoptimizingNode.mergeActions;
  28 
  29 import java.util.ArrayDeque;
  30 import java.util.Deque;
  31 import java.util.List;
  32 
  33 import jdk.internal.vm.compiler.collections.EconomicMap;
  34 import jdk.internal.vm.compiler.collections.Equivalence;
  35 import jdk.internal.vm.compiler.collections.MapCursor;
  36 import jdk.internal.vm.compiler.collections.Pair;
  37 import org.graalvm.compiler.core.common.cfg.AbstractControlFlowGraph;
  38 import org.graalvm.compiler.core.common.cfg.BlockMap;
  39 import org.graalvm.compiler.core.common.type.ArithmeticOpTable;
  40 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp;
  41 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.And;
  42 import org.graalvm.compiler.core.common.type.ArithmeticOpTable.BinaryOp.Or;
  43 import org.graalvm.compiler.core.common.type.IntegerStamp;
  44 import org.graalvm.compiler.core.common.type.ObjectStamp;
  45 import org.graalvm.compiler.core.common.type.Stamp;
  46 import org.graalvm.compiler.core.common.type.StampFactory;
  47 import org.graalvm.compiler.debug.CounterKey;
  48 import org.graalvm.compiler.debug.DebugCloseable;
  49 import org.graalvm.compiler.debug.DebugContext;
  50 import org.graalvm.compiler.graph.Node;
  51 import org.graalvm.compiler.graph.NodeMap;
  52 import org.graalvm.compiler.graph.NodeStack;
  53 import org.graalvm.compiler.graph.spi.CanonicalizerTool;
  54 import org.graalvm.compiler.nodeinfo.InputType;
  55 import org.graalvm.compiler.nodes.AbstractBeginNode;
  56 import org.graalvm.compiler.nodes.AbstractMergeNode;
  57 import org.graalvm.compiler.nodes.BinaryOpLogicNode;
  58 import org.graalvm.compiler.nodes.ConditionAnchorNode;
  59 import org.graalvm.compiler.nodes.DeoptimizingGuard;
  60 import org.graalvm.compiler.nodes.EndNode;
  61 import org.graalvm.compiler.nodes.FixedGuardNode;
  62 import org.graalvm.compiler.nodes.FixedNode;
  63 import org.graalvm.compiler.nodes.FixedWithNextNode;
  64 import org.graalvm.compiler.nodes.GuardNode;
  65 import org.graalvm.compiler.nodes.IfNode;
  66 import org.graalvm.compiler.nodes.LogicConstantNode;
  67 import org.graalvm.compiler.nodes.LogicNode;
  68 import org.graalvm.compiler.nodes.LoopExitNode;
  69 import org.graalvm.compiler.nodes.MergeNode;
  70 import org.graalvm.compiler.nodes.NodeView;
  71 import org.graalvm.compiler.nodes.ParameterNode;
  72 import org.graalvm.compiler.nodes.PiNode;
  73 import org.graalvm.compiler.nodes.ProxyNode;
  74 import org.graalvm.compiler.nodes.ShortCircuitOrNode;
  75 import org.graalvm.compiler.nodes.StructuredGraph;
  76 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
  77 import org.graalvm.compiler.nodes.UnaryOpLogicNode;
  78 import org.graalvm.compiler.nodes.ValueNode;
  79 import org.graalvm.compiler.nodes.ValuePhiNode;
  80 import org.graalvm.compiler.nodes.calc.AndNode;
  81 import org.graalvm.compiler.nodes.calc.BinaryArithmeticNode;
  82 import org.graalvm.compiler.nodes.calc.BinaryNode;
  83 import org.graalvm.compiler.nodes.calc.IntegerEqualsNode;
  84 import org.graalvm.compiler.nodes.calc.UnaryNode;
  85 import org.graalvm.compiler.nodes.cfg.Block;
  86 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  87 import org.graalvm.compiler.nodes.extended.GuardingNode;
  88 import org.graalvm.compiler.nodes.extended.IntegerSwitchNode;
  89 import org.graalvm.compiler.nodes.extended.LoadHubNode;
  90 import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
  91 import org.graalvm.compiler.nodes.java.InstanceOfNode;
  92 import org.graalvm.compiler.nodes.java.TypeSwitchNode;
  93 import org.graalvm.compiler.nodes.spi.NodeWithState;
  94 import org.graalvm.compiler.nodes.spi.StampInverter;
  95 import org.graalvm.compiler.nodes.util.GraphUtil;
  96 import org.graalvm.compiler.phases.BasePhase;
  97 import org.graalvm.compiler.phases.schedule.SchedulePhase;
  98 import org.graalvm.compiler.phases.schedule.SchedulePhase.SchedulingStrategy;
  99 import org.graalvm.compiler.phases.tiers.PhaseContext;
 100 
 101 import jdk.vm.ci.meta.DeoptimizationAction;
 102 import jdk.vm.ci.meta.JavaConstant;
 103 import jdk.vm.ci.meta.SpeculationLog.Speculation;
 104 import jdk.vm.ci.meta.TriState;
 105 
 106 public class ConditionalEliminationPhase extends BasePhase<PhaseContext> {
 107 
 108     private static final CounterKey counterStampsRegistered = DebugContext.counter("StampsRegistered");
 109     private static final CounterKey counterStampsFound = DebugContext.counter("StampsFound");
 110     private static final CounterKey counterIfsKilled = DebugContext.counter("CE_KilledIfs");
 111     private static final CounterKey counterPhiStampsImproved = DebugContext.counter("CE_ImprovedPhis");
 112     private final boolean fullSchedule;
 113     private final boolean moveGuards;
 114 
 115     public ConditionalEliminationPhase(boolean fullSchedule) {
 116         this(fullSchedule, true);
 117     }
 118 
 119     public ConditionalEliminationPhase(boolean fullSchedule, boolean moveGuards) {
 120         this.fullSchedule = fullSchedule;
 121         this.moveGuards = moveGuards;
 122     }
 123 
 124     @Override
 125     @SuppressWarnings("try")
 126     protected void run(StructuredGraph graph, PhaseContext context) {
 127         try (DebugContext.Scope s = graph.getDebug().scope("DominatorConditionalElimination")) {
 128             BlockMap<List<Node>> blockToNodes = null;
 129             NodeMap<Block> nodeToBlock = null;
 130             ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, true, true);
 131             if (fullSchedule) {
 132                 if (moveGuards) {
 133                     cfg.visitDominatorTree(new MoveGuardsUpwards(), graph.hasValueProxies());
 134                 }
 135                 try (DebugContext.Scope scheduleScope = graph.getDebug().scope(SchedulePhase.class)) {
 136                     SchedulePhase.run(graph, SchedulingStrategy.EARLIEST_WITH_GUARD_ORDER, cfg);
 137                 } catch (Throwable t) {
 138                     throw graph.getDebug().handle(t);
 139                 }
 140                 ScheduleResult r = graph.getLastSchedule();
 141                 blockToNodes = r.getBlockToNodesMap();
 142                 nodeToBlock = r.getNodeToBlockMap();
 143             } else {
 144                 nodeToBlock = cfg.getNodeToBlock();
 145                 blockToNodes = getBlockToNodes(cfg);
 146             }
 147             ControlFlowGraph.RecursiveVisitor<?> visitor = createVisitor(graph, cfg, blockToNodes, nodeToBlock, context);
 148             cfg.visitDominatorTree(visitor, graph.hasValueProxies());
 149         }
 150     }
 151 
 152     protected BlockMap<List<Node>> getBlockToNodes(@SuppressWarnings("unused") ControlFlowGraph cfg) {
 153         return null;
 154     }
 155 
 156     protected ControlFlowGraph.RecursiveVisitor<?> createVisitor(StructuredGraph graph, @SuppressWarnings("unused") ControlFlowGraph cfg, BlockMap<List<Node>> blockToNodes,
 157                     NodeMap<Block> nodeToBlock, PhaseContext context) {
 158         return new Instance(graph, blockToNodes, nodeToBlock, context);
 159     }
 160 
 161     public static class MoveGuardsUpwards implements ControlFlowGraph.RecursiveVisitor<Block> {
 162 
 163         Block anchorBlock;
 164 
 165         @Override
 166         @SuppressWarnings("try")
 167         public Block enter(Block b) {
 168             Block oldAnchorBlock = anchorBlock;
 169             if (b.getDominator() == null || b.getDominator().getPostdominator() != b) {
 170                 // New anchor.
 171                 anchorBlock = b;
 172             }
 173 
 174             AbstractBeginNode beginNode = b.getBeginNode();
 175             if (beginNode instanceof AbstractMergeNode && anchorBlock != b) {
 176                 AbstractMergeNode mergeNode = (AbstractMergeNode) beginNode;
 177                 for (GuardNode guard : mergeNode.guards().snapshot()) {
 178                     try (DebugCloseable closeable = guard.withNodeSourcePosition()) {
 179                         GuardNode newlyCreatedGuard = new GuardNode(guard.getCondition(), anchorBlock.getBeginNode(), guard.getReason(), guard.getAction(), guard.isNegated(), guard.getSpeculation(),
 180                                         guard.getNoDeoptSuccessorPosition());
 181                         GuardNode newGuard = mergeNode.graph().unique(newlyCreatedGuard);
 182                         guard.replaceAndDelete(newGuard);
 183                     }
 184                 }
 185             }
 186 
 187             FixedNode endNode = b.getEndNode();
 188             if (endNode instanceof IfNode) {
 189                 IfNode node = (IfNode) endNode;
 190 
 191                 // Check if we can move guards upwards.
 192                 AbstractBeginNode trueSuccessor = node.trueSuccessor();
 193                 EconomicMap<LogicNode, GuardNode> trueGuards = EconomicMap.create(Equivalence.IDENTITY);
 194                 for (GuardNode guard : trueSuccessor.guards()) {
 195                     LogicNode condition = guard.getCondition();
 196                     if (condition.hasMoreThanOneUsage()) {
 197                         trueGuards.put(condition, guard);
 198                     }
 199                 }
 200 
 201                 if (!trueGuards.isEmpty()) {
 202                     for (GuardNode guard : node.falseSuccessor().guards().snapshot()) {
 203                         GuardNode otherGuard = trueGuards.get(guard.getCondition());
 204                         if (otherGuard != null && guard.isNegated() == otherGuard.isNegated()) {
 205                             Speculation speculation = otherGuard.getSpeculation();
 206                             if (speculation == null) {
 207                                 speculation = guard.getSpeculation();
 208                             } else if (guard.getSpeculation() != null && guard.getSpeculation() != speculation) {
 209                                 // Cannot optimize due to different speculations.
 210                                 continue;
 211                             }
 212                             try (DebugCloseable closeable = guard.withNodeSourcePosition()) {
 213                                 GuardNode newlyCreatedGuard = new GuardNode(guard.getCondition(), anchorBlock.getBeginNode(), guard.getReason(), guard.getAction(), guard.isNegated(), speculation,
 214                                                 guard.getNoDeoptSuccessorPosition());
 215                                 GuardNode newGuard = node.graph().unique(newlyCreatedGuard);
 216                                 if (otherGuard.isAlive()) {
 217                                     otherGuard.replaceAndDelete(newGuard);
 218                                 }
 219                                 guard.replaceAndDelete(newGuard);
 220                             }
 221                         }
 222                     }
 223                 }
 224             }
 225             return oldAnchorBlock;
 226         }
 227 
 228         @Override
 229         public void exit(Block b, Block value) {
 230             anchorBlock = value;
 231         }
 232 
 233     }
 234 
 235     private static final class PhiInfoElement {
 236 
 237         private EconomicMap<EndNode, InfoElement> infoElements;
 238 
 239         public void set(EndNode end, InfoElement infoElement) {
 240             if (infoElements == null) {
 241                 infoElements = EconomicMap.create(Equivalence.IDENTITY);
 242             }
 243             infoElements.put(end, infoElement);
 244         }
 245 
 246         public InfoElement get(EndNode end) {
 247             if (infoElements == null) {
 248                 return null;
 249             }
 250             return infoElements.get(end);
 251         }
 252     }
 253 
 254     public static final class Marks {
 255         final int infoElementOperations;
 256         final int conditions;
 257 
 258         public Marks(int infoElementOperations, int conditions) {
 259             this.infoElementOperations = infoElementOperations;
 260             this.conditions = conditions;
 261         }
 262     }
 263 
 264     protected static final class GuardedCondition {
 265         private final GuardingNode guard;
 266         private final LogicNode condition;
 267         private final boolean negated;
 268 
 269         public GuardedCondition(GuardingNode guard, LogicNode condition, boolean negated) {
 270             this.guard = guard;
 271             this.condition = condition;
 272             this.negated = negated;
 273         }
 274 
 275         public GuardingNode getGuard() {
 276             return guard;
 277         }
 278 
 279         public LogicNode getCondition() {
 280             return condition;
 281         }
 282 
 283         public boolean isNegated() {
 284             return negated;
 285         }
 286     }
 287 
 288     public static class Instance implements ControlFlowGraph.RecursiveVisitor<Marks> {
 289         protected final NodeMap<InfoElement> map;
 290         protected final BlockMap<List<Node>> blockToNodes;
 291         protected final NodeMap<Block> nodeToBlock;
 292         protected final CanonicalizerTool tool;
 293         protected final NodeStack undoOperations;
 294         protected final StructuredGraph graph;
 295         protected final DebugContext debug;
 296         protected final EconomicMap<MergeNode, EconomicMap<ValuePhiNode, PhiInfoElement>> mergeMaps;
 297 
 298         protected final ArrayDeque<GuardedCondition> conditions;
 299 
 300         /**
 301          * Tests which may be eliminated because post dominating tests to prove a broader condition.
 302          */
 303         private Deque<DeoptimizingGuard> pendingTests;
 304 
 305         public Instance(StructuredGraph graph, BlockMap<List<Node>> blockToNodes, NodeMap<Block> nodeToBlock, PhaseContext context) {
 306             this.graph = graph;
 307             this.debug = graph.getDebug();
 308             this.blockToNodes = blockToNodes;
 309             this.nodeToBlock = nodeToBlock;
 310             this.undoOperations = new NodeStack();
 311             this.map = graph.createNodeMap();
 312             this.pendingTests = new ArrayDeque<>();
 313             this.conditions = new ArrayDeque<>();
 314             tool = GraphUtil.getDefaultSimplifier(context.getMetaAccess(), context.getConstantReflection(), context.getConstantFieldProvider(), false, graph.getAssumptions(), graph.getOptions(),
 315                             context.getLowerer());
 316             mergeMaps = EconomicMap.create();
 317         }
 318 
 319         protected void processConditionAnchor(ConditionAnchorNode node) {
 320             tryProveCondition(node.condition(), (guard, result, guardedValueStamp, newInput) -> {
 321                 if (result != node.isNegated()) {
 322                     node.replaceAtUsages(guard.asNode());
 323                     GraphUtil.unlinkFixedNode(node);
 324                     GraphUtil.killWithUnusedFloatingInputs(node);
 325                 } else {
 326                     ValueAnchorNode valueAnchor = node.graph().add(new ValueAnchorNode(null));
 327                     node.replaceAtUsages(valueAnchor);
 328                     node.graph().replaceFixedWithFixed(node, valueAnchor);
 329                 }
 330                 return true;
 331             });
 332         }
 333 
 334         protected void processGuard(GuardNode node) {
 335             if (!tryProveGuardCondition(node, node.getCondition(), (guard, result, guardedValueStamp, newInput) -> {
 336                 if (result != node.isNegated()) {
 337                     node.replaceAndDelete(guard.asNode());
 338                 } else {
 339                     /*
 340                      * Don't kill this branch immediately because `killCFG` can have complex
 341                      * implications in the presence of loops: it might replace or delete nodes in
 342                      * other branches or even above the kill point. Instead of killing immediately,
 343                      * just leave the graph in a state that is easy to simplify by a subsequent
 344                      * canonicalizer phase.
 345                      */
 346                     FixedGuardNode deopt = new FixedGuardNode(LogicConstantNode.forBoolean(result, node.graph()), node.getReason(), node.getAction(), node.getSpeculation(), node.isNegated(),
 347                                     node.getNodeSourcePosition());
 348                     AbstractBeginNode beginNode = (AbstractBeginNode) node.getAnchor();
 349                     graph.addAfterFixed(beginNode, node.graph().add(deopt));
 350 
 351                 }
 352                 return true;
 353             })) {
 354                 registerNewCondition(node.getCondition(), node.isNegated(), node);
 355             }
 356         }
 357 
 358         protected void processFixedGuard(FixedGuardNode node) {
 359             if (!tryProveGuardCondition(node, node.condition(), (guard, result, guardedValueStamp, newInput) -> {
 360                 if (result != node.isNegated()) {
 361                     node.replaceAtUsages(guard.asNode());
 362                     GraphUtil.unlinkFixedNode(node);
 363                     GraphUtil.killWithUnusedFloatingInputs(node);
 364                 } else {
 365                     node.setCondition(LogicConstantNode.forBoolean(result, node.graph()), node.isNegated());
 366                     // Don't kill this branch immediately, see `processGuard`.
 367                 }
 368 
 369                 if (guard instanceof DeoptimizingGuard && !node.isNegated() && !((DeoptimizingGuard) guard).isNegated()) {
 370                     LogicNode newCondition = ((DeoptimizingGuard) guard.asNode()).getCondition();
 371                     if (newCondition instanceof InstanceOfNode) {
 372                         InstanceOfNode inst = (InstanceOfNode) newCondition;
 373                         ValueNode originalValue = GraphUtil.skipPi(inst.getValue());
 374                         PiNode pi = null;
 375                         // Ensure that any Pi that's weaker than what the instanceof proves is
 376                         // replaced by one derived from the instanceof itself.
 377                         for (PiNode existing : guard.asNode().usages().filter(PiNode.class).snapshot()) {
 378                             if (!existing.isAlive()) {
 379                                 continue;
 380                             }
 381                             if (originalValue != GraphUtil.skipPi(existing.object())) {
 382                                 // Somehow these are unrelated values so leave it alone
 383                                 continue;
 384                             }
 385                             // If the pi has a weaker stamp or the same stamp but a different input
 386                             // then replace it.
 387                             boolean strongerStamp = !existing.piStamp().join(inst.getCheckedStamp()).equals(inst.getCheckedStamp());
 388                             boolean differentStamp = !existing.piStamp().equals(inst.getCheckedStamp());
 389                             boolean differentObject = existing.object() != inst.getValue();
 390                             if (!strongerStamp && (differentStamp || differentObject)) {
 391                                 if (pi == null) {
 392                                     pi = graph.unique(new PiNode(inst.getValue(), inst.getCheckedStamp(), (ValueNode) guard));
 393                                 }
 394                                 existing.replaceAndDelete(pi);
 395                             }
 396                         }
 397                     }
 398                 }
 399                 debug.log("Kill fixed guard %s", node);
 400                 return true;
 401             })) {
 402                 registerNewCondition(node.condition(), node.isNegated(), node);
 403             }
 404         }
 405 
 406         protected void processIf(IfNode node) {
 407             tryProveCondition(node.condition(), (guard, result, guardedValueStamp, newInput) -> {
 408                 node.setCondition(LogicConstantNode.forBoolean(result, node.graph()));
 409                 AbstractBeginNode survivingSuccessor = node.getSuccessor(result);
 410                 survivingSuccessor.replaceAtUsages(InputType.Guard, guard.asNode());
 411                 // Don't kill the other branch immediately, see `processGuard`.
 412                 counterIfsKilled.increment(debug);
 413                 return true;
 414             });
 415         }
 416 
 417         @Override
 418         public Marks enter(Block block) {
 419             int infoElementsMark = undoOperations.size();
 420             int conditionsMark = conditions.size();
 421             debug.log("[Pre Processing block %s]", block);
 422             // For now conservatively collect guards only within the same block.
 423             pendingTests.clear();
 424             processNodes(block);
 425             return new Marks(infoElementsMark, conditionsMark);
 426         }
 427 
 428         protected void processNodes(Block block) {
 429             if (blockToNodes != null) {
 430                 for (Node n : blockToNodes.get(block)) {
 431                     if (n.isAlive()) {
 432                         processNode(n);
 433                     }
 434                 }
 435             } else {
 436                 processBlock(block);
 437             }
 438         }
 439 
 440         private void processBlock(Block block) {
 441             FixedNode n = block.getBeginNode();
 442             FixedNode endNode = block.getEndNode();
 443             debug.log("[Processing block %s]", block);
 444             while (n != endNode) {
 445                 if (n.isDeleted() || endNode.isDeleted()) {
 446                     // This branch was deleted!
 447                     return;
 448                 }
 449                 FixedNode next = ((FixedWithNextNode) n).next();
 450                 processNode(n);
 451                 n = next;
 452             }
 453             if (endNode.isAlive()) {
 454                 processNode(endNode);
 455             }
 456         }
 457 
 458         @SuppressWarnings("try")
 459         protected void processNode(Node node) {
 460             try (DebugCloseable closeable = node.withNodeSourcePosition()) {
 461                 if (node instanceof NodeWithState && !(node instanceof GuardingNode)) {
 462                     pendingTests.clear();
 463                 }
 464 
 465                 if (node instanceof MergeNode) {
 466                     introducePisForPhis((MergeNode) node);
 467                 }
 468 
 469                 if (node instanceof AbstractBeginNode) {
 470                     if (node instanceof LoopExitNode && graph.hasValueProxies()) {
 471                         // Condition must not be used down this path.
 472                         return;
 473                     }
 474                     processAbstractBegin((AbstractBeginNode) node);
 475                 } else if (node instanceof FixedGuardNode) {
 476                     processFixedGuard((FixedGuardNode) node);
 477                 } else if (node instanceof GuardNode) {
 478                     processGuard((GuardNode) node);
 479                 } else if (node instanceof ConditionAnchorNode) {
 480                     processConditionAnchor((ConditionAnchorNode) node);
 481                 } else if (node instanceof IfNode) {
 482                     processIf((IfNode) node);
 483                 } else if (node instanceof EndNode) {
 484                     processEnd((EndNode) node);
 485                 }
 486             }
 487         }
 488 
 489         protected void introducePisForPhis(MergeNode merge) {
 490             EconomicMap<ValuePhiNode, PhiInfoElement> mergeMap = this.mergeMaps.get(merge);
 491             if (mergeMap != null) {
 492                 MapCursor<ValuePhiNode, PhiInfoElement> entries = mergeMap.getEntries();
 493                 while (entries.advance()) {
 494                     ValuePhiNode phi = entries.getKey();
 495                     assert phi.isAlive() || phi.isDeleted();
 496                     /*
 497                      * Phi might have been killed already via a conditional elimination in another
 498                      * branch.
 499                      */
 500                     if (phi.isDeleted()) {
 501                         continue;
 502                     }
 503                     PhiInfoElement phiInfoElements = entries.getValue();
 504                     Stamp bestPossibleStamp = null;
 505                     for (int i = 0; i < phi.valueCount(); ++i) {
 506                         ValueNode valueAt = phi.valueAt(i);
 507                         Stamp curBestStamp = valueAt.stamp(NodeView.DEFAULT);
 508                         InfoElement infoElement = phiInfoElements.get(merge.forwardEndAt(i));
 509                         if (infoElement != null) {
 510                             curBestStamp = curBestStamp.join(infoElement.getStamp());
 511                         }
 512 
 513                         if (bestPossibleStamp == null) {
 514                             bestPossibleStamp = curBestStamp;
 515                         } else {
 516                             bestPossibleStamp = bestPossibleStamp.meet(curBestStamp);
 517                         }
 518                     }
 519 
 520                     Stamp oldStamp = phi.stamp(NodeView.DEFAULT);
 521                     if (oldStamp.tryImproveWith(bestPossibleStamp) != null) {
 522 
 523                         // Need to be careful to not run into stamp update cycles with the iterative
 524                         // canonicalization.
 525                         boolean allow = false;
 526                         if (bestPossibleStamp instanceof ObjectStamp) {
 527                             // Always allow object stamps.
 528                             allow = true;
 529                         } else if (bestPossibleStamp instanceof IntegerStamp) {
 530                             IntegerStamp integerStamp = (IntegerStamp) bestPossibleStamp;
 531                             IntegerStamp oldIntegerStamp = (IntegerStamp) oldStamp;
 532                             if (integerStamp.isPositive() != oldIntegerStamp.isPositive()) {
 533                                 allow = true;
 534                             } else if (integerStamp.isNegative() != oldIntegerStamp.isNegative()) {
 535                                 allow = true;
 536                             } else if (integerStamp.isStrictlyPositive() != oldIntegerStamp.isStrictlyPositive()) {
 537                                 allow = true;
 538                             } else if (integerStamp.isStrictlyNegative() != oldIntegerStamp.isStrictlyNegative()) {
 539                                 allow = true;
 540                             } else if (integerStamp.asConstant() != null) {
 541                                 allow = true;
 542                             } else if (oldStamp.isUnrestricted()) {
 543                                 allow = true;
 544                             }
 545                         } else {
 546                             // Fortify: Suppress Null Dereference false positive
 547                             assert bestPossibleStamp != null;
 548                             allow = (bestPossibleStamp.asConstant() != null);
 549                         }
 550 
 551                         if (allow) {
 552                             ValuePhiNode newPhi = graph.addWithoutUnique(new ValuePhiNode(bestPossibleStamp, merge));
 553                             for (int i = 0; i < phi.valueCount(); ++i) {
 554                                 ValueNode valueAt = phi.valueAt(i);
 555                                 if (bestPossibleStamp.meet(valueAt.stamp(NodeView.DEFAULT)).equals(bestPossibleStamp)) {
 556                                     // Pi not required here.
 557                                 } else {
 558                                     InfoElement infoElement = phiInfoElements.get(merge.forwardEndAt(i));
 559                                     assert infoElement != null;
 560                                     Stamp curBestStamp = infoElement.getStamp();
 561                                     ValueNode input = infoElement.getProxifiedInput();
 562                                     if (input == null) {
 563                                         input = valueAt;
 564                                     }
 565                                     valueAt = graph.maybeAddOrUnique(PiNode.create(input, curBestStamp, (ValueNode) infoElement.guard));
 566                                 }
 567                                 newPhi.addInput(valueAt);
 568                             }
 569                             counterPhiStampsImproved.increment(debug);
 570                             phi.replaceAtUsagesAndDelete(newPhi);
 571                         }
 572                     }
 573                 }
 574             }
 575         }
 576 
 577         protected void processEnd(EndNode end) {
 578             AbstractMergeNode abstractMerge = end.merge();
 579             if (abstractMerge instanceof MergeNode) {
 580                 MergeNode merge = (MergeNode) abstractMerge;
 581 
 582                 EconomicMap<ValuePhiNode, PhiInfoElement> mergeMap = this.mergeMaps.get(merge);
 583                 for (ValuePhiNode phi : merge.valuePhis()) {
 584                     ValueNode valueAt = phi.valueAt(end);
 585                     InfoElement infoElement = this.getInfoElements(valueAt);
 586                     while (infoElement != null) {
 587                         Stamp newStamp = infoElement.getStamp();
 588                         if (phi.stamp(NodeView.DEFAULT).tryImproveWith(newStamp) != null) {
 589                             if (mergeMap == null) {
 590                                 mergeMap = EconomicMap.create();
 591                                 mergeMaps.put(merge, mergeMap);
 592                             }
 593 
 594                             PhiInfoElement phiInfoElement = mergeMap.get(phi);
 595                             if (phiInfoElement == null) {
 596                                 phiInfoElement = new PhiInfoElement();
 597                                 mergeMap.put(phi, phiInfoElement);
 598                             }
 599 
 600                             phiInfoElement.set(end, infoElement);
 601                             break;
 602                         }
 603                         infoElement = nextElement(infoElement);
 604                     }
 605                 }
 606             }
 607         }
 608 
 609         protected void registerNewCondition(LogicNode condition, boolean negated, GuardingNode guard) {
 610             if (condition instanceof UnaryOpLogicNode) {
 611                 UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) condition;
 612                 ValueNode value = unaryLogicNode.getValue();
 613                 if (maybeMultipleUsages(value)) {
 614                     // getSucceedingStampForValue doesn't take the (potentially a Pi Node) input
 615                     // stamp into account, so it can be safely propagated.
 616                     Stamp newStamp = unaryLogicNode.getSucceedingStampForValue(negated);
 617                     registerNewStamp(value, newStamp, guard, true);
 618                 }
 619             } else if (condition instanceof BinaryOpLogicNode) {
 620                 BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) condition;
 621                 ValueNode x = binaryOpLogicNode.getX();
 622                 ValueNode y = binaryOpLogicNode.getY();
 623                 if (!x.isConstant() && maybeMultipleUsages(x)) {
 624                     Stamp newStampX = binaryOpLogicNode.getSucceedingStampForX(negated, getSafeStamp(x), getOtherSafeStamp(y));
 625                     registerNewStamp(x, newStampX, guard);
 626                 }
 627 
 628                 if (!y.isConstant() && maybeMultipleUsages(y)) {
 629                     Stamp newStampY = binaryOpLogicNode.getSucceedingStampForY(negated, getOtherSafeStamp(x), getSafeStamp(y));
 630                     registerNewStamp(y, newStampY, guard);
 631                 }
 632 
 633                 if (condition instanceof IntegerEqualsNode && guard instanceof DeoptimizingGuard && !negated) {
 634                     if (y.isConstant() && x instanceof AndNode) {
 635                         AndNode and = (AndNode) x;
 636                         ValueNode andX = and.getX();
 637                         if (and.getY() == y && maybeMultipleUsages(andX)) {
 638                             /*
 639                              * This 'and' proves something about some of the bits in and.getX().
 640                              * It's equivalent to or'ing in the mask value since those values are
 641                              * known to be set.
 642                              */
 643                             BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp(NodeView.DEFAULT)).getOr();
 644                             IntegerStamp newStampX = (IntegerStamp) op.foldStamp(getSafeStamp(andX), getOtherSafeStamp(y));
 645                             registerNewStamp(andX, newStampX, guard);
 646                         }
 647                     }
 648                 }
 649             }
 650             if (guard instanceof DeoptimizingGuard) {
 651                 assert ((DeoptimizingGuard) guard).getCondition() == condition;
 652                 pendingTests.push((DeoptimizingGuard) guard);
 653             }
 654             registerCondition(condition, negated, guard);
 655         }
 656 
 657         Pair<InfoElement, Stamp> recursiveFoldStamp(Node node) {
 658             if (node instanceof UnaryNode) {
 659                 UnaryNode unary = (UnaryNode) node;
 660                 ValueNode value = unary.getValue();
 661                 InfoElement infoElement = getInfoElements(value);
 662                 while (infoElement != null) {
 663                     Stamp result = unary.foldStamp(infoElement.getStamp());
 664                     if (result != null) {
 665                         return Pair.create(infoElement, result);
 666                     }
 667                     infoElement = nextElement(infoElement);
 668                 }
 669             } else if (node instanceof BinaryNode) {
 670                 BinaryNode binary = (BinaryNode) node;
 671                 ValueNode y = binary.getY();
 672                 ValueNode x = binary.getX();
 673                 if (y.isConstant()) {
 674                     InfoElement infoElement = getInfoElements(x);
 675                     while (infoElement != null) {
 676                         Stamp result = binary.foldStamp(infoElement.stamp, y.stamp(NodeView.DEFAULT));
 677                         if (result != null) {
 678                             return Pair.create(infoElement, result);
 679                         }
 680                         infoElement = nextElement(infoElement);
 681                     }
 682                 }
 683             }
 684             return null;
 685         }
 686 
 687         /**
 688          * Get the stamp that may be used for the value for which we are registering the condition.
 689          * We may directly use the stamp here without restriction, because any later lookup of the
 690          * registered info elements is in the same chain of pi nodes.
 691          */
 692         private static Stamp getSafeStamp(ValueNode x) {
 693             return x.stamp(NodeView.DEFAULT);
 694         }
 695 
 696         /**
 697          * We can only use the stamp of a second value involved in the condition if we are sure that
 698          * we are not implicitly creating a dependency on a pi node that is responsible for that
 699          * stamp. For now, we are conservatively only using the stamps of constants. Under certain
 700          * circumstances, we may also be able to use the stamp of the value after skipping pi nodes
 701          * (e.g., the stamp of a parameter after inlining, or the stamp of a fixed node that can
 702          * never be replaced with a pi node via canonicalization).
 703          */
 704         private static Stamp getOtherSafeStamp(ValueNode x) {
 705             if (x.isConstant() || x.graph().isAfterFixedReadPhase()) {
 706                 return x.stamp(NodeView.DEFAULT);
 707             }
 708             return x.stamp(NodeView.DEFAULT).unrestricted();
 709         }
 710 
 711         /**
 712          * Recursively try to fold stamps within this expression using information from
 713          * {@link #getInfoElements(ValueNode)}. It's only safe to use constants and one
 714          * {@link InfoElement} otherwise more than one guard would be required.
 715          *
 716          * @param node
 717          * @return the pair of the @{link InfoElement} used and the stamp produced for the whole
 718          *         expression
 719          */
 720         Pair<InfoElement, Stamp> recursiveFoldStampFromInfo(Node node) {
 721             return recursiveFoldStamp(node);
 722         }
 723 
 724         /**
 725          * Look for a preceding guard whose condition is implied by {@code thisGuard}. If we find
 726          * one, try to move this guard just above that preceding guard so that we can fold it:
 727          *
 728          * <pre>
 729          *     guard(C1); // preceding guard
 730          *     ...
 731          *     guard(C2); // thisGuard
 732          * </pre>
 733          *
 734          * If C2 => C1, transform to:
 735          *
 736          * <pre>
 737          *     guard(C2);
 738          *     ...
 739          * </pre>
 740          */
 741         protected boolean foldPendingTest(DeoptimizingGuard thisGuard, ValueNode original, Stamp newStamp, GuardRewirer rewireGuardFunction) {
 742             for (DeoptimizingGuard pendingGuard : pendingTests) {
 743                 LogicNode pendingCondition = pendingGuard.getCondition();
 744                 TriState result = TriState.UNKNOWN;
 745                 if (pendingCondition instanceof UnaryOpLogicNode) {
 746                     UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) pendingCondition;
 747                     if (unaryLogicNode.getValue() == original) {
 748                         result = unaryLogicNode.tryFold(newStamp);
 749                     }
 750                 } else if (pendingCondition instanceof BinaryOpLogicNode) {
 751                     BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) pendingCondition;
 752                     ValueNode x = binaryOpLogicNode.getX();
 753                     ValueNode y = binaryOpLogicNode.getY();
 754                     if (x == original) {
 755                         result = binaryOpLogicNode.tryFold(newStamp, getOtherSafeStamp(y));
 756                     } else if (y == original) {
 757                         result = binaryOpLogicNode.tryFold(getOtherSafeStamp(x), newStamp);
 758                     } else if (binaryOpLogicNode instanceof IntegerEqualsNode && y.isConstant() && x instanceof AndNode) {
 759                         AndNode and = (AndNode) x;
 760                         if (and.getY() == y && and.getX() == original) {
 761                             BinaryOp<And> andOp = ArithmeticOpTable.forStamp(newStamp).getAnd();
 762                             result = binaryOpLogicNode.tryFold(andOp.foldStamp(newStamp, getOtherSafeStamp(y)), getOtherSafeStamp(y));
 763                         }
 764                     }
 765                 }
 766                 if (result.isKnown()) {
 767                     /*
 768                      * The test case be folded using the information available but the test can only
 769                      * be moved up if we're sure there's no schedule dependence.
 770                      */
 771                     if (canScheduleAbove(thisGuard.getCondition(), pendingGuard.asNode(), original) && foldGuard(thisGuard, pendingGuard, result.toBoolean(), newStamp, rewireGuardFunction)) {
 772                         return true;
 773                     }
 774                 }
 775             }
 776             return false;
 777         }
 778 
 779         private boolean canScheduleAbove(Node n, Node target, ValueNode knownToBeAbove) {
 780             Block targetBlock = nodeToBlock.get(target);
 781             Block testBlock = nodeToBlock.get(n);
 782             if (targetBlock != null && testBlock != null) {
 783                 if (targetBlock == testBlock) {
 784                     for (Node fixed : blockToNodes.get(targetBlock)) {
 785                         if (fixed == n) {
 786                             return true;
 787                         } else if (fixed == target) {
 788                             break;
 789                         }
 790                     }
 791                 } else if (AbstractControlFlowGraph.dominates(testBlock, targetBlock)) {
 792                     return true;
 793                 }
 794             }
 795             InputFilter v = new InputFilter(knownToBeAbove);
 796             n.applyInputs(v);
 797             return v.ok;
 798         }
 799 
 800         protected boolean foldGuard(DeoptimizingGuard thisGuard, DeoptimizingGuard otherGuard, boolean outcome, Stamp guardedValueStamp, GuardRewirer rewireGuardFunction) {
 801             DeoptimizationAction action = mergeActions(otherGuard.getAction(), thisGuard.getAction());
 802             if (action != null && otherGuard.getSpeculation() == thisGuard.getSpeculation()) {
 803                 LogicNode condition = (LogicNode) thisGuard.getCondition().copyWithInputs();
 804                 /*
 805                  * We have ...; guard(C1); guard(C2);...
 806                  *
 807                  * Where the first guard is `otherGuard` and the second one `thisGuard`.
 808                  *
 809                  * Depending on `outcome`, we have C2 => C1 or C2 => !C1.
 810                  *
 811                  * - If C2 => C1, `mustDeopt` below is false and we transform to ...; guard(C2); ...
 812                  *
 813                  * - If C2 => !C1, `mustDeopt` is true and we transform to ..; guard(C1); deopt;
 814                  */
 815                 // for the second case, the action of the deopt is copied from there:
 816                 thisGuard.setAction(action);
 817                 GuardRewirer rewirer = (guard, result, innerGuardedValueStamp, newInput) -> {
 818                     // `result` is `outcome`, `guard` is `otherGuard`
 819                     boolean mustDeopt = result == otherGuard.isNegated();
 820                     if (rewireGuardFunction.rewire(guard, mustDeopt == thisGuard.isNegated(), innerGuardedValueStamp, newInput)) {
 821                         if (!mustDeopt) {
 822                             otherGuard.setCondition(condition, thisGuard.isNegated());
 823                             otherGuard.setAction(action);
 824                             otherGuard.setReason(thisGuard.getReason());
 825                         }
 826                         return true;
 827                     }
 828                     condition.safeDelete();
 829                     return false;
 830                 };
 831                 // Move the later test up
 832                 return rewireGuards(otherGuard, outcome, null, guardedValueStamp, rewirer);
 833             }
 834             return false;
 835         }
 836 
 837         protected void registerCondition(LogicNode condition, boolean negated, GuardingNode guard) {
 838             if (condition.hasMoreThanOneUsage()) {
 839                 registerNewStamp(condition, negated ? StampFactory.contradiction() : StampFactory.tautology(), guard);
 840             }
 841             conditions.push(new GuardedCondition(guard, condition, negated));
 842         }
 843 
 844         protected InfoElement getInfoElements(ValueNode proxiedValue) {
 845             if (proxiedValue == null) {
 846                 return null;
 847             }
 848             InfoElement infoElement = map.getAndGrow(proxiedValue);
 849             if (infoElement == null) {
 850                 infoElement = map.getAndGrow(GraphUtil.skipPi(proxiedValue));
 851             }
 852             return infoElement;
 853         }
 854 
 855         protected boolean rewireGuards(GuardingNode guard, boolean result, ValueNode proxifiedInput, Stamp guardedValueStamp, GuardRewirer rewireGuardFunction) {
 856             counterStampsFound.increment(debug);
 857             return rewireGuardFunction.rewire(guard, result, guardedValueStamp, proxifiedInput);
 858         }
 859 
 860         protected boolean tryProveCondition(LogicNode node, GuardRewirer rewireGuardFunction) {
 861             return tryProveGuardCondition(null, node, rewireGuardFunction);
 862         }
 863 
 864         private InfoElement nextElement(InfoElement current) {
 865             InfoElement parent = current.getParent();
 866             if (parent != null) {
 867                 return parent;
 868             } else {
 869                 ValueNode proxifiedInput = current.getProxifiedInput();
 870                 if (proxifiedInput instanceof PiNode) {
 871                     PiNode piNode = (PiNode) proxifiedInput;
 872                     return getInfoElements(piNode.getOriginalNode());
 873                 }
 874             }
 875             return null;
 876         }
 877 
 878         protected boolean tryProveGuardCondition(DeoptimizingGuard thisGuard, LogicNode node, GuardRewirer rewireGuardFunction) {
 879             InfoElement infoElement = getInfoElements(node);
 880             while (infoElement != null) {
 881                 Stamp stamp = infoElement.getStamp();
 882                 JavaConstant constant = (JavaConstant) stamp.asConstant();
 883                 if (constant != null) {
 884                     // No proxified input and stamp required.
 885                     return rewireGuards(infoElement.getGuard(), constant.asBoolean(), null, null, rewireGuardFunction);
 886                 }
 887                 infoElement = nextElement(infoElement);
 888             }
 889 
 890             for (GuardedCondition guardedCondition : this.conditions) {
 891                 TriState result = guardedCondition.getCondition().implies(guardedCondition.isNegated(), node);
 892                 if (result.isKnown()) {
 893                     return rewireGuards(guardedCondition.guard, result.toBoolean(), null, null, rewireGuardFunction);
 894                 }
 895             }
 896 
 897             if (node instanceof UnaryOpLogicNode) {
 898                 UnaryOpLogicNode unaryLogicNode = (UnaryOpLogicNode) node;
 899                 ValueNode value = unaryLogicNode.getValue();
 900                 infoElement = getInfoElements(value);
 901                 while (infoElement != null) {
 902                     Stamp stamp = infoElement.getStamp();
 903                     TriState result = unaryLogicNode.tryFold(stamp);
 904                     if (result.isKnown()) {
 905                         return rewireGuards(infoElement.getGuard(), result.toBoolean(), infoElement.getProxifiedInput(), infoElement.getStamp(), rewireGuardFunction);
 906                     }
 907                     infoElement = nextElement(infoElement);
 908                 }
 909                 Pair<InfoElement, Stamp> foldResult = recursiveFoldStampFromInfo(value);
 910                 if (foldResult != null) {
 911                     TriState result = unaryLogicNode.tryFold(foldResult.getRight());
 912                     if (result.isKnown()) {
 913                         return rewireGuards(foldResult.getLeft().getGuard(), result.toBoolean(), foldResult.getLeft().getProxifiedInput(), foldResult.getRight(), rewireGuardFunction);
 914                     }
 915                 }
 916                 if (thisGuard != null) {
 917                     Stamp newStamp = unaryLogicNode.getSucceedingStampForValue(thisGuard.isNegated());
 918                     if (newStamp != null && foldPendingTest(thisGuard, value, newStamp, rewireGuardFunction)) {
 919                         return true;
 920                     }
 921 
 922                 }
 923             } else if (node instanceof BinaryOpLogicNode) {
 924                 BinaryOpLogicNode binaryOpLogicNode = (BinaryOpLogicNode) node;
 925                 ValueNode x = binaryOpLogicNode.getX();
 926                 ValueNode y = binaryOpLogicNode.getY();
 927                 infoElement = getInfoElements(x);
 928                 while (infoElement != null) {
 929                     TriState result = binaryOpLogicNode.tryFold(infoElement.getStamp(), y.stamp(NodeView.DEFAULT));
 930                     if (result.isKnown()) {
 931                         return rewireGuards(infoElement.getGuard(), result.toBoolean(), infoElement.getProxifiedInput(), infoElement.getStamp(), rewireGuardFunction);
 932                     }
 933                     infoElement = nextElement(infoElement);
 934                 }
 935 
 936                 if (y.isConstant()) {
 937                     Pair<InfoElement, Stamp> foldResult = recursiveFoldStampFromInfo(x);
 938                     if (foldResult != null) {
 939                         TriState result = binaryOpLogicNode.tryFold(foldResult.getRight(), y.stamp(NodeView.DEFAULT));
 940                         if (result.isKnown()) {
 941                             return rewireGuards(foldResult.getLeft().getGuard(), result.toBoolean(), foldResult.getLeft().getProxifiedInput(), foldResult.getRight(), rewireGuardFunction);
 942                         }
 943                     }
 944                 } else {
 945                     infoElement = getInfoElements(y);
 946                     while (infoElement != null) {
 947                         TriState result = binaryOpLogicNode.tryFold(x.stamp(NodeView.DEFAULT), infoElement.getStamp());
 948                         if (result.isKnown()) {
 949                             return rewireGuards(infoElement.getGuard(), result.toBoolean(), infoElement.getProxifiedInput(), infoElement.getStamp(), rewireGuardFunction);
 950                         }
 951                         infoElement = nextElement(infoElement);
 952                     }
 953                 }
 954 
 955                 /*
 956                  * For complex expressions involving constants, see if it's possible to fold the
 957                  * tests by using stamps one level up in the expression. For instance, (x + n < y)
 958                  * might fold if something is known about x and all other values are constants. The
 959                  * reason for the constant restriction is that if more than 1 real value is involved
 960                  * the code might need to adopt multiple guards to have proper dependences.
 961                  */
 962                 if (x instanceof BinaryArithmeticNode<?> && y.isConstant()) {
 963                     BinaryArithmeticNode<?> binary = (BinaryArithmeticNode<?>) x;
 964                     if (binary.getY().isConstant()) {
 965                         infoElement = getInfoElements(binary.getX());
 966                         while (infoElement != null) {
 967                             Stamp newStampX = binary.foldStamp(infoElement.getStamp(), binary.getY().stamp(NodeView.DEFAULT));
 968                             TriState result = binaryOpLogicNode.tryFold(newStampX, y.stamp(NodeView.DEFAULT));
 969                             if (result.isKnown()) {
 970                                 return rewireGuards(infoElement.getGuard(), result.toBoolean(), infoElement.getProxifiedInput(), newStampX, rewireGuardFunction);
 971                             }
 972                             infoElement = nextElement(infoElement);
 973                         }
 974                     }
 975                 }
 976 
 977                 if (thisGuard != null && binaryOpLogicNode instanceof IntegerEqualsNode && !thisGuard.isNegated()) {
 978                     if (y.isConstant() && x instanceof AndNode) {
 979                         AndNode and = (AndNode) x;
 980                         if (and.getY() == y) {
 981                             /*
 982                              * This 'and' proves something about some of the bits in and.getX().
 983                              * It's equivalent to or'ing in the mask value since those values are
 984                              * known to be set.
 985                              */
 986                             BinaryOp<Or> op = ArithmeticOpTable.forStamp(x.stamp(NodeView.DEFAULT)).getOr();
 987                             IntegerStamp newStampX = (IntegerStamp) op.foldStamp(getSafeStamp(and.getX()), getOtherSafeStamp(y));
 988                             if (foldPendingTest(thisGuard, and.getX(), newStampX, rewireGuardFunction)) {
 989                                 return true;
 990                             }
 991                         }
 992                     }
 993                 }
 994 
 995                 if (thisGuard != null) {
 996                     if (!x.isConstant()) {
 997                         Stamp newStampX = binaryOpLogicNode.getSucceedingStampForX(thisGuard.isNegated(), getSafeStamp(x), getOtherSafeStamp(y));
 998                         if (newStampX != null && foldPendingTest(thisGuard, x, newStampX, rewireGuardFunction)) {
 999                             return true;
1000                         }
1001                     }
1002                     if (!y.isConstant()) {
1003                         Stamp newStampY = binaryOpLogicNode.getSucceedingStampForY(thisGuard.isNegated(), getOtherSafeStamp(x), getSafeStamp(y));
1004                         if (newStampY != null && foldPendingTest(thisGuard, y, newStampY, rewireGuardFunction)) {
1005                             return true;
1006                         }
1007                     }
1008                 }
1009             } else if (node instanceof ShortCircuitOrNode) {
1010                 final ShortCircuitOrNode shortCircuitOrNode = (ShortCircuitOrNode) node;
1011                 return tryProveCondition(shortCircuitOrNode.getX(), (guard, result, guardedValueStamp, newInput) -> {
1012                     if (result == !shortCircuitOrNode.isXNegated()) {
1013                         return rewireGuards(guard, true, newInput, guardedValueStamp, rewireGuardFunction);
1014                     } else {
1015                         return tryProveCondition(shortCircuitOrNode.getY(), (innerGuard, innerResult, innerGuardedValueStamp, innerNewInput) -> {
1016                             ValueNode proxifiedInput = newInput;
1017                             if (proxifiedInput == null) {
1018                                 proxifiedInput = innerNewInput;
1019                             } else if (innerNewInput != null) {
1020                                 if (innerNewInput != newInput) {
1021                                     // Cannot canonicalize due to different proxied inputs.
1022                                     return false;
1023                                 }
1024                             }
1025                             // Can only canonicalize if the guards are equal.
1026                             if (innerGuard == guard) {
1027                                 return rewireGuards(guard, innerResult ^ shortCircuitOrNode.isYNegated(), proxifiedInput, guardedValueStamp, rewireGuardFunction);
1028                             }
1029                             return false;
1030                         });
1031                     }
1032                 });
1033             }
1034 
1035             return false;
1036         }
1037 
1038         protected void registerNewStamp(ValueNode maybeProxiedValue, Stamp newStamp, GuardingNode guard) {
1039             registerNewStamp(maybeProxiedValue, newStamp, guard, false);
1040         }
1041 
1042         protected void registerNewStamp(ValueNode maybeProxiedValue, Stamp newStamp, GuardingNode guard, boolean propagateThroughPis) {
1043             assert maybeProxiedValue != null;
1044             assert guard != null;
1045 
1046             if (newStamp == null || newStamp.isUnrestricted()) {
1047                 return;
1048             }
1049 
1050             ValueNode value = maybeProxiedValue;
1051             Stamp stamp = newStamp;
1052 
1053             while (stamp != null && value != null) {
1054                 ValueNode proxiedValue = null;
1055                 if (value instanceof PiNode) {
1056                     proxiedValue = value;
1057                 }
1058                 counterStampsRegistered.increment(debug);
1059                 debug.log("\t Saving stamp for node %s stamp %s guarded by %s", value, stamp, guard);
1060                 assert value instanceof LogicNode || stamp.isCompatible(value.stamp(NodeView.DEFAULT)) : stamp + " vs. " + value.stamp(NodeView.DEFAULT) + " (" + value + ")";
1061                 map.setAndGrow(value, new InfoElement(stamp, guard, proxiedValue, map.getAndGrow(value)));
1062                 undoOperations.push(value);
1063                 if (propagateThroughPis && value instanceof PiNode) {
1064                     PiNode piNode = (PiNode) value;
1065                     value = piNode.getOriginalNode();
1066                 } else if (value instanceof StampInverter) {
1067                     StampInverter stampInverter = (StampInverter) value;
1068                     value = stampInverter.getValue();
1069                     stamp = stampInverter.invertStamp(stamp);
1070                 } else {
1071                     break;
1072                 }
1073             }
1074         }
1075 
1076         protected void processAbstractBegin(AbstractBeginNode beginNode) {
1077             Node predecessor = beginNode.predecessor();
1078             if (predecessor instanceof IfNode) {
1079                 IfNode ifNode = (IfNode) predecessor;
1080                 boolean negated = (ifNode.falseSuccessor() == beginNode);
1081                 LogicNode condition = ifNode.condition();
1082                 registerNewCondition(condition, negated, beginNode);
1083             } else if (predecessor instanceof TypeSwitchNode) {
1084                 TypeSwitchNode typeSwitch = (TypeSwitchNode) predecessor;
1085                 processTypeSwitch(beginNode, typeSwitch);
1086             } else if (predecessor instanceof IntegerSwitchNode) {
1087                 IntegerSwitchNode integerSwitchNode = (IntegerSwitchNode) predecessor;
1088                 processIntegerSwitch(beginNode, integerSwitchNode);
1089             }
1090         }
1091 
1092         private static boolean maybeMultipleUsages(ValueNode value) {
1093             if (value.hasMoreThanOneUsage()) {
1094                 return true;
1095             } else {
1096                 return value instanceof ProxyNode ||
1097                                 value instanceof PiNode ||
1098                                 value instanceof StampInverter;
1099             }
1100         }
1101 
1102         protected void processIntegerSwitch(AbstractBeginNode beginNode, IntegerSwitchNode integerSwitchNode) {
1103             ValueNode value = integerSwitchNode.value();
1104             if (maybeMultipleUsages(value)) {
1105                 Stamp stamp = integerSwitchNode.getValueStampForSuccessor(beginNode);
1106                 if (stamp != null) {
1107                     registerNewStamp(value, stamp, beginNode);
1108                 }
1109             }
1110         }
1111 
1112         protected void processTypeSwitch(AbstractBeginNode beginNode, TypeSwitchNode typeSwitch) {
1113             ValueNode hub = typeSwitch.value();
1114             if (hub instanceof LoadHubNode) {
1115                 LoadHubNode loadHub = (LoadHubNode) hub;
1116                 ValueNode value = loadHub.getValue();
1117                 if (maybeMultipleUsages(value)) {
1118                     Stamp stamp = typeSwitch.getValueStampForSuccessor(beginNode);
1119                     if (stamp != null) {
1120                         registerNewStamp(value, stamp, beginNode);
1121                     }
1122                 }
1123             }
1124         }
1125 
1126         @Override
1127         public void exit(Block b, Marks marks) {
1128             int infoElementsMark = marks.infoElementOperations;
1129             while (undoOperations.size() > infoElementsMark) {
1130                 Node node = undoOperations.pop();
1131                 if (node.isAlive()) {
1132                     map.set(node, map.get(node).getParent());
1133                 }
1134             }
1135 
1136             int conditionsMark = marks.conditions;
1137             while (conditions.size() > conditionsMark) {
1138                 conditions.pop();
1139             }
1140         }
1141     }
1142 
1143     @FunctionalInterface
1144     protected interface InfoElementProvider {
1145         Iterable<InfoElement> getInfoElements(ValueNode value);
1146     }
1147 
1148     /**
1149      * Checks for safe nodes when moving pending tests up.
1150      */
1151     static class InputFilter extends Node.EdgeVisitor {
1152         boolean ok;
1153         private ValueNode value;
1154 
1155         InputFilter(ValueNode value) {
1156             this.value = value;
1157             this.ok = true;
1158         }
1159 
1160         @Override
1161         public Node apply(Node node, Node curNode) {
1162             if (!ok) {
1163                 // Abort the recursion
1164                 return curNode;
1165             }
1166             if (!(curNode instanceof ValueNode)) {
1167                 ok = false;
1168                 return curNode;
1169             }
1170             ValueNode curValue = (ValueNode) curNode;
1171             if (curValue.isConstant() || curValue == value || curValue instanceof ParameterNode) {
1172                 return curNode;
1173             }
1174             if (curValue instanceof BinaryNode || curValue instanceof UnaryNode) {
1175                 curValue.applyInputs(this);
1176             } else {
1177                 ok = false;
1178             }
1179             return curNode;
1180         }
1181     }
1182 
1183     @FunctionalInterface
1184     protected interface GuardRewirer {
1185         /**
1186          * Called if the condition could be proven to have a constant value ({@code result}) under
1187          * {@code guard}.
1188          *
1189          * @param guard the guard whose result is proven
1190          * @param result the known result of the guard
1191          * @param newInput new input to pi nodes depending on the new guard
1192          * @return whether the transformation could be applied
1193          */
1194         boolean rewire(GuardingNode guard, boolean result, Stamp guardedValueStamp, ValueNode newInput);
1195     }
1196 
1197     protected static final class InfoElement {
1198         private final Stamp stamp;
1199         private final GuardingNode guard;
1200         private final ValueNode proxifiedInput;
1201         private final InfoElement parent;
1202 
1203         public InfoElement(Stamp stamp, GuardingNode guard, ValueNode proxifiedInput, InfoElement parent) {
1204             this.stamp = stamp;
1205             this.guard = guard;
1206             this.proxifiedInput = proxifiedInput;
1207             this.parent = parent;
1208         }
1209 
1210         public InfoElement getParent() {
1211             return parent;
1212         }
1213 
1214         public Stamp getStamp() {
1215             return stamp;
1216         }
1217 
1218         public GuardingNode getGuard() {
1219             return guard;
1220         }
1221 
1222         public ValueNode getProxifiedInput() {
1223             return proxifiedInput;
1224         }
1225 
1226         @Override
1227         public String toString() {
1228             return stamp + " -> " + guard;
1229         }
1230     }
1231 
1232     @Override
1233     public float codeSizeIncrease() {
1234         return 1.5f;
1235     }
1236 }