Skip to content

Commit 890e0b0

Browse files
committed
refactor(analyzer): separate SSA state from IR evaluation
1 parent cba3429 commit 890e0b0

3 files changed

Lines changed: 245 additions & 164 deletions

File tree

crates/analyzer/src/comb_loop_detect.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
1111
mod procedure;
1212
mod region;
13+
mod ssa;
1314

1415
use region::{ArraySpan, BitPartition, IdxKey, NodeKey, PackedSpan, dst_writes, var_reads};
1516

crates/analyzer/src/comb_loop_detect/procedure.rs

Lines changed: 34 additions & 164 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Analyzer-IR procedure evaluation for combinational dependency extraction.
22
33
use super::region::{BitPartition, NodeKey, PackedSpan, dst_writes, var_reads};
4+
use super::ssa::{SsaStore, VersionId};
5+
use crate::HashSet;
46
use crate::conv::Context;
57
use crate::ir::VarId;
68
use crate::ir::{
@@ -9,7 +11,6 @@ use crate::ir::{
911
VarSelect,
1012
};
1113
use crate::value::Value;
12-
use crate::{HashMap, HashSet};
1314

1415
pub(super) fn analyze(
1516
module: &Module,
@@ -19,23 +20,10 @@ pub(super) fn analyze(
1920
ProcedureAnalysis::analyze(module, bit_part, statements)
2021
}
2122

22-
// Minimal statement-ordered SSA used by the loop detector.
23-
24-
type VersionId = usize;
25-
26-
#[derive(Clone)]
27-
enum SsaVersion {
28-
Entry(NodeKey),
29-
Definition(Vec<VersionId>),
30-
Phi(Vec<VersionId>),
31-
}
32-
3323
struct ProcedureAnalysis<'a> {
3424
bit_part: &'a BitPartition,
3525
ctx: Context,
36-
versions: Vec<SsaVersion>,
37-
entries: HashMap<NodeKey, VersionId>,
38-
state: HashMap<NodeKey, VersionId>,
26+
ssa: SsaStore<NodeKey>,
3927
written: HashSet<NodeKey>,
4028
}
4129

@@ -51,116 +39,21 @@ impl<'a> ProcedureAnalysis<'a> {
5139
let mut this = Self {
5240
bit_part,
5341
ctx,
54-
versions: Vec::new(),
55-
entries: HashMap::default(),
56-
state: HashMap::default(),
42+
ssa: SsaStore::default(),
5743
written: HashSet::default(),
5844
};
5945
this.eval_block(statements, &[]);
6046

6147
let mut dependencies = Vec::new();
6248
let destinations: Vec<_> = this.written.iter().copied().collect();
6349
for destination in destinations {
64-
let version = this.current_version(destination);
65-
let mut sources = HashSet::default();
66-
let mut visited = HashSet::default();
67-
this.collect_root_sources(version, &mut sources, &mut visited);
50+
let version = this.ssa.read(destination);
51+
let sources = this.ssa.root_sources(version);
6852
dependencies.extend(sources.into_iter().map(|source| (source, destination)));
6953
}
7054
dependencies
7155
}
7256

73-
fn entry_version(&mut self, key: NodeKey) -> VersionId {
74-
if let Some(version) = self.entries.get(&key) {
75-
return *version;
76-
}
77-
let version = self.versions.len();
78-
self.versions.push(SsaVersion::Entry(key));
79-
self.entries.insert(key, version);
80-
version
81-
}
82-
83-
fn current_version(&mut self, key: NodeKey) -> VersionId {
84-
if let Some(version) = self.state.get(&key) {
85-
*version
86-
} else {
87-
let version = self.entry_version(key);
88-
self.state.insert(key, version);
89-
version
90-
}
91-
}
92-
93-
fn definition(&mut self, mut sources: Vec<VersionId>) -> VersionId {
94-
sources.sort_unstable();
95-
sources.dedup();
96-
let version = self.versions.len();
97-
self.versions.push(SsaVersion::Definition(sources));
98-
version
99-
}
100-
101-
fn phi(&mut self, mut inputs: Vec<VersionId>) -> VersionId {
102-
inputs.sort_unstable();
103-
inputs.dedup();
104-
if inputs.len() == 1 {
105-
return inputs[0];
106-
}
107-
let version = self.versions.len();
108-
self.versions.push(SsaVersion::Phi(inputs));
109-
version
110-
}
111-
112-
fn collect_root_sources(
113-
&self,
114-
version: VersionId,
115-
sources: &mut HashSet<NodeKey>,
116-
visited: &mut HashSet<(VersionId, bool)>,
117-
) {
118-
match &self.versions[version] {
119-
// A final LiveOnEntry value is retained state, not a combinational
120-
// read. Entry versions reached through an explicit definition are.
121-
SsaVersion::Entry(_) => {}
122-
SsaVersion::Definition(inputs) => {
123-
for input in inputs {
124-
self.collect_sources(*input, true, sources, visited);
125-
}
126-
}
127-
SsaVersion::Phi(inputs) => {
128-
for input in inputs {
129-
self.collect_sources(*input, false, sources, visited);
130-
}
131-
}
132-
}
133-
}
134-
135-
fn collect_sources(
136-
&self,
137-
version: VersionId,
138-
include_entry: bool,
139-
sources: &mut HashSet<NodeKey>,
140-
visited: &mut HashSet<(VersionId, bool)>,
141-
) {
142-
if !visited.insert((version, include_entry)) {
143-
return;
144-
}
145-
match &self.versions[version] {
146-
SsaVersion::Entry(key) => {
147-
if include_entry {
148-
sources.insert(*key);
149-
}
150-
}
151-
SsaVersion::Definition(inputs) => {
152-
for input in inputs {
153-
self.collect_sources(*input, true, sources, visited);
154-
}
155-
}
156-
SsaVersion::Phi(inputs) => {
157-
for input in inputs {
158-
self.collect_sources(*input, include_entry, sources, visited);
159-
}
160-
}
161-
}
162-
}
163-
16457
fn read_keys(&mut self, id: VarId, index: &VarIndex, select: &VarSelect) -> Vec<NodeKey> {
16558
let mut keys = Vec::new();
16659
for (idx, span) in var_reads(id, index, select, &mut self.ctx) {
@@ -184,7 +77,7 @@ impl<'a> ProcedureAnalysis<'a> {
18477
fn read_variable(&mut self, id: VarId, index: &VarIndex, select: &VarSelect) -> Vec<VersionId> {
18578
self.read_keys(id, index, select)
18679
.into_iter()
187-
.map(|key| self.current_version(key))
80+
.map(|key| self.ssa.read(key))
18881
.collect()
18982
}
19083

@@ -208,8 +101,8 @@ impl<'a> ProcedureAnalysis<'a> {
208101
dependencies.extend(self.eval_expr(expression));
209102
}
210103
for key in self.write_keys(destination) {
211-
let version = self.definition(dependencies.clone());
212-
self.state.insert(key, version);
104+
let version = self.ssa.definition(dependencies.clone());
105+
self.ssa.bind(key, version);
213106
self.written.insert(key);
214107
}
215108
}
@@ -269,8 +162,8 @@ impl<'a> ProcedureAnalysis<'a> {
269162
} else {
270163
dependencies.extend(self.eval_expr(expression));
271164
}
272-
let version = self.definition(dependencies);
273-
self.state.insert(key, version);
165+
let version = self.ssa.definition(dependencies);
166+
self.ssa.bind(key, version);
274167
self.written.insert(key);
275168
}
276169
}
@@ -347,20 +240,20 @@ impl<'a> ProcedureAnalysis<'a> {
347240
let condition = self.eval_expr(&statement.cond);
348241
let mut nested_controls = controls.to_vec();
349242
nested_controls.extend_from_slice(&condition);
350-
let saved_state = self.state.clone();
243+
let saved_state = self.ssa.snapshot();
351244
let saved_written = self.written.clone();
352245

353246
self.eval_block(&statement.true_side, &nested_controls);
354-
let true_state = self.state.clone();
247+
let true_state = self.ssa.snapshot();
355248
let true_written = self.written.clone();
356249

357-
self.state = saved_state.clone();
250+
self.ssa.restore(&saved_state);
358251
self.written = saved_written;
359252
self.eval_block(&statement.false_side, &nested_controls);
360-
let false_state = self.state.clone();
253+
let false_state = self.ssa.snapshot();
361254
let false_written = self.written.clone();
362255

363-
self.merge_states(&saved_state, &[true_state, false_state]);
256+
self.ssa.merge(&saved_state, &[true_state, false_state]);
364257
self.written = true_written;
365258
self.written.extend(false_written);
366259
}
@@ -382,50 +275,26 @@ impl<'a> ProcedureAnalysis<'a> {
382275
}
383276
let mut nested_controls = controls.to_vec();
384277
nested_controls.extend(condition);
385-
let saved_state = self.state.clone();
278+
let saved_state = self.ssa.snapshot();
386279
let saved_written = self.written.clone();
387280
let mut states = Vec::with_capacity(statement.arms.len() + 1);
388281
let mut written = saved_written.clone();
389282
for arm in &statement.arms {
390-
self.state = saved_state.clone();
283+
self.ssa.restore(&saved_state);
391284
self.written = saved_written.clone();
392285
self.eval_block(&arm.body, &nested_controls);
393-
states.push(self.state.clone());
286+
states.push(self.ssa.snapshot());
394287
written.extend(self.written.iter().copied());
395288
}
396-
self.state = saved_state.clone();
289+
self.ssa.restore(&saved_state);
397290
self.written = saved_written;
398291
self.eval_block(&statement.default, &nested_controls);
399-
states.push(self.state.clone());
292+
states.push(self.ssa.snapshot());
400293
written.extend(self.written.iter().copied());
401-
self.merge_states(&saved_state, &states);
294+
self.ssa.merge(&saved_state, &states);
402295
self.written = written;
403296
}
404297

405-
fn merge_states(
406-
&mut self,
407-
base: &HashMap<NodeKey, VersionId>,
408-
states: &[HashMap<NodeKey, VersionId>],
409-
) {
410-
let mut keys: HashSet<NodeKey> = base.keys().copied().collect();
411-
for state in states {
412-
keys.extend(state.keys().copied());
413-
}
414-
let mut merged = HashMap::default();
415-
for key in keys {
416-
let fallback = base
417-
.get(&key)
418-
.copied()
419-
.unwrap_or_else(|| self.entry_version(key));
420-
let inputs = states
421-
.iter()
422-
.map(|state| state.get(&key).copied().unwrap_or(fallback))
423-
.collect();
424-
merged.insert(key, self.phi(inputs));
425-
}
426-
self.state = merged;
427-
}
428-
429298
fn eval_for(&mut self, statement: &ForStatement, controls: &[VersionId]) {
430299
let mut range_controls = controls.to_vec();
431300
let bounds = match &statement.range {
@@ -460,12 +329,13 @@ impl<'a> ProcedureAnalysis<'a> {
460329
// Runtime loops have a zero-trip path. One symbolic body traversal is
461330
// enough to expose all explicit reads; the exit phi keeps LiveOnEntry
462331
// separate so retained state does not become a loop edge.
463-
let saved_state = self.state.clone();
332+
let saved_state = self.ssa.snapshot();
464333
let saved_written = self.written.clone();
465334
self.eval_block(&statement.body, &range_controls);
466-
let body_state = self.state.clone();
335+
let body_state = self.ssa.snapshot();
467336
let body_written = self.written.clone();
468-
self.merge_states(&saved_state, &[saved_state.clone(), body_state]);
337+
self.ssa
338+
.merge(&saved_state, &[saved_state.clone(), body_state]);
469339
self.written = saved_written;
470340
self.written.extend(body_written);
471341
}
@@ -523,7 +393,7 @@ impl<'a> ProcedureAnalysis<'a> {
523393
for key in
524394
self.bit_part.overlapping_access(*id, idx, source_span)
525395
{
526-
reads.push(self.current_version(key));
396+
reads.push(self.ssa.read(key));
527397
}
528398
}
529399
}
@@ -735,8 +605,8 @@ impl<'a> ProcedureAnalysis<'a> {
735605
};
736606
for key in self.keys_for_id(formal) {
737607
let sources = self.eval_actual_for_formal_key(actual, key);
738-
let version = self.definition(sources);
739-
self.state.insert(key, version);
608+
let version = self.ssa.definition(sources);
609+
self.ssa.bind(key, version);
740610
}
741611
}
742612

@@ -795,7 +665,7 @@ impl<'a> ProcedureAnalysis<'a> {
795665
fn current_versions_for_id(&mut self, id: VarId) -> Vec<VersionId> {
796666
self.keys_for_id(id)
797667
.into_iter()
798-
.map(|key| self.current_version(key))
668+
.map(|key| self.ssa.read(key))
799669
.collect()
800670
}
801671

@@ -816,7 +686,7 @@ impl<'a> ProcedureAnalysis<'a> {
816686
.bit_part
817687
.overlapping((*id, formal_key.1), span)
818688
.into_iter()
819-
.map(|range| self.current_version((*id, formal_key.1, range)))
689+
.map(|range| self.ssa.read((*id, formal_key.1, range)))
820690
.collect();
821691
}
822692
self.eval_expr_bits(actual, span)
@@ -855,15 +725,15 @@ impl<'a> ProcedureAnalysis<'a> {
855725
continue;
856726
};
857727
if formal_span.overlaps(requested) {
858-
sources.push(self.current_version(formal_key));
728+
sources.push(self.ssa.read(formal_key));
859729
}
860730
}
861731
}
862732
} else {
863733
sources.extend(self.current_versions_for_id(formal));
864734
}
865-
let version = self.definition(sources);
866-
self.state.insert(key, version);
735+
let version = self.ssa.definition(sources);
736+
self.ssa.bind(key, version);
867737
self.written.insert(key);
868738
}
869739
}

0 commit comments

Comments
 (0)