Skip to content

Commit 4992078

Browse files
(feat): improved analyzer trait to better deal with intra block flow
1 parent a8d2185 commit 4992078

File tree

1 file changed

+42
-90
lines changed
  • crates/cairo-lang-lowering/src/analysis

1 file changed

+42
-90
lines changed

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

Lines changed: 42 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -10,58 +10,56 @@
1010
//! 2. **Transfer granularity** (choose one):
1111
//! - Block-level: override `transfer_block` for coarse-grained analysis
1212
//! - 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
13+
//! 3. **Variable tracking** (optional): `transfer_edge` - override if tracking per-variable state
1614
//!
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
15+
//! The Runner's (backward/forward/etc) should handle control flow mechanics automatically:
16+
//! - Goto with remapping/Match split: calls `transfer_edge`
17+
//! - Call transfer block for each block in need of processing
2118
22-
use crate::{Block, BlockEnd, BlockId, MatchInfo, Statement, VarRemapping};
19+
use crate::ids::LocationId;
20+
use crate::{Block, BlockEnd, BlockId, MatchArm, MatchInfo, Statement, VarRemapping, VarUsage};
2321

2422
/// Location of a lowering statement inside a block.
2523
pub type StatementLocation = (BlockId, usize);
2624

2725
/// The direction of dataflow analysis.
28-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2926
#[expect(dead_code)]
27+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3028
pub enum Direction {
3129
Forward,
3230
Backward,
3331
}
3432

33+
/// Represents an edge in the control flow graph.
34+
///
35+
/// Each variant captures the specific information needed for that edge type,
36+
/// enabling analyzers to handle variable introductions and remappings.
37+
#[derive(Debug)]
38+
#[expect(dead_code)]
39+
pub enum Edge<'db, 'a> {
40+
/// A goto edge with variable remapping.
41+
Goto { target: BlockId, remapping: &'a VarRemapping<'db> },
42+
/// A match arm edge with the arm's introduced variables.
43+
MatchArm { arm: &'a MatchArm<'db>, match_info: &'a MatchInfo<'db> },
44+
/// A return edge (terminal).
45+
Return { vars: &'a [VarUsage<'db>], location: &'a LocationId<'db> },
46+
/// A panic edge (terminal).
47+
Panic { var: &'a VarUsage<'db> },
48+
}
49+
3550
/// Unified analyzer trait for dataflow analysis.
3651
///
3752
/// Implementors specify the direction via `DIRECTION` and implement the core methods.
38-
/// The framework handles control flow mechanics (remapping, match arms) automatically,
39-
/// with sensible defaults that can be overridden for complex analyses.
53+
/// The framework specifies the "behaviour" of the dataflow (essentially updates to lattice state
54+
/// for lattice analysis). Running an analysis is done by a runner (backward/forward/etc) which will
55+
/// handle control flow and dataflow mechanics.
4056
///
4157
/// # Transfer Granularity
4258
///
4359
/// You can work at either block or statement level:
4460
/// - **Block-level**: Override `transfer_block` for coarse-grained analysis
4561
/// - **Statement-level**: Override `transfer_stmt`; default `transfer_block` iterates statements
4662
///
47-
/// # Lifetime parameters
48-
/// - `'db`: The database lifetime (for interned types).
49-
/// - `'a`: The lifetime of borrowed lowering data.
50-
///
51-
/// # Example (statement-level forward analysis)
52-
/// ```ignore
53-
/// impl<'db, 'a> DataflowAnalyzer<'db, 'a> for MyAnalyzer {
54-
/// type Info = HashSet<VariableId>;
55-
/// const DIRECTION: Direction = Direction::Forward;
56-
///
57-
/// fn initial_info(&mut self) -> Self::Info { HashSet::new() }
58-
/// fn transfer_stmt(&mut self, info: &mut Self::Info, ..) { /* update info */ }
59-
/// fn merge(&mut self, infos: impl Iterator<Item = Self::Info>) -> Self::Info {
60-
/// infos.fold(HashSet::new(), |mut acc, i| { acc.extend(i); acc })
61-
/// }
62-
/// }
63-
/// ```
64-
///
6563
/// # Example (block-level analysis)
6664
/// ```ignore
6765
/// impl<'db, 'a> DataflowAnalyzer<'db, 'a> for BlockCounter {
@@ -72,8 +70,8 @@ pub enum Direction {
7270
/// fn transfer_block(&mut self, info: &mut Self::Info, block_id: BlockId, block: &Block<'db>) {
7371
/// *info += 1; // Just count blocks
7472
/// }
75-
/// fn merge(&mut self, infos: impl Iterator<Item = Self::Info>) -> Self::Info {
76-
/// infos.max().unwrap_or(0)
73+
/// fn merge(&mut self, _loc: StatementLocation, _end_loc: &LocationId, infos: ..) -> Self::Info {
74+
/// infos.map(|(_, i)| i).max().unwrap_or(0)
7775
/// }
7876
/// }
7977
/// ```
@@ -85,10 +83,6 @@ pub trait DataflowAnalyzer<'db, 'a> {
8583
/// The direction of this analysis.
8684
const DIRECTION: Direction;
8785

88-
// ========================================================================
89-
// Core methods (must implement)
90-
// ========================================================================
91-
9286
/// Create the initial analysis state at a terminal block.
9387
///
9488
/// - Backward: called at return/panic blocks (what we know at function exit)
@@ -101,18 +95,16 @@ pub trait DataflowAnalyzer<'db, 'a> {
10195
/// Merge/join states from multiple control flow paths.
10296
/// Called at join points (match merge for backward, block entry for forward).
10397
///
104-
/// - `statement_location`: where the merge occurs in the CFG
105-
/// - `infos`: iterator of (source_block_id, info) pairs from each incoming path
98+
/// - `statement_location`: where the merge occurs in the CFG.
99+
/// - `location`: source location of the merge.
100+
/// - `infos`: iterator of (source_block_id, info) pairs from each incoming path.
106101
fn merge(
107102
&mut self,
108103
statement_location: StatementLocation,
104+
location: &'a LocationId<'db>,
109105
infos: impl Iterator<Item = (BlockId, Self::Info)>,
110106
) -> Self::Info;
111107

112-
// ========================================================================
113-
// Transfer functions (choose granularity)
114-
// ========================================================================
115-
116108
/// Transfer function for an entire block.
117109
/// - Backward: transforms post-block state to pre-block state
118110
/// - Forward: transforms pre-block state to post-block state
@@ -147,30 +139,19 @@ pub trait DataflowAnalyzer<'db, 'a> {
147139
) {
148140
}
149141

150-
// ========================================================================
151-
// Variable tracking (optional - override if tracking per-variable state)
152-
// ========================================================================
153-
154-
/// Apply variable remapping to the state.
155-
/// Called by the framework when crossing a goto with remapping.
142+
/// Transfer state along a CFG edge.
143+
/// Called when traversing between blocks via control flow edges.
156144
///
157-
/// - Backward: translates demands on destination vars to demands on source vars
158-
/// - Forward: translates state of source vars to state of destination vars
145+
/// - `info`: the state to transfer
146+
/// - `edge`: the edge being traversed, containing all relevant information
159147
///
160-
/// Default is no-op (fine for analyses that don't track per-variable state).
161-
fn apply_remapping(
162-
&mut self,
163-
_info: &mut Self::Info,
164-
_statement_location: StatementLocation,
165-
_target_block_id: BlockId,
166-
_remapping: &'a VarRemapping<'db>,
167-
) {
148+
/// Default implementation clones the state.
149+
/// Override to modify state based on edge properties (e.g., variable remapping,
150+
/// introduced variables in match arms).
151+
fn transfer_edge(&mut self, info: &Self::Info, _edge: &Edge<'db, 'a>) -> Self::Info {
152+
info.clone()
168153
}
169154

170-
// ========================================================================
171-
// Block boundary hooks (optional)
172-
// ========================================================================
173-
174155
/// Called when entering a block during traversal (before transfer_block).
175156
fn visit_block_start(
176157
&mut self,
@@ -179,33 +160,4 @@ pub trait DataflowAnalyzer<'db, 'a> {
179160
_block: &Block<'db>,
180161
) {
181162
}
182-
183-
// ========================================================================
184-
// Match handling (optional - have sensible defaults)
185-
// ========================================================================
186-
187-
/// Backward: merge states from match arms.
188-
/// Default implementation calls `merge` with arm block IDs.
189-
/// Override for special handling (e.g., tracking arm-specific metadata).
190-
fn merge_match(
191-
&mut self,
192-
statement_location: StatementLocation,
193-
match_info: &'a MatchInfo<'db>,
194-
arm_infos: impl Iterator<Item = Self::Info>,
195-
) -> Self::Info {
196-
let infos_with_blocks = match_info.arms().iter().map(|arm| arm.block_id).zip(arm_infos);
197-
self.merge(statement_location, infos_with_blocks)
198-
}
199-
200-
/// Forward: split state for match arms.
201-
/// Default implementation clones state for each arm.
202-
/// Override to refine state based on match conditions.
203-
fn split_match(
204-
&mut self,
205-
info: &Self::Info,
206-
_statement_location: StatementLocation,
207-
match_info: &'a MatchInfo<'db>,
208-
) -> Vec<Self::Info> {
209-
match_info.arms().iter().map(|_| info.clone()).collect()
210-
}
211163
}

0 commit comments

Comments
 (0)