1 /*
   2  * Copyright (c) 2011, 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 static org.graalvm.compiler.core.common.GraalOptions.OptEliminateGuards;
  26 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
  27 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
  28 import static org.graalvm.compiler.phases.common.LoweringPhase.ProcessBlockState.ST_ENTER;
  29 import static org.graalvm.compiler.phases.common.LoweringPhase.ProcessBlockState.ST_ENTER_ALWAYS_REACHED;
  30 import static org.graalvm.compiler.phases.common.LoweringPhase.ProcessBlockState.ST_LEAVE;
  31 import static org.graalvm.compiler.phases.common.LoweringPhase.ProcessBlockState.ST_PROCESS;
  32 import static org.graalvm.compiler.phases.common.LoweringPhase.ProcessBlockState.ST_PROCESS_ALWAYS_REACHED;
  33 
  34 import java.util.ArrayList;
  35 import java.util.Collection;
  36 import java.util.Iterator;
  37 import java.util.List;
  38 
  39 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
  40 import org.graalvm.compiler.core.common.type.StampFactory;
  41 import org.graalvm.compiler.debug.DebugCloseable;
  42 import org.graalvm.compiler.debug.GraalError;
  43 import org.graalvm.compiler.graph.Graph.Mark;
  44 import org.graalvm.compiler.graph.Node;
  45 import org.graalvm.compiler.graph.NodeBitMap;
  46 import org.graalvm.compiler.graph.NodeClass;
  47 import org.graalvm.compiler.graph.iterators.NodeIterable;
  48 import org.graalvm.compiler.nodeinfo.InputType;
  49 import org.graalvm.compiler.nodeinfo.NodeInfo;
  50 import org.graalvm.compiler.nodes.AbstractBeginNode;
  51 import org.graalvm.compiler.nodes.BeginNode;
  52 import org.graalvm.compiler.nodes.FixedGuardNode;
  53 import org.graalvm.compiler.nodes.FixedNode;
  54 import org.graalvm.compiler.nodes.FixedWithNextNode;
  55 import org.graalvm.compiler.nodes.GuardNode;
  56 import org.graalvm.compiler.nodes.LogicNode;
  57 import org.graalvm.compiler.nodes.StructuredGraph;
  58 import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
  59 import org.graalvm.compiler.nodes.ValueNode;
  60 import org.graalvm.compiler.nodes.calc.FloatingNode;
  61 import org.graalvm.compiler.nodes.cfg.Block;
  62 import org.graalvm.compiler.nodes.extended.AnchoringNode;
  63 import org.graalvm.compiler.nodes.extended.GuardedNode;
  64 import org.graalvm.compiler.nodes.extended.GuardingNode;
  65 import org.graalvm.compiler.nodes.spi.Lowerable;
  66 import org.graalvm.compiler.nodes.spi.LoweringProvider;
  67 import org.graalvm.compiler.nodes.spi.LoweringTool;
  68 import org.graalvm.compiler.nodes.spi.NodeCostProvider;
  69 import org.graalvm.compiler.nodes.spi.Replacements;
  70 import org.graalvm.compiler.nodes.spi.StampProvider;
  71 import org.graalvm.compiler.phases.BasePhase;
  72 import org.graalvm.compiler.phases.Phase;
  73 import org.graalvm.compiler.phases.schedule.SchedulePhase;
  74 import org.graalvm.compiler.phases.tiers.PhaseContext;
  75 
  76 import jdk.vm.ci.meta.ConstantReflectionProvider;
  77 import jdk.vm.ci.meta.DeoptimizationAction;
  78 import jdk.vm.ci.meta.DeoptimizationReason;
  79 import jdk.vm.ci.meta.JavaConstant;
  80 import jdk.vm.ci.meta.MetaAccessProvider;
  81 
  82 /**
  83  * Processes all {@link Lowerable} nodes to do their lowering.
  84  */
  85 public class LoweringPhase extends BasePhase<PhaseContext> {
  86 
  87     @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
  88     static final class DummyGuardHandle extends ValueNode implements GuardedNode {
  89         public static final NodeClass<DummyGuardHandle> TYPE = NodeClass.create(DummyGuardHandle.class);
  90         @Input(InputType.Guard) GuardingNode guard;
  91 
  92         protected DummyGuardHandle(GuardingNode guard) {
  93             super(TYPE, StampFactory.forVoid());
  94             this.guard = guard;
  95         }
  96 
  97         @Override
  98         public GuardingNode getGuard() {
  99             return guard;
 100         }
 101 
 102         @Override
 103         public void setGuard(GuardingNode guard) {
 104             updateUsagesInterface(this.guard, guard);
 105             this.guard = guard;
 106         }
 107 
 108         @Override
 109         public ValueNode asNode() {
 110             return this;
 111         }
 112     }
 113 
 114     @Override
 115     public boolean checkContract() {
 116         return false;
 117     }
 118 
 119     final class LoweringToolImpl implements LoweringTool {
 120 
 121         private final PhaseContext context;
 122         private final NodeBitMap activeGuards;
 123         private AnchoringNode guardAnchor;
 124         private FixedWithNextNode lastFixedNode;
 125 
 126         LoweringToolImpl(PhaseContext context, AnchoringNode guardAnchor, NodeBitMap activeGuards, FixedWithNextNode lastFixedNode) {
 127             this.context = context;
 128             this.guardAnchor = guardAnchor;
 129             this.activeGuards = activeGuards;
 130             this.lastFixedNode = lastFixedNode;
 131         }
 132 
 133         @Override
 134         public LoweringStage getLoweringStage() {
 135             return loweringStage;
 136         }
 137 
 138         @Override
 139         public ConstantReflectionProvider getConstantReflection() {
 140             return context.getConstantReflection();
 141         }
 142 
 143         @Override
 144         public ConstantFieldProvider getConstantFieldProvider() {
 145             return context.getConstantFieldProvider();
 146         }
 147 
 148         @Override
 149         public MetaAccessProvider getMetaAccess() {
 150             return context.getMetaAccess();
 151         }
 152 
 153         @Override
 154         public LoweringProvider getLowerer() {
 155             return context.getLowerer();
 156         }
 157 
 158         @Override
 159         public Replacements getReplacements() {
 160             return context.getReplacements();
 161         }
 162 
 163         @Override
 164         public AnchoringNode getCurrentGuardAnchor() {
 165             return guardAnchor;
 166         }
 167 
 168         @Override
 169         public GuardingNode createGuard(FixedNode before, LogicNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action) {
 170             return createGuard(before, condition, deoptReason, action, JavaConstant.NULL_POINTER, false);
 171         }
 172 
 173         @Override
 174         public StampProvider getStampProvider() {
 175             return context.getStampProvider();
 176         }
 177 
 178         @Override
 179         public GuardingNode createGuard(FixedNode before, LogicNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action, JavaConstant speculation, boolean negated) {
 180             if (OptEliminateGuards.getValue()) {
 181                 for (Node usage : condition.usages()) {
 182                     if (!activeGuards.isNew(usage) && activeGuards.isMarked(usage) && ((GuardNode) usage).isNegated() == negated) {
 183                         return (GuardNode) usage;
 184                     }
 185                 }
 186             }
 187             StructuredGraph graph = before.graph();
 188             if (!condition.graph().getGuardsStage().allowsFloatingGuards()) {
 189                 FixedGuardNode fixedGuard = graph.add(new FixedGuardNode(condition, deoptReason, action, speculation, negated));
 190                 graph.addBeforeFixed(before, fixedGuard);
 191                 DummyGuardHandle handle = graph.add(new DummyGuardHandle(fixedGuard));
 192                 fixedGuard.lower(this);
 193                 GuardingNode result = handle.getGuard();
 194                 handle.safeDelete();
 195                 return result;
 196             } else {
 197                 GuardNode newGuard = graph.unique(new GuardNode(condition, guardAnchor, deoptReason, action, negated, speculation));
 198                 if (OptEliminateGuards.getValue()) {
 199                     activeGuards.markAndGrow(newGuard);
 200                 }
 201                 return newGuard;
 202             }
 203         }
 204 
 205         @Override
 206         public FixedWithNextNode lastFixedNode() {
 207             return lastFixedNode;
 208         }
 209 
 210         @Override
 211         public NodeCostProvider getNodeCostProvider() {
 212             return context.getNodeCostProvider();
 213         }
 214 
 215         private void setLastFixedNode(FixedWithNextNode n) {
 216             assert n.isAlive() : n;
 217             lastFixedNode = n;
 218         }
 219     }
 220 
 221     private final CanonicalizerPhase canonicalizer;
 222     private final LoweringTool.LoweringStage loweringStage;
 223 
 224     public LoweringPhase(CanonicalizerPhase canonicalizer, LoweringTool.LoweringStage loweringStage) {
 225         this.canonicalizer = canonicalizer;
 226         this.loweringStage = loweringStage;
 227     }
 228 
 229     /**
 230      * Checks that second lowering of a given graph did not introduce any new nodes.
 231      *
 232      * @param graph a graph that was just {@linkplain #lower lowered}
 233      * @throws AssertionError if the check fails
 234      */
 235     private boolean checkPostLowering(StructuredGraph graph, PhaseContext context) {
 236         Mark expectedMark = graph.getMark();
 237         lower(graph, context, LoweringMode.VERIFY_LOWERING);
 238         Mark mark = graph.getMark();
 239         assert mark.equals(expectedMark) : graph + ": a second round in the current lowering phase introduced these new nodes: " + graph.getNewNodes(expectedMark).snapshot();
 240         return true;
 241     }
 242 
 243     @Override
 244     protected void run(final StructuredGraph graph, PhaseContext context) {
 245         lower(graph, context, LoweringMode.LOWERING);
 246         assert checkPostLowering(graph, context);
 247     }
 248 
 249     private void lower(StructuredGraph graph, PhaseContext context, LoweringMode mode) {
 250         IncrementalCanonicalizerPhase<PhaseContext> incrementalCanonicalizer = new IncrementalCanonicalizerPhase<>(canonicalizer);
 251         incrementalCanonicalizer.appendPhase(new Round(context, mode));
 252         incrementalCanonicalizer.apply(graph, context);
 253         assert graph.verify();
 254     }
 255 
 256     /**
 257      * Checks that lowering of a given node did not introduce any new {@link Lowerable} nodes that
 258      * could be lowered in the current {@link LoweringPhase}. Such nodes must be recursively lowered
 259      * as part of lowering {@code node}.
 260      *
 261      * @param node a node that was just lowered
 262      * @param preLoweringMark the graph mark before {@code node} was lowered
 263      * @param unscheduledUsages set of {@code node}'s usages that were unscheduled before it was
 264      *            lowered
 265      * @throws AssertionError if the check fails
 266      */
 267     private static boolean checkPostNodeLowering(Node node, LoweringToolImpl loweringTool, Mark preLoweringMark, Collection<Node> unscheduledUsages) {
 268         StructuredGraph graph = (StructuredGraph) node.graph();
 269         Mark postLoweringMark = graph.getMark();
 270         NodeIterable<Node> newNodesAfterLowering = graph.getNewNodes(preLoweringMark);
 271         if (node instanceof FloatingNode) {
 272             if (!unscheduledUsages.isEmpty()) {
 273                 for (Node n : newNodesAfterLowering) {
 274                     assert !(n instanceof FixedNode) : node.graph() + ": cannot lower floatable node " + node + " as it introduces fixed node(s) but has the following unscheduled usages: " +
 275                                     unscheduledUsages;
 276                 }
 277             }
 278         }
 279         for (Node n : newNodesAfterLowering) {
 280             if (n instanceof Lowerable) {
 281                 ((Lowerable) n).lower(loweringTool);
 282                 Mark mark = graph.getMark();
 283                 assert postLoweringMark.equals(mark) : graph + ": lowering of " + node + " produced lowerable " + n + " that should have been recursively lowered as it introduces these new nodes: " +
 284                                 graph.getNewNodes(postLoweringMark).snapshot();
 285             }
 286         }
 287         return true;
 288     }
 289 
 290     private enum LoweringMode {
 291         LOWERING,
 292         VERIFY_LOWERING
 293     }
 294 
 295     private final class Round extends Phase {
 296 
 297         private final PhaseContext context;
 298         private final LoweringMode mode;
 299         private ScheduleResult schedule;
 300         private final SchedulePhase schedulePhase;
 301 
 302         private Round(PhaseContext context, LoweringMode mode) {
 303             this.context = context;
 304             this.mode = mode;
 305 
 306             /*
 307              * In VERIFY_LOWERING, we want to verify whether the lowering itself changes the graph.
 308              * Make sure we're not detecting spurious changes because the SchedulePhase modifies the
 309              * graph.
 310              */
 311             boolean immutableSchedule = mode == LoweringMode.VERIFY_LOWERING;
 312 
 313             this.schedulePhase = new SchedulePhase(immutableSchedule);
 314         }
 315 
 316         @Override
 317         protected CharSequence getName() {
 318             switch (mode) {
 319                 case LOWERING:
 320                     return "LoweringRound";
 321                 case VERIFY_LOWERING:
 322                     return "VerifyLoweringRound";
 323                 default:
 324                     throw GraalError.shouldNotReachHere();
 325             }
 326         }
 327 
 328         @Override
 329         public boolean checkContract() {
 330             /*
 331              * lowering with snippets cannot be fully built in the node costs of all high level
 332              * nodes
 333              */
 334             return false;
 335         }
 336 
 337         @Override
 338         public void run(StructuredGraph graph) {
 339             schedulePhase.apply(graph, false);
 340             schedule = graph.getLastSchedule();
 341             schedule.getCFG().computePostdominators();
 342             Block startBlock = schedule.getCFG().getStartBlock();
 343             ProcessFrame rootFrame = new ProcessFrame(startBlock, graph.createNodeBitMap(), startBlock.getBeginNode(), null);
 344             LoweringPhase.processBlock(rootFrame);
 345         }
 346 
 347         private class ProcessFrame extends Frame<ProcessFrame> {
 348             private final NodeBitMap activeGuards;
 349             private AnchoringNode anchor;
 350 
 351             ProcessFrame(Block block, NodeBitMap activeGuards, AnchoringNode anchor, ProcessFrame parent) {
 352                 super(block, parent);
 353                 this.activeGuards = activeGuards;
 354                 this.anchor = anchor;
 355             }
 356 
 357             @Override
 358             public void preprocess() {
 359                 this.anchor = Round.this.process(block, activeGuards, anchor);
 360             }
 361 
 362             @Override
 363             public ProcessFrame enter(Block b) {
 364                 return new ProcessFrame(b, activeGuards, b.getBeginNode(), this);
 365             }
 366 
 367             @Override
 368             public Frame<?> enterAlwaysReached(Block b) {
 369                 AnchoringNode newAnchor = anchor;
 370                 if (parent != null && b.getLoop() != parent.block.getLoop() && !b.isLoopHeader()) {
 371                     // We are exiting a loop => cannot reuse the anchor without inserting loop
 372                     // proxies.
 373                     newAnchor = b.getBeginNode();
 374                 }
 375                 return new ProcessFrame(b, activeGuards, newAnchor, this);
 376             }
 377 
 378             @Override
 379             public void postprocess() {
 380                 if (anchor != null && OptEliminateGuards.getValue()) {
 381                     for (GuardNode guard : anchor.asNode().usages().filter(GuardNode.class)) {
 382                         if (activeGuards.isMarkedAndGrow(guard)) {
 383                             activeGuards.clear(guard);
 384                         }
 385                     }
 386                 }
 387             }
 388 
 389         }
 390 
 391         @SuppressWarnings("try")
 392         private AnchoringNode process(final Block b, final NodeBitMap activeGuards, final AnchoringNode startAnchor) {
 393 
 394             final LoweringToolImpl loweringTool = new LoweringToolImpl(context, startAnchor, activeGuards, b.getBeginNode());
 395 
 396             // Lower the instructions of this block.
 397             List<Node> nodes = schedule.nodesFor(b);
 398             for (Node node : nodes) {
 399 
 400                 if (node.isDeleted()) {
 401                     // This case can happen when previous lowerings deleted nodes.
 402                     continue;
 403                 }
 404 
 405                 // Cache the next node to be able to reconstruct the previous of the next node
 406                 // after lowering.
 407                 FixedNode nextNode = null;
 408                 if (node instanceof FixedWithNextNode) {
 409                     nextNode = ((FixedWithNextNode) node).next();
 410                 } else {
 411                     nextNode = loweringTool.lastFixedNode().next();
 412                 }
 413 
 414                 if (node instanceof Lowerable) {
 415                     Collection<Node> unscheduledUsages = null;
 416                     assert (unscheduledUsages = getUnscheduledUsages(node)) != null;
 417                     Mark preLoweringMark = node.graph().getMark();
 418                     try (DebugCloseable s = node.graph().withNodeSourcePosition(node)) {
 419                         ((Lowerable) node).lower(loweringTool);
 420                     }
 421                     if (loweringTool.guardAnchor.asNode().isDeleted()) {
 422                         // TODO nextNode could be deleted but this is not currently supported
 423                         assert nextNode.isAlive();
 424                         loweringTool.guardAnchor = AbstractBeginNode.prevBegin(nextNode);
 425                     }
 426                     assert checkPostNodeLowering(node, loweringTool, preLoweringMark, unscheduledUsages);
 427                 }
 428 
 429                 if (!nextNode.isAlive()) {
 430                     // can happen when the rest of the block is killed by lowering
 431                     // (e.g. by an unconditional deopt)
 432                     break;
 433                 } else {
 434                     Node nextLastFixed = nextNode.predecessor();
 435                     if (!(nextLastFixed instanceof FixedWithNextNode)) {
 436                         // insert begin node, to have a valid last fixed for next lowerable node.
 437                         // This is about lowering a FixedWithNextNode to a control split while this
 438                         // FixedWithNextNode is followed by some kind of BeginNode.
 439                         // For example the when a FixedGuard followed by a loop exit is lowered to a
 440                         // control-split + deopt.
 441                         AbstractBeginNode begin = node.graph().add(new BeginNode());
 442                         nextLastFixed.replaceFirstSuccessor(nextNode, begin);
 443                         begin.setNext(nextNode);
 444                         nextLastFixed = begin;
 445                     }
 446                     loweringTool.setLastFixedNode((FixedWithNextNode) nextLastFixed);
 447                 }
 448             }
 449             return loweringTool.getCurrentGuardAnchor();
 450         }
 451 
 452         /**
 453          * Gets all usages of a floating, lowerable node that are unscheduled.
 454          * <p>
 455          * Given that the lowering of such nodes may introduce fixed nodes, they must be lowered in
 456          * the context of a usage that dominates all other usages. The fixed nodes resulting from
 457          * lowering are attached to the fixed node context of the dominating usage. This ensures the
 458          * post-lowering graph still has a valid schedule.
 459          *
 460          * @param node a {@link Lowerable} node
 461          */
 462         private Collection<Node> getUnscheduledUsages(Node node) {
 463             List<Node> unscheduledUsages = new ArrayList<>();
 464             if (node instanceof FloatingNode) {
 465                 for (Node usage : node.usages()) {
 466                     if (usage instanceof ValueNode) {
 467                         if (schedule.getCFG().getNodeToBlock().isNew(usage) || schedule.getCFG().blockFor(usage) == null) {
 468                             unscheduledUsages.add(usage);
 469                         }
 470                     }
 471                 }
 472             }
 473             return unscheduledUsages;
 474         }
 475     }
 476 
 477     enum ProcessBlockState {
 478         ST_ENTER,
 479         ST_PROCESS,
 480         ST_ENTER_ALWAYS_REACHED,
 481         ST_LEAVE,
 482         ST_PROCESS_ALWAYS_REACHED;
 483     }
 484 
 485     /**
 486      * This state-machine resembles the following recursion:
 487      *
 488      * <pre>
 489      * void processBlock(Block block) {
 490      *     preprocess();
 491      *     // Process always reached block first.
 492      *     Block alwaysReachedBlock = block.getPostdominator();
 493      *     if (alwaysReachedBlock != null &amp;&amp; alwaysReachedBlock.getDominator() == block) {
 494      *         processBlock(alwaysReachedBlock);
 495      *     }
 496      *
 497      *     // Now go for the other dominators.
 498      *     for (Block dominated : block.getDominated()) {
 499      *         if (dominated != alwaysReachedBlock) {
 500      *             assert dominated.getDominator() == block;
 501      *             processBlock(dominated);
 502      *         }
 503      *     }
 504      *     postprocess();
 505      * }
 506      * </pre>
 507      *
 508      * This is necessary, as the recursive implementation quickly exceed the stack depth on SPARC.
 509      *
 510      * @param rootFrame contains the starting block.
 511      */
 512     public static void processBlock(final Frame<?> rootFrame) {
 513         ProcessBlockState state = ST_PROCESS;
 514         Frame<?> f = rootFrame;
 515         while (f != null) {
 516             ProcessBlockState nextState;
 517             if (state == ST_PROCESS || state == ST_PROCESS_ALWAYS_REACHED) {
 518                 f.preprocess();
 519                 nextState = state == ST_PROCESS_ALWAYS_REACHED ? ST_ENTER : ST_ENTER_ALWAYS_REACHED;
 520             } else if (state == ST_ENTER_ALWAYS_REACHED) {
 521                 if (f.alwaysReachedBlock != null && f.alwaysReachedBlock.getDominator() == f.block) {
 522                     f = f.enterAlwaysReached(f.alwaysReachedBlock);
 523                     nextState = ST_PROCESS;
 524                 } else {
 525                     nextState = ST_ENTER;
 526                 }
 527             } else if (state == ST_ENTER) {
 528                 if (f.dominated.hasNext()) {
 529                     Block n = f.dominated.next();
 530                     if (n == f.alwaysReachedBlock) {
 531                         if (f.dominated.hasNext()) {
 532                             n = f.dominated.next();
 533                         } else {
 534                             n = null;
 535                         }
 536                     }
 537                     if (n == null) {
 538                         nextState = ST_LEAVE;
 539                     } else {
 540                         f = f.enter(n);
 541                         assert f.block.getDominator() == f.parent.block;
 542                         nextState = ST_PROCESS;
 543                     }
 544                 } else {
 545                     nextState = ST_LEAVE;
 546                 }
 547             } else if (state == ST_LEAVE) {
 548                 f.postprocess();
 549                 f = f.parent;
 550                 nextState = ST_ENTER;
 551             } else {
 552                 throw GraalError.shouldNotReachHere();
 553             }
 554             state = nextState;
 555         }
 556     }
 557 
 558     public static void processBlockBounded(final Frame<?> rootFrame) {
 559         ProcessBlockState state = ST_PROCESS;
 560         Frame<?> f = rootFrame;
 561         while (f != null) {
 562             ProcessBlockState nextState;
 563             if (state == ST_PROCESS || state == ST_PROCESS_ALWAYS_REACHED) {
 564                 f.preprocess();
 565                 nextState = state == ST_PROCESS_ALWAYS_REACHED ? ST_ENTER : ST_ENTER_ALWAYS_REACHED;
 566             } else if (state == ST_ENTER_ALWAYS_REACHED) {
 567                 if (f.alwaysReachedBlock != null && f.alwaysReachedBlock.getDominator() == f.block) {
 568                     Frame<?> continueRecur = f.enterAlwaysReached(f.alwaysReachedBlock);
 569                     if (continueRecur == null) {
 570                         // stop recursion here
 571                         f.postprocess();
 572                         f = f.parent;
 573                         state = ST_ENTER;
 574                         continue;
 575                     }
 576                     f = continueRecur;
 577                     nextState = ST_PROCESS;
 578                 } else {
 579                     nextState = ST_ENTER;
 580                 }
 581             } else if (state == ST_ENTER) {
 582                 if (f.dominated.hasNext()) {
 583                     Block n = f.dominated.next();
 584                     if (n == f.alwaysReachedBlock) {
 585                         if (f.dominated.hasNext()) {
 586                             n = f.dominated.next();
 587                         } else {
 588                             n = null;
 589                         }
 590                     }
 591                     if (n == null) {
 592                         nextState = ST_LEAVE;
 593                     } else {
 594                         Frame<?> continueRecur = f.enter(n);
 595                         if (continueRecur == null) {
 596                             // stop recursion here
 597                             f.postprocess();
 598                             f = f.parent;
 599                             state = ST_ENTER;
 600                             continue;
 601                         }
 602                         f = continueRecur;
 603                         nextState = ST_PROCESS;
 604                     }
 605                 } else {
 606                     nextState = ST_LEAVE;
 607                 }
 608             } else if (state == ST_LEAVE) {
 609                 f.postprocess();
 610                 f = f.parent;
 611                 nextState = ST_ENTER;
 612             } else {
 613                 throw GraalError.shouldNotReachHere();
 614             }
 615             state = nextState;
 616         }
 617     }
 618 
 619     public abstract static class Frame<T extends Frame<?>> {
 620         protected final Block block;
 621         final T parent;
 622         Iterator<Block> dominated;
 623         final Block alwaysReachedBlock;
 624 
 625         public Frame(Block block, T parent) {
 626             super();
 627             this.block = block;
 628             this.alwaysReachedBlock = block.getPostdominator();
 629             this.dominated = block.getDominated().iterator();
 630             this.parent = parent;
 631         }
 632 
 633         public Frame<?> enterAlwaysReached(Block b) {
 634             return enter(b);
 635         }
 636 
 637         public abstract Frame<?> enter(Block b);
 638 
 639         public abstract void preprocess();
 640 
 641         public abstract void postprocess();
 642     }
 643 
 644 }