Skip to content

Commit 8a6f9a7

Browse files
(feat): Creating a dataflow analysis framework and migrating backward analysis
1 parent ac62f4f commit 8a6f9a7

File tree

2 files changed

+214
-2
lines changed

2 files changed

+214
-2
lines changed
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
//! Core dataflow analysis traits.
2+
//!
3+
//! This module provides a unified trait for both forward and backward dataflow analysis.
4+
//!
5+
//! # Design
6+
//!
7+
//! The `DataflowAnalyzer` trait provides a layered API:
8+
//!
9+
//! 1. **Core methods** (must implement): `initial_info`, `merge`
10+
//! 2. **Transfer granularity** (choose one):
11+
//! - Block-level: override `transfer_block` for coarse-grained analysis
12+
//! - Statement-level: override `transfer_stmt` (default `transfer_block` iterates statements)
13+
//! 3. **Variable tracking** (optional): `apply_remapping` - override if tracking per-variable state
14+
//! 4. **Advanced hooks** (optional): `merge_match`, `split_match`, etc. - override for special
15+
//! cases
16+
//!
17+
//! The framework handles control flow mechanics automatically:
18+
//! - Goto with remapping: calls `apply_remapping`
19+
//! - Match (backward): calls `merge_match` which defaults to `merge`
20+
//! - Match (forward): calls `split_match` which defaults to cloning
21+
22+
use crate::{Block, BlockEnd, BlockId, MatchInfo, Statement, VarRemapping};
23+
24+
/// Location of a lowering statement inside a block.
25+
#[allow(dead_code)]
26+
pub type StatementLocation = (BlockId, usize);
27+
28+
/// The direction of dataflow analysis.
29+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30+
#[allow(dead_code)]
31+
pub enum Direction {
32+
Forward,
33+
Backward,
34+
}
35+
36+
/// Unified analyzer trait for dataflow analysis.
37+
///
38+
/// Implementors specify the direction via `DIRECTION` and implement the core methods.
39+
/// The framework handles control flow mechanics (remapping, match arms) automatically,
40+
/// with sensible defaults that can be overridden for complex analyses.
41+
///
42+
/// # Transfer Granularity
43+
///
44+
/// You can work at either block or statement level:
45+
/// - **Block-level**: Override `transfer_block` for coarse-grained analysis
46+
/// - **Statement-level**: Override `transfer_stmt`; default `transfer_block` iterates statements
47+
///
48+
/// # Lifetime parameters
49+
/// - `'db`: The database lifetime (for interned types).
50+
/// - `'a`: The lifetime of borrowed lowering data.
51+
///
52+
/// # Example (statement-level forward analysis)
53+
/// ```ignore
54+
/// impl<'db, 'a> DataflowAnalyzer<'db, 'a> for MyAnalyzer {
55+
/// type Info = HashSet<VariableId>;
56+
/// const DIRECTION: Direction = Direction::Forward;
57+
///
58+
/// fn initial_info(&mut self) -> Self::Info { HashSet::new() }
59+
/// fn transfer_stmt(&mut self, info: &mut Self::Info, ..) { /* update info */ }
60+
/// fn merge(&mut self, infos: impl Iterator<Item = Self::Info>) -> Self::Info {
61+
/// infos.fold(HashSet::new(), |mut acc, i| { acc.extend(i); acc })
62+
/// }
63+
/// }
64+
/// ```
65+
///
66+
/// # Example (block-level analysis)
67+
/// ```ignore
68+
/// impl<'db, 'a> DataflowAnalyzer<'db, 'a> for BlockCounter {
69+
/// type Info = usize;
70+
/// const DIRECTION: Direction = Direction::Forward;
71+
///
72+
/// fn initial_info(&mut self) -> Self::Info { 0 }
73+
/// fn transfer_block(&mut self, info: &mut Self::Info, block_id: BlockId, block: &Block<'db>) {
74+
/// *info += 1; // Just count blocks
75+
/// }
76+
/// fn merge(&mut self, infos: impl Iterator<Item = Self::Info>) -> Self::Info {
77+
/// infos.max().unwrap_or(0)
78+
/// }
79+
/// }
80+
/// ```
81+
#[allow(dead_code)]
82+
pub trait DataflowAnalyzer<'db, 'a> {
83+
/// The analysis state/info type.
84+
type Info: Clone;
85+
86+
/// The direction of this analysis.
87+
const DIRECTION: Direction;
88+
89+
// ========================================================================
90+
// Core methods (must implement)
91+
// ========================================================================
92+
93+
/// Create the initial analysis state at a terminal block.
94+
///
95+
/// - Backward: called at return/panic blocks (what we know at function exit)
96+
/// - Forward: called at function entry
97+
///
98+
/// For backward analysis, `block_end` provides access to return variables or panic data.
99+
/// For forward analysis, this is typically called once at the root block.
100+
fn initial_info(&mut self, block_id: BlockId, block_end: &'a BlockEnd<'db>) -> Self::Info;
101+
102+
/// Merge/join states from multiple control flow paths.
103+
/// Called at join points (match merge for backward, block entry for forward).
104+
///
105+
/// - `statement_location`: where the merge occurs in the CFG
106+
/// - `infos`: iterator of (source_block_id, info) pairs from each incoming path
107+
fn merge(
108+
&mut self,
109+
statement_location: StatementLocation,
110+
infos: impl Iterator<Item = (BlockId, Self::Info)>,
111+
) -> Self::Info;
112+
113+
// ========================================================================
114+
// Transfer functions (choose granularity)
115+
// ========================================================================
116+
117+
/// Transfer function for an entire block.
118+
/// - Backward: transforms post-block state to pre-block state
119+
/// - Forward: transforms pre-block state to post-block state
120+
///
121+
/// Default implementation iterates statements and calls `transfer_stmt`.
122+
/// Override this for block-level analysis (ignoring individual statements).
123+
fn transfer_block(&mut self, info: &mut Self::Info, block_id: BlockId, block: &'a Block<'db>) {
124+
match Self::DIRECTION {
125+
Direction::Forward => {
126+
for (i, stmt) in block.statements.iter().enumerate() {
127+
self.transfer_stmt(info, (block_id, i), stmt);
128+
}
129+
}
130+
Direction::Backward => {
131+
for (i, stmt) in block.statements.iter().enumerate().rev() {
132+
self.transfer_stmt(info, (block_id, i), stmt);
133+
}
134+
}
135+
}
136+
}
137+
138+
/// Transfer function for a single statement.
139+
/// - Backward: transforms post-state to pre-state
140+
/// - Forward: transforms pre-state to post-state
141+
///
142+
/// Default is no-op. Override this for statement-level analysis.
143+
fn transfer_stmt(
144+
&mut self,
145+
_info: &mut Self::Info,
146+
_statement_location: StatementLocation,
147+
_stmt: &'a Statement<'db>,
148+
) {
149+
}
150+
151+
// ========================================================================
152+
// Variable tracking (optional - override if tracking per-variable state)
153+
// ========================================================================
154+
155+
/// Apply variable remapping to the state.
156+
/// Called by the framework when crossing a goto with remapping.
157+
///
158+
/// - Backward: translates demands on destination vars to demands on source vars
159+
/// - Forward: translates state of source vars to state of destination vars
160+
///
161+
/// Default is no-op (fine for analyses that don't track per-variable state).
162+
fn apply_remapping(
163+
&mut self,
164+
_info: &mut Self::Info,
165+
_statement_location: StatementLocation,
166+
_target_block_id: BlockId,
167+
_remapping: &'a VarRemapping<'db>,
168+
) {
169+
}
170+
171+
// ========================================================================
172+
// Block boundary hooks (optional)
173+
// ========================================================================
174+
175+
/// Called when entering a block during traversal (before transfer_block).
176+
fn visit_block_start(
177+
&mut self,
178+
_info: &mut Self::Info,
179+
_block_id: BlockId,
180+
_block: &Block<'db>,
181+
) {
182+
}
183+
184+
// ========================================================================
185+
// Match handling (optional - have sensible defaults)
186+
// ========================================================================
187+
188+
/// Backward: merge states from match arms.
189+
/// Default implementation calls `merge` with arm block IDs.
190+
/// Override for special handling (e.g., tracking arm-specific metadata).
191+
fn merge_match(
192+
&mut self,
193+
statement_location: StatementLocation,
194+
match_info: &'a MatchInfo<'db>,
195+
arm_infos: impl Iterator<Item = Self::Info>,
196+
) -> Self::Info {
197+
let infos_with_blocks = match_info.arms().iter().map(|arm| arm.block_id).zip(arm_infos);
198+
self.merge(statement_location, infos_with_blocks)
199+
}
200+
201+
/// Forward: split state for match arms.
202+
/// Default implementation clones state for each arm.
203+
/// Override to refine state based on match conditions.
204+
fn split_match(
205+
&mut self,
206+
info: &Self::Info,
207+
_statement_location: StatementLocation,
208+
match_info: &'a MatchInfo<'db>,
209+
) -> Vec<Self::Info> {
210+
match_info.arms().iter().map(|_| info.clone()).collect()
211+
}
212+
}

crates/cairo-lang-lowering/src/analysis/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
//! This module provides generic analysis frameworks that can be used by various
44
//! optimization passes and semantic checks.
55
6-
pub mod backward;
6+
mod backward;
7+
mod core;
78

8-
// Re-export commonly used types at the module level for convenience.
99
pub use backward::BackAnalysis;
1010

1111
use crate::{Block, BlockId, MatchInfo, Statement, VarRemapping, VarUsage};

0 commit comments

Comments
 (0)