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