cairo_lang_lowering/analysis/
backward.rs1use cairo_lang_utils::unordered_hash_map::UnorderedHashMap;
5
6use crate::analysis::{Analyzer, DataflowAnalyzer, Direction, Edge, StatementLocation};
7use crate::{Block, BlockEnd, BlockId, Lowered, MatchInfo, Statement, VarRemapping, VarUsage};
8
9pub struct BackAnalysis<'db, 'a, TAnalyzer: Analyzer<'db, 'a>> {
11 lowered: &'a Lowered<'db>,
12 pub analyzer: TAnalyzer,
13 block_info: UnorderedHashMap<BlockId, TAnalyzer::Info>,
14}
15impl<'db, 'a, TAnalyzer: Analyzer<'db, 'a>> BackAnalysis<'db, 'a, TAnalyzer> {
16 pub fn new(lowered: &'a Lowered<'db>, analyzer: TAnalyzer) -> Self {
18 Self {
19 lowered,
20 analyzer,
21 block_info: UnorderedHashMap::with_capacity(lowered.blocks.len()),
22 }
23 }
24 pub fn get_root_info(&mut self) -> TAnalyzer::Info {
26 let mut dfs_stack = Vec::with_capacity(self.lowered.blocks.len());
27 dfs_stack.push(BlockId::root());
28 while let Some(block_id) = dfs_stack.last() {
29 let end = &self.lowered.blocks[*block_id].end;
30 if !self.add_missing_dependency_blocks(&mut dfs_stack, end) {
31 self.calc_block_info(dfs_stack.pop().unwrap());
32 }
33 }
34 self.block_info.remove(&BlockId::root()).unwrap()
35 }
36
37 fn calc_block_info(&mut self, block_id: BlockId) {
39 let mut info = self.get_end_info(block_id);
40
41 for (i, stmt) in self.lowered.blocks[block_id].statements.iter().enumerate().rev() {
43 let statement_location = (block_id, i);
44 self.analyzer.visit_stmt(&mut info, statement_location, stmt);
45 }
46
47 self.analyzer.visit_block_start(&mut info, block_id, &self.lowered.blocks[block_id]);
48
49 self.block_info.insert(block_id, info);
51 }
52
53 fn add_missing_dependency_blocks(
56 &self,
57 dfs_stack: &mut Vec<BlockId>,
58 block_end: &'a BlockEnd<'_>,
59 ) -> bool {
60 match block_end {
61 BlockEnd::NotSet => unreachable!(),
62 BlockEnd::Goto(target_block_id, _)
63 if !self.block_info.contains_key(target_block_id) =>
64 {
65 dfs_stack.push(*target_block_id);
66 true
67 }
68 BlockEnd::Goto(_, _) | BlockEnd::Return(..) | BlockEnd::Panic(_) => false,
69 BlockEnd::Match { info } => {
70 let mut missing_cache = false;
71 for arm in info.arms() {
72 if !self.block_info.contains_key(&arm.block_id) {
73 dfs_stack.push(arm.block_id);
74 missing_cache = true;
75 }
76 }
77 missing_cache
78 }
79 }
80 }
81
82 fn get_end_info(&mut self, block_id: BlockId) -> TAnalyzer::Info {
84 let block_end = &self.lowered.blocks[block_id].end;
85 let statement_location = (block_id, self.lowered.blocks[block_id].statements.len());
86 match block_end {
87 BlockEnd::NotSet => unreachable!(),
88 BlockEnd::Goto(target_block_id, remapping) => {
89 let mut info = self.block_info[target_block_id].clone();
90 self.analyzer.visit_goto(
91 &mut info,
92 statement_location,
93 *target_block_id,
94 remapping,
95 );
96 info
97 }
98 BlockEnd::Return(vars, _location) => {
99 self.analyzer.info_from_return(statement_location, vars)
100 }
101 BlockEnd::Panic(data) => self.analyzer.info_from_panic(statement_location, data),
102 BlockEnd::Match { info } => {
103 let arm_infos =
105 info.arms().iter().map(|arm| self.block_info.remove(&arm.block_id).unwrap());
106 self.analyzer.merge_match(statement_location, info, arm_infos)
107 }
108 }
109 }
110}
111
112pub struct DataflowBackAnalysis<'db, 'a, TAnalyzer: DataflowAnalyzer<'db, 'a>> {
119 inner: BackAnalysis<'db, 'a, AnalyzerAdapter<'db, 'a, TAnalyzer>>,
120}
121
122impl<'db, 'a, TAnalyzer: DataflowAnalyzer<'db, 'a>> DataflowBackAnalysis<'db, 'a, TAnalyzer> {
123 pub fn new(lowered: &'a Lowered<'db>, analyzer: &'a mut TAnalyzer) -> Self {
125 assert!(
126 TAnalyzer::DIRECTION == Direction::Backward,
127 "DataflowBackAnalysis requires a backward analyzer"
128 );
129 let adapter = AnalyzerAdapter { analyzer, lowered };
130 Self { inner: BackAnalysis::new(lowered, adapter) }
131 }
132
133 pub fn run(mut self) -> TAnalyzer::Info {
137 self.inner.get_root_info()
138 }
139}
140
141pub struct AnalyzerAdapter<'db, 'a, TAnalyzer: DataflowAnalyzer<'db, 'a>> {
143 pub analyzer: &'a mut TAnalyzer,
144 lowered: &'a Lowered<'db>,
145}
146
147impl<'db, 'a, TAnalyzer: DataflowAnalyzer<'db, 'a>> Analyzer<'db, 'a>
148 for AnalyzerAdapter<'db, 'a, TAnalyzer>
149{
150 type Info = TAnalyzer::Info;
151
152 fn visit_block_start(&mut self, info: &mut Self::Info, block_id: BlockId, _block: &Block<'db>) {
153 let block = &self.lowered.blocks[block_id];
155 self.analyzer.transfer_block(info, block_id, block);
157 self.analyzer.visit_block_start(info, block_id, block);
159 }
160
161 fn visit_stmt(
162 &mut self,
163 _info: &mut Self::Info,
164 _statement_location: StatementLocation,
165 _stmt: &'a Statement<'db>,
166 ) {
167 }
170
171 fn visit_goto(
172 &mut self,
173 info: &mut Self::Info,
174 _statement_location: StatementLocation,
175 target_block_id: BlockId,
176 remapping: &'a VarRemapping<'db>,
177 ) {
178 let edge = Edge::Goto { target: target_block_id, remapping };
179 *info = self.analyzer.transfer_edge(info, &edge);
180 }
181
182 fn merge_match(
183 &mut self,
184 statement_location: StatementLocation,
185 match_info: &'a MatchInfo<'db>,
186 infos: impl Iterator<Item = Self::Info>,
187 ) -> Self::Info {
188 let mut iter = match_info.arms().iter().zip(infos);
190 let transformer = |analyzer: &mut TAnalyzer, info, arm| {
191 let edge = Edge::MatchArm { arm, match_info };
192 analyzer.transfer_edge(&info, &edge)
193 };
194 let Some(first) = iter.next().map(|(arm, info)| transformer(self.analyzer, info, arm))
195 else {
196 return self.analyzer.initial_info(
197 statement_location.0,
198 &self.lowered.blocks[statement_location.0].end,
199 );
200 };
201 iter.fold(first, |a, b| {
202 let b = transformer(self.analyzer, b.1, b.0);
203 self.analyzer.merge(self.lowered, statement_location, a, b)
204 })
205 }
206
207 fn info_from_return(
208 &mut self,
209 statement_location: StatementLocation,
210 vars: &'a [VarUsage<'db>],
211 ) -> Self::Info {
212 let block_end = &self.lowered.blocks[statement_location.0].end;
213 let BlockEnd::Return(_, location) = block_end else {
214 unreachable!("Unexpected block end type")
215 };
216 let location = *location;
217 let info = self.analyzer.initial_info(statement_location.0, block_end);
218 self.analyzer.transfer_edge(&info, &Edge::Return { vars, location })
219 }
220
221 fn info_from_panic(
222 &mut self,
223 statement_location: StatementLocation,
224 var: &VarUsage<'db>,
225 ) -> Self::Info {
226 let block_end = &self.lowered.blocks[statement_location.0].end;
227 let info = self.analyzer.initial_info(statement_location.0, block_end);
228 self.analyzer.transfer_edge(&info, &Edge::Panic { var: *var })
229 }
230}