charon_lib/transform/
merge_goto_chains.rs1use crate::ids::Vector;
3use crate::transform::TransformCtx;
4use crate::ullbc_ast::*;
5
6use super::ctx::UllbcPass;
7
8enum Antecedents {
11 Zero,
12 One {
13 id: BlockId,
14 is_goto: bool,
16 },
17 Many,
18}
19
20impl Antecedents {
21 fn add(&mut self, id: BlockId, is_goto: bool) {
22 match self {
23 Antecedents::Zero => *self = Antecedents::One { id, is_goto },
24 Antecedents::One { .. } => *self = Antecedents::Many,
25 Antecedents::Many => {}
26 }
27 }
28}
29
30pub struct Transform;
31impl UllbcPass for Transform {
32 fn transform_body(&self, ctx: &mut TransformCtx, body: &mut ExprBody) {
33 if ctx.options.no_merge_goto_chains {
35 return;
36 }
37
38 let mut antecedents: Vector<BlockId, Antecedents> =
40 body.body.map_ref(|_| Antecedents::Zero);
41 for (block_id, block) in body.body.iter_indexed() {
42 let is_goto = block.terminator.content.is_goto();
43 for target in block.targets() {
44 antecedents.get_mut(target).unwrap().add(block_id, is_goto);
45 }
46 }
47 for mut id in body.body.all_indices() {
49 while let Antecedents::One {
53 id: antecedent_id,
54 is_goto: true,
55 } = antecedents[id]
56 {
57 id = antecedent_id;
58 }
59 while let Some(source) = body.body.get(id)
62 && let RawTerminator::Goto { target } = source.terminator.content
63 && let Antecedents::One { .. } = antecedents[target]
64 {
65 let mut target = body.body.remove(target).unwrap();
66 let source = &mut body.body[id];
67 source.statements.append(&mut target.statements);
68 source.terminator = target.terminator;
69 }
70 }
71 }
72}