cairo_lang_lowering/
reorganize_blocks.rs1use std::collections::HashMap;
2
3use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
4use id_arena::Arena;
5use itertools::Itertools;
6
7use crate::blocks::FlatBlocksBuilder;
8use crate::borrow_check::analysis::{Analyzer, BackAnalysis, StatementLocation};
9use crate::optimizations::remappings;
10use crate::utils::{Rebuilder, RebuilderEx};
11use crate::{
12 BlockId, FlatBlock, FlatBlockEnd, FlatLowered, MatchInfo, Statement, VarRemapping, VarUsage,
13 Variable, VariableId,
14};
15
16pub fn reorganize_blocks(lowered: &mut FlatLowered) {
22 if lowered.blocks.is_empty() {
23 return;
24 }
25 let mut ctx = TopSortContext {
26 old_block_rev_order: Default::default(),
27 incoming_gotos: vec![0; lowered.blocks.len()],
28 can_be_merged: vec![true; lowered.blocks.len()],
29 remappings_ctx: Default::default(),
30 };
31
32 remappings::visit_remappings(lowered, |remapping| {
33 for (dst, src) in remapping.iter() {
34 ctx.remappings_ctx.dest_to_srcs.entry(*dst).or_default().push(src.var_id);
35 }
36 });
37
38 let mut analysis = BackAnalysis::new(lowered, ctx);
39 analysis.get_root_info();
40 let ctx = analysis.analyzer;
41
42 let mut new_blocks = FlatBlocksBuilder::default();
44
45 let mut old_block_rev_order = ctx
50 .old_block_rev_order
51 .into_iter()
52 .filter(|block_id| !ctx.can_be_merged[block_id.0] || ctx.incoming_gotos[block_id.0] > 1)
53 .collect_vec();
54
55 old_block_rev_order.push(BlockId::root());
57
58 let n_visited_blocks = old_block_rev_order.len();
59
60 let mut rebuilder = RebuildContext {
61 block_remapping: HashMap::from_iter(
62 old_block_rev_order
63 .iter()
64 .enumerate()
65 .map(|(idx, block_id)| (*block_id, BlockId(n_visited_blocks - idx - 1))),
66 ),
67 remappings_ctx: ctx.remappings_ctx,
68 };
69
70 let mut var_reassigner = VarReassigner::new(&lowered.variables);
71 for param in lowered.parameters.iter_mut() {
72 *param = var_reassigner.map_var_id(*param);
73 }
74
75 for block_id in old_block_rev_order.into_iter().rev() {
76 let mut statements = vec![];
77
78 let mut block = &lowered.blocks[block_id];
79 loop {
80 for stmt in &block.statements {
81 statements
82 .push(var_reassigner.rebuild_statement(&rebuilder.rebuild_statement(stmt)));
83 }
84 if let FlatBlockEnd::Goto(target_block_id, remappings) = &block.end {
85 if !rebuilder.block_remapping.contains_key(target_block_id) {
86 assert!(
87 rebuilder.rebuild_remapping(remappings).is_empty(),
88 "Remapping should be empty."
89 );
90 block = &lowered.blocks[*target_block_id];
91 continue;
92 }
93 }
94 break;
95 }
96
97 let end = var_reassigner.rebuild_end(&rebuilder.rebuild_end(&block.end));
98 new_blocks.alloc(FlatBlock { statements, end });
99 }
100
101 lowered.variables = var_reassigner.new_vars;
102 lowered.blocks = new_blocks.build().unwrap();
103}
104
105pub struct TopSortContext {
106 old_block_rev_order: Vec<BlockId>,
107 incoming_gotos: Vec<usize>,
109
110 can_be_merged: Vec<bool>,
112
113 remappings_ctx: remappings::Context,
114}
115
116impl Analyzer<'_> for TopSortContext {
117 type Info = ();
118
119 fn visit_block_start(&mut self, _info: &mut Self::Info, block_id: BlockId, _block: &FlatBlock) {
120 self.old_block_rev_order.push(block_id);
121 }
122
123 fn visit_stmt(
124 &mut self,
125 _info: &mut Self::Info,
126 _statement_location: StatementLocation,
127 stmt: &Statement,
128 ) {
129 for var_usage in stmt.inputs() {
130 self.remappings_ctx.set_used(var_usage.var_id);
131 }
132 }
133
134 fn visit_goto(
135 &mut self,
136 _info: &mut Self::Info,
137 _statement_location: StatementLocation,
138 target_block_id: BlockId,
139 _remapping: &VarRemapping,
142 ) {
143 self.incoming_gotos[target_block_id.0] += 1;
144 }
145
146 fn merge_match(
147 &mut self,
148 _statement_location: StatementLocation,
149 match_info: &MatchInfo,
150 _infos: impl Iterator<Item = Self::Info>,
151 ) -> Self::Info {
152 for var_usage in match_info.inputs() {
153 self.remappings_ctx.set_used(var_usage.var_id);
154 }
155
156 for arm in match_info.arms().iter() {
157 self.can_be_merged[arm.block_id.0] = false;
158 }
159 }
160
161 fn info_from_return(
162 &mut self,
163 _statement_location: StatementLocation,
164 vars: &[VarUsage],
165 ) -> Self::Info {
166 for var_usage in vars {
167 self.remappings_ctx.set_used(var_usage.var_id);
168 }
169 }
170
171 fn info_from_panic(
172 &mut self,
173 _statement_location: StatementLocation,
174 data: &VarUsage,
175 ) -> Self::Info {
176 self.remappings_ctx.set_used(data.var_id);
177 }
178}
179
180pub struct RebuildContext {
181 block_remapping: HashMap<BlockId, BlockId>,
182 remappings_ctx: remappings::Context,
183}
184impl Rebuilder for RebuildContext {
185 fn map_block_id(&mut self, block: BlockId) -> BlockId {
186 self.block_remapping[&block]
187 }
188
189 fn map_var_id(&mut self, var: VariableId) -> VariableId {
190 self.remappings_ctx.map_var_id(var)
191 }
192
193 fn transform_remapping(&mut self, remapping: &mut VarRemapping) {
194 self.remappings_ctx.transform_remapping(remapping)
195 }
196}
197
198pub struct VarReassigner<'a> {
203 pub old_vars: &'a Arena<Variable>,
204 pub new_vars: Arena<Variable>,
205
206 pub vars: UnorderedHashMap<VariableId, VariableId>,
208}
209
210impl<'a> VarReassigner<'a> {
211 pub fn new(old_vars: &'a Arena<Variable>) -> Self {
212 Self { old_vars, new_vars: Default::default(), vars: UnorderedHashMap::default() }
213 }
214}
215
216impl Rebuilder for VarReassigner<'_> {
217 fn map_var_id(&mut self, var: VariableId) -> VariableId {
218 *self.vars.entry(var).or_insert_with(|| self.new_vars.alloc(self.old_vars[var].clone()))
219 }
220}