cairo_lang_lowering/analysis/
backward.rs1use std::collections::HashMap;
5
6use crate::analysis::Analyzer;
7use crate::{BlockEnd, BlockId, Lowered};
8
9pub struct BackAnalysis<'db, 'a, TAnalyzer: Analyzer<'db, 'a>> {
11 lowered: &'a Lowered<'db>,
12 pub analyzer: TAnalyzer,
13 block_info: HashMap<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 { lowered, analyzer, block_info: Default::default() }
19 }
20 pub fn get_root_info(&mut self) -> TAnalyzer::Info {
22 let mut dfs_stack = vec![BlockId::root()];
23 while let Some(block_id) = dfs_stack.last() {
24 let end = &self.lowered.blocks[*block_id].end;
25 if !self.add_missing_dependency_blocks(&mut dfs_stack, end) {
26 self.calc_block_info(dfs_stack.pop().unwrap());
27 }
28 }
29 self.block_info.remove(&BlockId::root()).unwrap()
30 }
31
32 fn calc_block_info(&mut self, block_id: BlockId) {
34 let mut info = self.get_end_info(block_id);
35
36 for (i, stmt) in self.lowered.blocks[block_id].statements.iter().enumerate().rev() {
38 let statement_location = (block_id, i);
39 self.analyzer.visit_stmt(&mut info, statement_location, stmt);
40 }
41
42 self.analyzer.visit_block_start(&mut info, block_id, &self.lowered.blocks[block_id]);
43
44 self.block_info.insert(block_id, info);
46 }
47
48 fn add_missing_dependency_blocks(
51 &self,
52 dfs_stack: &mut Vec<BlockId>,
53 block_end: &'a BlockEnd<'_>,
54 ) -> bool {
55 match block_end {
56 BlockEnd::NotSet => unreachable!(),
57 BlockEnd::Goto(target_block_id, _)
58 if !self.block_info.contains_key(target_block_id) =>
59 {
60 dfs_stack.push(*target_block_id);
61 true
62 }
63 BlockEnd::Goto(_, _) | BlockEnd::Return(..) | BlockEnd::Panic(_) => false,
64 BlockEnd::Match { info } => {
65 let mut missing_cache = false;
66 for arm in info.arms() {
67 if !self.block_info.contains_key(&arm.block_id) {
68 dfs_stack.push(arm.block_id);
69 missing_cache = true;
70 }
71 }
72 missing_cache
73 }
74 }
75 }
76
77 fn get_end_info(&mut self, block_id: BlockId) -> TAnalyzer::Info {
79 let block_end = &self.lowered.blocks[block_id].end;
80 let statement_location = (block_id, self.lowered.blocks[block_id].statements.len());
81 match block_end {
82 BlockEnd::NotSet => unreachable!(),
83 BlockEnd::Goto(target_block_id, remapping) => {
84 let mut info = self.block_info[target_block_id].clone();
85 self.analyzer.visit_goto(
86 &mut info,
87 statement_location,
88 *target_block_id,
89 remapping,
90 );
91 info
92 }
93 BlockEnd::Return(vars, _location) => {
94 self.analyzer.info_from_return(statement_location, vars)
95 }
96 BlockEnd::Panic(data) => self.analyzer.info_from_panic(statement_location, data),
97 BlockEnd::Match { info } => {
98 let arm_infos =
100 info.arms().iter().map(|arm| self.block_info.remove(&arm.block_id).unwrap());
101 self.analyzer.merge_match(statement_location, info, arm_infos)
102 }
103 }
104 }
105}