11//! Analyzer-IR procedure evaluation for combinational dependency extraction.
22
33use super :: region:: { BitPartition , NodeKey , PackedSpan , dst_writes, var_reads} ;
4+ use super :: ssa:: { SsaStore , VersionId } ;
5+ use crate :: HashSet ;
46use crate :: conv:: Context ;
57use crate :: ir:: VarId ;
68use crate :: ir:: {
@@ -9,7 +11,6 @@ use crate::ir::{
911 VarSelect ,
1012} ;
1113use crate :: value:: Value ;
12- use crate :: { HashMap , HashSet } ;
1314
1415pub ( 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-
3323struct 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