Skip to main content

cinrs_core/
reloop.rs

1//! Recovering Rust's own control flow from the [graph](crate::cfg).
2//!
3//! The graph is a correct lowering of any `goto`, and as a flat
4//! `loop { match state { … } }` it is also an opaque one: every edge is a store
5//! and a jump back to a dispatch, and the loops the C program had are gone —
6//! which is what made SQLite's `sqlite3VdbeExec` run five times the
7//! instructions GCC's does. This module puts them back. It reads the finished
8//! graph and rebuilds a *structured* program out of three shapes, which
9//! [`codegen`](crate::codegen) emits as ordinary Rust:
10//!
11//! * **Simple** — one basic block. Its terminator becomes an `if` or a `match`,
12//!   and the blocks that only *it* can reach are emitted inside the arms, so a
13//!   two-way branch is `if c { … } else { … }` and a `switch` is a `match` with
14//!   the case bodies in it.
15//! * **Loop** — a set of blocks with an entry that something inside jumps back
16//!   to. It becomes `'l: loop { … }`: a back edge is `continue 'l` and an exit
17//!   is `break 'l`. The label is the C label the head carries, where there is
18//!   one.
19//! * **Sequence** — the run of shapes at one level, in an order in which every
20//!   remaining jump goes *forward*. A forward jump is `break` of a labelled
21//!   block that ends where its target begins, which is the same trick
22//!   [`regions`](crate::regions) plays on the statement tree, and the reason
23//!   nothing here needs a variable to say where control went.
24//!
25//! The algorithm is Emscripten's relooper — Alon Zakai, *Emscripten: An
26//! LLVM-to-JavaScript Compiler*, OOPSLA 2011, §3.2 — as it is also implemented
27//! in `c2rust-transpile`'s `cfg::relooper`. It was written from the description
28//! of the algorithm rather than from either implementation.
29//!
30//! # The recursion
31//!
32//! `process(entries, blocks)` builds the shapes for `blocks`, which control can
33//! only get into at one of `entries`:
34//!
35//! 1. **One entry that nothing in `blocks` jumps back to** → a Simple for it,
36//!    then `process` of what its terminator reaches.
37//! 2. **Several entries** → try a Multiple: give each entry the blocks that
38//!    *only it* can reach, lay those groups out one after another, and
39//!    `process` the rest after them. An entry another entry can reach owns
40//!    nothing and waits for the rest.
41//! 3. **Otherwise** → a Loop. Its body is the entries plus every block that can
42//!    get back to one of them; what is left follows it. The back edges are then
43//!    *taken out of the graph*, which is what lets the body be relooped as if
44//!    its entries were entered from outside only.
45//!
46//! # Irreducible regions
47//!
48//! A `goto` into the middle of a loop, Duff's device and two loops that jump
49//! into each other's bodies all produce a cycle with two heads, which no
50//! arrangement of Rust's blocks and loops can express. Step 3 is where they
51//! land: the Loop keeps *both* heads, and once its back edges are cut, step 2
52//! splits the body into one group per head. That dispatch — and only that one —
53//! reads a state variable, which every jump to a head writes first. It is one
54//! `u32` per irreducible region rather than one per function, and the blocks
55//! outside the region never touch it.
56//!
57//! # What the shapes preserve
58//!
59//! Everything, because the graph already holds it. A `cleanup` call and a
60//! variable length array's release are *statements at the end of the block the
61//! edge leaves from* — [`cfg`](crate::cfg) puts them there — so no shape has to
62//! know about them. `switch` fallthrough is an edge from one case block to the
63//! next, and comes out as the case body falling out of its `match` arm into the
64//! code after the `match`. A `return` is a block terminator wherever it stands.
65//!
66//! # Giving up
67//!
68//! Two things keep the [state machine](crate::cfg) alive. A `switch` whose
69//! `case`s fall through one into the next gives each of them a labelled block,
70//! and past two hundred of those `rustc`'s own parser runs out of stack, where
71//! the machine's flat `match` does not. And the shapes are checked before they
72//! are handed over: if every block is not in the tree exactly once, or if some
73//! jump has nothing to break to, [`plan`] answers `None` and the state machine
74//! runs instead of something subtly wrong.
75//!
76//! GNU's computed `goto` is not one of them: [`cfg`](crate::cfg) has already
77//! made it a `switch` over the labels whose address is taken, so an
78//! interpreter's dispatch loop is a Loop around a Simple whose `match` holds
79//! the handlers — the shape the same interpreter written with a `switch` has.
80
81use std::collections::{BTreeSet, HashMap, HashSet};
82
83use crate::cfg::{BasicBlock, BlockId, Terminator};
84
85/// A set of blocks, in block order.
86type Set = BTreeSet<BlockId>;
87
88/// Where control arrives when it runs off the end of a shape, or when it
89/// `break`s or `continue`s a labelled one.
90///
91/// More than one target means an irreducible region's dispatch: which of them
92/// is entered is the value written to [`Exit::state`] on the way.
93#[derive(Clone, Debug, Default, PartialEq, Eq)]
94pub struct Exit {
95    /// The blocks control can arrive at.
96    pub targets: Vec<BlockId>,
97    /// The state variable that says which, when there is more than one.
98    pub state: Option<u32>,
99}
100
101impl Exit {
102    /// Nothing follows: control cannot leave this way.
103    pub fn nowhere() -> Self {
104        Self::default()
105    }
106
107    /// One block follows, with no dispatch in between.
108    pub fn to(target: BlockId) -> Self {
109        Self {
110            targets: vec![target],
111            state: None,
112        }
113    }
114
115    /// Which entry `target` is, if this exit reaches it.
116    pub fn index_of(&self, target: BlockId) -> Option<usize> {
117        self.targets.iter().position(|block| *block == target)
118    }
119}
120
121/// A run of shapes, one after another.
122///
123/// A shape may jump to the entry of any shape *after* it in the run, which is a
124/// `break` of a labelled block that ends where that shape begins; the last one
125/// falls out of the run.
126pub type Seq = Vec<Shape>;
127
128/// One entry of a branch, and the shapes that run it.
129#[derive(Clone, Debug)]
130pub struct Arm {
131    /// The block the branch enters.
132    pub entry: BlockId,
133    /// What runs there, up to where the arms meet again.
134    pub body: Seq,
135}
136
137/// A piece of recovered control flow.
138#[derive(Clone, Debug)]
139pub enum Shape {
140    /// One basic block: its statements, then its terminator.
141    Simple {
142        /// The block.
143        block: BlockId,
144        /// The targets of its terminator that nothing else reaches, emitted
145        /// inside the `if` or the `match` the terminator becomes. Every other
146        /// target becomes a jump.
147        arms: Vec<Arm>,
148    },
149    /// `'l: loop { … }`.
150    Loop {
151        /// Distinguishes this loop's label from the others in the function.
152        id: u32,
153        /// The C label its head carries, which the Rust label is named after.
154        name: Option<String>,
155        /// The blocks it is entered at: one for a natural loop, several for an
156        /// irreducible region.
157        entries: Vec<BlockId>,
158        /// The state variable that picks the entry, for such a region.
159        state: Option<u32>,
160        /// What runs inside. For an irreducible region the first shape is the
161        /// [dispatch](Shape::Dispatch) over `state`.
162        body: Seq,
163    },
164    /// The head of an irreducible region: a `match` on the state variable that
165    /// picks which entry this turn round the loop runs.
166    Dispatch {
167        /// The state variable it reads.
168        state: u32,
169        /// One arm per entry, in the order the state numbers them.
170        arms: Vec<Arm>,
171    },
172}
173
174impl Shape {
175    /// Where a jump to this shape arrives.
176    pub fn exit(&self) -> Exit {
177        match self {
178            Shape::Simple { block, .. } => Exit::to(*block),
179            Shape::Loop { entries, state, .. } => Exit {
180                targets: entries.clone(),
181                state: *state,
182            },
183            Shape::Dispatch { state, arms } => Exit {
184                targets: arms.iter().map(|arm| arm.entry).collect(),
185                state: Some(*state),
186            },
187        }
188    }
189}
190
191/// A whole function body, recovered.
192#[derive(Clone, Debug)]
193pub struct Plan {
194    /// The shapes of the body.
195    pub body: Seq,
196    /// How many state variables the irreducible regions need; they are
197    /// numbered from zero.
198    pub states: u32,
199}
200
201/// Recovers the structured form of a graph, or answers `None`.
202///
203/// `names` is the C label each block stands at, where it stands at one, which
204/// is what the loops are named after. `None` means the [state
205/// machine](crate::cfg) has to be used: a function whose shapes would nest
206/// deeper than `rustc` parses, or — which has not been observed, and is checked
207/// for rather than trusted — one whose shapes would not account for every block
208/// or would leave a jump with nothing to break to.
209pub fn plan(blocks: &[BasicBlock], names: &HashMap<BlockId, String>) -> Option<Plan> {
210    if blocks.is_empty() {
211        return None;
212    }
213    let mut relooper = Relooper::new(blocks, names);
214    let all: Set = (0..blocks.len() as u32).map(BlockId).collect();
215    let entries: Set = [BlockId(0)].into_iter().collect();
216    let body = relooper.process(&entries, &all);
217    let plan = Plan {
218        body,
219        states: relooper.states,
220    };
221    let mut seen = vec![0usize; blocks.len()];
222    count_blocks(&plan.body, &mut seen);
223    if seen.iter().any(|times| *times != 1) {
224        return None;
225    }
226    if nesting(&plan.body, 0) > MAX_NESTING {
227        return None;
228    }
229    let mut scopes = Vec::new();
230    resolves(blocks, &plan.body, &Exit::nowhere(), &mut scopes).then_some(plan)
231}
232
233/// How deeply the output may nest blocks and loops.
234///
235/// A `switch` whose `case`s fall through one into the next gives each of them a
236/// labelled block of its own, and `rustc`'s own parser runs out of stack
237/// somewhere past four hundred of those — which is what the same figure in
238/// [`sema`](crate::sema) is for. Past this, the [state machine](crate::cfg),
239/// whose `match` is flat however many arms it has, is the answer.
240const MAX_NESTING: usize = 200;
241
242/// How deeply the shapes of `seq` nest, given that they already stand `depth`
243/// blocks deep.
244///
245/// It stops counting past [`MAX_NESTING`], which is also what bounds its own
246/// recursion — and the recursion of everything that walks a plan that passed
247/// the check, [`codegen`](crate::codegen) included.
248fn nesting(seq: &Seq, depth: usize) -> usize {
249    if depth > MAX_NESTING {
250        return depth;
251    }
252    let last = seq.len().saturating_sub(1);
253    let mut out = depth;
254    for (index, shape) in seq.iter().enumerate() {
255        // Everything but the last shape stands inside one labelled block per
256        // shape that follows it.
257        let here = depth + (last - index);
258        out = out.max(match shape {
259            Shape::Simple { arms, .. } | Shape::Dispatch { arms, .. } => arms
260                .iter()
261                .map(|arm| nesting(&arm.body, here + 1))
262                .max()
263                .unwrap_or(here),
264            Shape::Loop { body, .. } => nesting(body, here + 1),
265        });
266        if out > MAX_NESTING {
267            return out;
268        }
269    }
270    out
271}
272
273// ---------------------------------------------------------------------------
274// the recursion
275// ---------------------------------------------------------------------------
276
277struct Relooper<'a> {
278    blocks: &'a [BasicBlock],
279    names: &'a HashMap<BlockId, String>,
280    succ: Vec<Vec<BlockId>>,
281    preds: Vec<Vec<BlockId>>,
282    /// The back edges a loop has taken out of the graph.
283    ///
284    /// A jump from inside a loop to its head is a `continue`, not an edge the
285    /// shapes inside it have to follow; hiding it is what lets the body be
286    /// relooped as though its heads were entered from outside only, and is
287    /// what turns an irreducible region into a dispatch over its heads.
288    cut: HashSet<(BlockId, BlockId)>,
289    states: u32,
290    loops: u32,
291}
292
293impl<'a> Relooper<'a> {
294    fn new(blocks: &'a [BasicBlock], names: &'a HashMap<BlockId, String>) -> Self {
295        let succ: Vec<Vec<BlockId>> = blocks
296            .iter()
297            .map(|block| {
298                let mut out = block.term.successors();
299                out.sort_unstable();
300                out.dedup();
301                out
302            })
303            .collect();
304        let mut preds: Vec<Vec<BlockId>> = vec![Vec::new(); blocks.len()];
305        for (index, targets) in succ.iter().enumerate() {
306            for target in targets {
307                preds[target.0 as usize].push(BlockId(index as u32));
308            }
309        }
310        Self {
311            blocks,
312            names,
313            succ,
314            preds,
315            cut: HashSet::new(),
316            states: 0,
317            loops: 0,
318        }
319    }
320
321    /// The successors of `block` that are still in `set` and whose edge is
322    /// still in the graph.
323    fn succ_in(&self, block: BlockId, set: &Set) -> Vec<BlockId> {
324        self.succ[block.0 as usize]
325            .iter()
326            .copied()
327            .filter(|target| set.contains(target) && !self.cut.contains(&(block, *target)))
328            .collect()
329    }
330
331    /// Whether anything still in `set` jumps to `block`.
332    fn has_pred_in(&self, block: BlockId, set: &Set) -> bool {
333        self.preds[block.0 as usize]
334            .iter()
335            .any(|from| set.contains(from) && !self.cut.contains(&(*from, block)))
336    }
337
338    /// The shapes of `blocks`, which control enters at one of `entries`.
339    ///
340    /// What each shape leaves behind is the next turn of the loop below rather
341    /// than a recursive call: a run of straight-line blocks is one Simple after
342    /// another, and a function with a thousand of them would otherwise be a
343    /// thousand frames deep, each holding a copy of the block set.
344    fn process(&mut self, entries: &Set, blocks: &Set) -> Seq {
345        let mut out = Seq::new();
346        let mut entries = entries.clone();
347        let mut blocks = blocks.clone();
348        loop {
349            entries.retain(|block| blocks.contains(block));
350            if entries.is_empty() {
351                return out;
352            }
353            entries = if entries.len() == 1 {
354                let entry = *entries.iter().next().expect("one entry");
355                if self.has_pred_in(entry, &blocks) {
356                    self.make_loop(&entries, &mut blocks, &mut out)
357                } else {
358                    self.make_simple(entry, &mut blocks, &mut out)
359                }
360            } else {
361                // Several entries: they may be the arms of a branch, which is a
362                // Multiple, or the heads of one tangle, which is a Loop. The
363                // Multiple is tried first — an `if` whose body is a `while` has
364                // two entries and a back edge to one of them, and is an `if`
365                // around a loop rather than a loop with a dispatch at its head.
366                match self.make_multiple(&entries, &mut blocks, &mut out) {
367                    Some(next) => next,
368                    None => self.make_loop(&entries, &mut blocks, &mut out),
369                }
370            };
371        }
372    }
373
374    /// One block, and what its terminator reaches.
375    fn make_simple(&mut self, entry: BlockId, blocks: &mut Set, out: &mut Seq) -> Set {
376        blocks.remove(&entry);
377        let targets: Set = self.succ_in(entry, blocks).into_iter().collect();
378        // The blocks only this terminator can reach go inside the `if` or the
379        // `match` it becomes, which is what keeps a branch a branch.
380        if self.fusable(entry, &targets)
381            && let Some((arms, next)) = self.groups(&targets, blocks)
382        {
383            out.push(Shape::Simple { block: entry, arms });
384            return next;
385        }
386        out.push(Shape::Simple {
387            block: entry,
388            arms: Vec::new(),
389        });
390        targets
391    }
392
393    /// Whether the arms of `entry`'s terminator may hold the code of the blocks
394    /// they enter.
395    ///
396    /// They may not when one block is the target of two of them — a `switch`
397    /// whose `default` is also a `case`, say — because the code would then be
398    /// generated twice.
399    fn fusable(&self, entry: BlockId, targets: &Set) -> bool {
400        if targets.len() < 2 {
401            return false;
402        }
403        let listed = self.terminator_targets(entry);
404        targets
405            .iter()
406            .all(|target| listed.iter().filter(|other| *other == target).count() <= 1)
407    }
408
409    /// The blocks a terminator names, with the repeats a `match` would have to
410    /// generate twice.
411    fn terminator_targets(&self, block: BlockId) -> Vec<BlockId> {
412        match &self.blocks[block.0 as usize].term {
413            Terminator::Switch { cases, default, .. } => {
414                let mut out: Vec<BlockId> = Vec::new();
415                for (_, target) in cases {
416                    if !out.contains(target) {
417                        // Two `case` labels on one block share an arm.
418                        out.push(*target);
419                    }
420                }
421                out.push(*default);
422                out
423            }
424            other => other.successors(),
425        }
426    }
427
428    /// Lays the entries out one after another, each with the blocks only it can
429    /// reach.
430    ///
431    /// A group jumps forwards to whatever follows the last of them, which is a
432    /// `break` of the labelled block it stands in — so laying them out flat,
433    /// rather than nesting each one, is all a Multiple is here.
434    fn make_multiple(&mut self, entries: &Set, blocks: &mut Set, out: &mut Seq) -> Option<Set> {
435        let (arms, next) = self.groups(entries, blocks)?;
436        for arm in arms {
437            out.extend(arm.body);
438        }
439        Some(next)
440    }
441
442    /// The groups of a Multiple and the entries left for what follows them, or
443    /// `None` when every entry is reachable from another and nothing can be
444    /// split off. `blocks` is left holding what the groups did not take.
445    fn groups(&mut self, entries: &Set, blocks: &mut Set) -> Option<(Vec<Arm>, Set)> {
446        let owned = self.independent_groups(entries, blocks);
447        if owned.is_empty() {
448            return None;
449        }
450        let mut consumed = Set::new();
451        for (_, group) in &owned {
452            consumed.extend(group.iter().copied());
453        }
454        for block in &consumed {
455            blocks.remove(block);
456        }
457        let mut next: Set = entries
458            .iter()
459            .copied()
460            .filter(|entry| !consumed.contains(entry))
461            .collect();
462        for block in &consumed {
463            next.extend(self.succ_in(*block, blocks));
464        }
465        let arms: Vec<Arm> = owned
466            .into_iter()
467            .map(|(entry, group)| {
468                let one: Set = [entry].into_iter().collect();
469                Arm {
470                    entry,
471                    body: self.process(&one, &group),
472                }
473            })
474            .collect();
475        Some((arms, next))
476    }
477
478    /// For each entry, the blocks *only* that entry can reach.
479    ///
480    /// Such a group is closed: every jump into it from inside `blocks` comes
481    /// from the group itself or from its entry, because anything else would be
482    /// a second entry the block is reachable from. That is what lets the group
483    /// be emitted on its own — inside a `match` arm, or as one run of a
484    /// sequence. An entry another entry can reach owns nothing at all and is
485    /// left for the shapes that follow.
486    fn independent_groups(&self, entries: &Set, blocks: &Set) -> Vec<(BlockId, Set)> {
487        let count = self.blocks.len();
488        let mut owner: Vec<Option<BlockId>> = vec![None; count];
489        let mut shared = vec![false; count];
490        for entry in entries {
491            owner[entry.0 as usize] = Some(*entry);
492        }
493        let mut seen = vec![false; count];
494        let mut stack: Vec<BlockId> = Vec::new();
495        for entry in entries {
496            seen.iter_mut().for_each(|flag| *flag = false);
497            stack.clear();
498            stack.extend(self.succ_in(*entry, blocks));
499            for target in &stack {
500                seen[target.0 as usize] = true;
501            }
502            while let Some(block) = stack.pop() {
503                match owner[block.0 as usize] {
504                    None => owner[block.0 as usize] = Some(*entry),
505                    Some(other) if other != *entry => shared[block.0 as usize] = true,
506                    Some(_) => {}
507                }
508                for target in self.succ_in(block, blocks) {
509                    if !seen[target.0 as usize] {
510                        seen[target.0 as usize] = true;
511                        stack.push(target);
512                    }
513                }
514            }
515        }
516        let mut out = Vec::new();
517        for entry in entries {
518            if shared[entry.0 as usize] {
519                continue;
520            }
521            let group: Set = blocks
522                .iter()
523                .copied()
524                .filter(|block| {
525                    owner[block.0 as usize] == Some(*entry) && !shared[block.0 as usize]
526                })
527                .collect();
528            out.push((*entry, group));
529        }
530        out
531    }
532
533    /// A loop over the entries and everything that can get back to one of them.
534    fn make_loop(&mut self, entries: &Set, blocks: &mut Set, out: &mut Seq) -> Set {
535        let mut inner = entries.clone();
536        inner.extend(self.reaching(entries, blocks));
537        // One way out needs no labelled block around the loop, so there is
538        // nothing to gain by widening it; more than one is where it pays.
539        if self.leaving(blocks, &inner).len() > 1 {
540            self.widen_loop(blocks, &mut inner);
541        }
542        let follow = self.leaving(blocks, &inner);
543        for block in &inner {
544            blocks.remove(block);
545        }
546        // Take the back edges out: inside the body they are `continue`s.
547        for entry in entries {
548            for from in self.preds[entry.0 as usize].clone() {
549                if inner.contains(&from) {
550                    self.cut.insert((from, *entry));
551                }
552            }
553        }
554        let id = self.loops;
555        self.loops += 1;
556        let name = entries
557            .iter()
558            .find_map(|entry| self.names.get(entry))
559            .cloned();
560        let (state, entry_list, body) = if entries.len() == 1 {
561            let body = self.process(entries, &inner);
562            (None, entries.iter().copied().collect(), body)
563        } else {
564            // Every head now stands on its own — nothing inside jumps to one
565            // any more — so the body splits into one group per head, and the
566            // state variable is what says which one this turn runs.
567            let state = self.states;
568            self.states += 1;
569            let mut rest = inner.clone();
570            match self.groups(entries, &mut rest) {
571                Some((arms, next)) => {
572                    let list: Vec<BlockId> = arms.iter().map(|arm| arm.entry).collect();
573                    let mut body = vec![Shape::Dispatch { state, arms }];
574                    body.extend(self.process(&next, &rest));
575                    (Some(state), list, body)
576                }
577                // Cannot happen: cutting the back edges leaves every head
578                // without a predecessor inside. The check in `plan` turns it
579                // into the state machine rather than into wrong code.
580                None => (Some(state), entries.iter().copied().collect(), Seq::new()),
581            }
582        };
583        out.push(Shape::Loop {
584            id,
585            name,
586            entries: entry_list,
587            state,
588            body,
589        });
590        follow
591    }
592
593    /// Pulls the runs that leave the loop back into its body.
594    ///
595    /// Only the entries and what can get back to them *have* to be inside: a
596    /// `case` that ends in `goto fail` cannot reach the head, so the rule above
597    /// leaves it after the loop, and then everything before it has to stand
598    /// inside a labelled block so that the dispatch can break to it. A block
599    /// only one thing jumps to is not a place anything has to break to, so
600    /// following those chains in costs nothing and is what keeps the body of a
601    /// `switch` inside the `switch` — the difference between a `match` arm and
602    /// a labelled block per `case`. (`c2rust` calls this `heuristic_loop_body`;
603    /// the idea is the same.)
604    fn widen_loop(&self, blocks: &Set, inner: &mut Set) {
605        let mut queue: Vec<BlockId> = self.leaving(blocks, inner).into_iter().collect();
606        while let Some(mut block) = queue.pop() {
607            loop {
608                if inner.contains(&block) {
609                    break;
610                }
611                let entered = self.preds[block.0 as usize]
612                    .iter()
613                    .filter(|from| blocks.contains(from) && !self.cut.contains(&(**from, block)))
614                    .count();
615                if entered != 1 {
616                    break;
617                }
618                inner.insert(block);
619                let targets = self.succ_in(block, blocks);
620                let Some((next, rest)) = targets.split_first() else {
621                    break;
622                };
623                queue.extend(rest.iter().copied());
624                block = *next;
625            }
626        }
627    }
628
629    /// The blocks of `blocks` that `part` jumps to from the outside of `part`.
630    fn leaving(&self, blocks: &Set, part: &Set) -> Set {
631        let mut out = Set::new();
632        for block in part {
633            for target in self.succ_in(*block, blocks) {
634                if !part.contains(&target) {
635                    out.insert(target);
636                }
637            }
638        }
639        out
640    }
641
642    /// The blocks of `set` that can get to one of `targets` without leaving it.
643    fn reaching(&self, targets: &Set, set: &Set) -> Set {
644        let mut seen = vec![false; self.blocks.len()];
645        let mut out = Set::new();
646        let mut stack: Vec<BlockId> = targets.iter().copied().collect();
647        let mut todo: Vec<BlockId> = Vec::new();
648        while let Some(block) = stack.pop() {
649            for from in &self.preds[block.0 as usize] {
650                if !set.contains(from)
651                    || self.cut.contains(&(*from, block))
652                    || seen[from.0 as usize]
653                {
654                    continue;
655                }
656                seen[from.0 as usize] = true;
657                out.insert(*from);
658                todo.push(*from);
659            }
660            stack.append(&mut todo);
661        }
662        out
663    }
664}
665
666// ---------------------------------------------------------------------------
667// the checks
668// ---------------------------------------------------------------------------
669
670/// How many times each block stands in the tree.
671fn count_blocks(seq: &Seq, out: &mut [usize]) {
672    for shape in seq {
673        match shape {
674            Shape::Simple { block, arms } => {
675                out[block.0 as usize] += 1;
676                for arm in arms {
677                    count_blocks(&arm.body, out);
678                }
679            }
680            Shape::Loop { body, .. } => count_blocks(body, out),
681            Shape::Dispatch { arms, .. } => {
682                for arm in arms {
683                    count_blocks(&arm.body, out);
684                }
685            }
686        }
687    }
688}
689
690/// Whether every jump the tree still holds has somewhere to go.
691///
692/// It walks exactly as [`codegen`](crate::codegen) emits: the shapes of a
693/// sequence in order, with a labelled block open around everything before each
694/// of them, and a loop's head and follow reachable from inside it. A `false`
695/// would be a bug in this module, and answering it is what keeps such a bug
696/// from becoming generated code that jumps to the wrong place.
697fn resolves(blocks: &[BasicBlock], seq: &Seq, fall: &Exit, scopes: &mut Vec<Exit>) -> bool {
698    let exits: Vec<Exit> = seq.iter().map(Shape::exit).collect();
699    let depth = scopes.len();
700    for exit in exits.iter().skip(1).rev() {
701        scopes.push(exit.clone());
702    }
703    let mut ok = true;
704    for (index, shape) in seq.iter().enumerate() {
705        if index > 0 {
706            scopes.pop();
707        }
708        let next = exits.get(index + 1).unwrap_or(fall);
709        ok &= resolves_shape(blocks, shape, next, scopes);
710    }
711    scopes.truncate(depth);
712    ok
713}
714
715fn resolves_shape(
716    blocks: &[BasicBlock],
717    shape: &Shape,
718    fall: &Exit,
719    scopes: &mut Vec<Exit>,
720) -> bool {
721    match shape {
722        Shape::Simple { block, arms } => {
723            let mut ok = true;
724            for target in blocks[block.0 as usize].term.successors() {
725                match arms.iter().find(|arm| arm.entry == target) {
726                    Some(arm) => ok &= resolves(blocks, &arm.body, fall, scopes),
727                    None => {
728                        ok &= fall.index_of(target).is_some()
729                            || scopes.iter().any(|exit| exit.index_of(target).is_some());
730                    }
731                }
732            }
733            ok
734        }
735        Shape::Loop {
736            entries,
737            state,
738            body,
739            ..
740        } => {
741            let head = Exit {
742                targets: entries.clone(),
743                state: *state,
744            };
745            scopes.push(head.clone());
746            scopes.push(fall.clone());
747            let ok = resolves(blocks, body, &head, scopes);
748            scopes.pop();
749            scopes.pop();
750            ok
751        }
752        Shape::Dispatch { arms, .. } => arms
753            .iter()
754            .all(|arm| resolves(blocks, &arm.body, fall, scopes)),
755    }
756}