LoweringPhase.java revision 12651:6ef01bd40ce2
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 */
23package org.graalvm.compiler.phases.common;
24
25import static org.graalvm.compiler.core.common.GraalOptions.OptEliminateGuards;
26import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
27import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
28import static org.graalvm.compiler.phases.common.LoweringPhase.ProcessBlockState.ST_ENTER;
29import static org.graalvm.compiler.phases.common.LoweringPhase.ProcessBlockState.ST_ENTER_ALWAYS_REACHED;
30import static org.graalvm.compiler.phases.common.LoweringPhase.ProcessBlockState.ST_LEAVE;
31import static org.graalvm.compiler.phases.common.LoweringPhase.ProcessBlockState.ST_PROCESS;
32import static org.graalvm.compiler.phases.common.LoweringPhase.ProcessBlockState.ST_PROCESS_ALWAYS_REACHED;
33
34import java.util.ArrayList;
35import java.util.Collection;
36import java.util.Iterator;
37import java.util.List;
38
39import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
40import org.graalvm.compiler.core.common.type.StampFactory;
41import org.graalvm.compiler.debug.DebugCloseable;
42import org.graalvm.compiler.debug.GraalError;
43import org.graalvm.compiler.graph.Graph.Mark;
44import org.graalvm.compiler.graph.Node;
45import org.graalvm.compiler.graph.NodeBitMap;
46import org.graalvm.compiler.graph.NodeClass;
47import org.graalvm.compiler.graph.iterators.NodeIterable;
48import org.graalvm.compiler.nodeinfo.InputType;
49import org.graalvm.compiler.nodeinfo.NodeInfo;
50import org.graalvm.compiler.nodes.AbstractBeginNode;
51import org.graalvm.compiler.nodes.BeginNode;
52import org.graalvm.compiler.nodes.FixedGuardNode;
53import org.graalvm.compiler.nodes.FixedNode;
54import org.graalvm.compiler.nodes.FixedWithNextNode;
55import org.graalvm.compiler.nodes.GuardNode;
56import org.graalvm.compiler.nodes.LogicNode;
57import org.graalvm.compiler.nodes.StructuredGraph;
58import org.graalvm.compiler.nodes.StructuredGraph.ScheduleResult;
59import org.graalvm.compiler.nodes.ValueNode;
60import org.graalvm.compiler.nodes.calc.FloatingNode;
61import org.graalvm.compiler.nodes.cfg.Block;
62import org.graalvm.compiler.nodes.extended.AnchoringNode;
63import org.graalvm.compiler.nodes.extended.GuardedNode;
64import org.graalvm.compiler.nodes.extended.GuardingNode;
65import org.graalvm.compiler.nodes.spi.Lowerable;
66import org.graalvm.compiler.nodes.spi.LoweringProvider;
67import org.graalvm.compiler.nodes.spi.LoweringTool;
68import org.graalvm.compiler.nodes.spi.NodeCostProvider;
69import org.graalvm.compiler.nodes.spi.Replacements;
70import org.graalvm.compiler.nodes.spi.StampProvider;
71import org.graalvm.compiler.phases.BasePhase;
72import org.graalvm.compiler.phases.Phase;
73import org.graalvm.compiler.phases.schedule.SchedulePhase;
74import org.graalvm.compiler.phases.tiers.PhaseContext;
75
76import jdk.vm.ci.meta.ConstantReflectionProvider;
77import jdk.vm.ci.meta.DeoptimizationAction;
78import jdk.vm.ci.meta.DeoptimizationReason;
79import jdk.vm.ci.meta.JavaConstant;
80import jdk.vm.ci.meta.MetaAccessProvider;
81
82/**
83 * Processes all {@link Lowerable} nodes to do their lowering.
84 */
85public 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}
645