Skip to main content

qcode_passes/
terminator.rs

1//! Terminator rewriting shared by the CFG and DCE transforms.
2
3use qcode::value::{
4    BlockId, FunctionBody, QCodeView, ValueId,
5    insn::{Branch, Mnemonic},
6    util::base_ref::BaseRef,
7};
8
9use crate::PassCtx;
10
11pub fn replace_terminator_with_branch<'a, 'str>(
12    body: &'a mut FunctionBody<'str>,
13    cx: PassCtx<'a, 'str>,
14    block: BlockId,
15    target: BlockId,
16    args: Vec<ValueId>,
17) {
18    let mut old_successors = cx
19        .body_view(body)
20        .block_ref(block)
21        .successors()
22        .map(|(edge, _)| edge)
23        .collect::<Vec<_>>();
24    old_successors.sort_unstable();
25    old_successors.dedup();
26    for edge in old_successors {
27        body.remove_cfg_edge(edge);
28    }
29
30    // Reuse the existing terminator only if the block actually ends in one. The
31    // freshly-created unrolled blocks hold only copied *body* instructions (no
32    // terminator yet); their last instruction is a real value (e.g. the induction
33    // increment), which must not be clobbered into the branch — doing so destroys
34    // that value and, when it is the exit argument, yields a branch that passes
35    // itself. In that case append the branch instead.
36    let term_id = cx
37        .body_view(body)
38        .block_ref(block)
39        .instruction_ids()
40        .last()
41        .copied()
42        .filter(|&id| cx.body_view(body).insn_ref(id).mnemonic().is_terminator());
43    let local_target = target.localize(block.func);
44    let args: Vec<_> = args
45        .into_iter()
46        .map(|arg| arg.localize(block.func))
47        .collect();
48    if let Some(term_id) = term_id {
49        body.replace_instruction_mnemonic(
50            term_id,
51            Mnemonic::Branch(Branch {
52                target: local_target,
53                args,
54            }),
55        );
56    } else {
57        let branch = body.push_mnemonic(
58            cx.shr(),
59            Mnemonic::Branch(Branch {
60                target: local_target,
61                args,
62            }),
63            0,
64        );
65        let end = cx.body_view(body).block_ref(block).instruction_ids().len();
66        {
67            // TODO(5b-ii): `BaseRef::insert_insn_at_index` is not mirrored on
68            // `FunctionBody`; go through a temporary host.
69            let mut host = cx.host(body);
70            BaseRef::new(host.reborrow(), block).insert_insn_at_index(end, branch);
71        }
72    }
73    body.add_cfg_edge(block, target);
74}