@@ -35,6 +35,10 @@ type IdxKey = (VarId, usize);
3535/// `BitPartition`, so bit-disjoint reads/writes form disjoint nodes.
3636type NodeKey = ( VarId , usize , usize ) ;
3737
38+ /// Unpacked elements not separated by a constant-index access are represented
39+ /// by one split piece, regardless of the declared array length.
40+ const SPLIT_REMAINDER_INDEX : usize = usize:: MAX ;
41+
3842/// Per `IdxKey`, atomic bit-range masks. Two bits are in the same range
3943/// iff they appear in the same set of per-decl masks.
4044#[ derive( Default ) ]
@@ -57,6 +61,33 @@ impl BitPartition {
5761 . map ( |( i, _) | i)
5862 . collect ( )
5963 }
64+
65+ fn overlapping_access ( & self , id : VarId , index : usize , mask : & BigUint ) -> Vec < NodeKey > {
66+ if index != SPLIT_REMAINDER_INDEX {
67+ return self
68+ . overlapping ( ( id, index) , mask)
69+ . into_iter ( )
70+ . map ( |range| ( id, index, range) )
71+ . collect ( ) ;
72+ }
73+
74+ let zero = BigUint :: default ( ) ;
75+ let mut keys = self
76+ . ranges
77+ . iter ( )
78+ . filter ( |( ( object, _) , _) | * object == id)
79+ . flat_map ( |( ( _, split_index) , ranges) | {
80+ ranges
81+ . iter ( )
82+ . enumerate ( )
83+ . filter ( |( _, range) | ( * range & mask) != zero)
84+ . map ( |( range, _) | ( id, * split_index, range) )
85+ } )
86+ . collect :: < Vec < _ > > ( ) ;
87+ keys. sort_unstable ( ) ;
88+ keys. dedup ( ) ;
89+ keys
90+ }
6091}
6192
6293/// `feedthrough[child_in_id] = { child_out_ids reachable purely combinationally }`.
@@ -189,11 +220,6 @@ fn atomic_ranges(masks: &[BigUint], _width: usize) -> Vec<BigUint> {
189220 ret
190221}
191222
192- /// The minimal SSA uses existing per-element coordinates. Whole or dynamic
193- /// accesses above this limit are left for the sparse-array follow-up instead
194- /// of expanding work with the declared element count.
195- const OVERSIZED_ARRAY : usize = 1 << 16 ;
196-
197223fn build_bit_partition ( module : & Module , ctx : & mut Context ) -> BitPartition {
198224 let mut masks: HashMap < ( VarId , usize ) , Vec < BigUint > > = HashMap :: default ( ) ;
199225
@@ -660,9 +686,7 @@ fn collect_factor_node_keys(
660686 match factor {
661687 Factor :: Variable ( id, index, select, _) => {
662688 for ( idx, mask) in var_reads ( * id, index, select, ctx) {
663- for r in bit_part. overlapping ( ( * id, idx) , & mask) {
664- out. push ( ( * id, idx, r) ) ;
665- }
689+ out. extend ( bit_part. overlapping_access ( * id, idx, & mask) ) ;
666690 }
667691 }
668692 Factor :: FunctionCall ( _) | Factor :: SystemFunctionCall ( _) => {
@@ -951,12 +975,7 @@ impl<'a> SsaProcedure<'a> {
951975 fn read_keys ( & mut self , id : VarId , index : & VarIndex , select : & VarSelect ) -> Vec < NodeKey > {
952976 let mut keys = Vec :: new ( ) ;
953977 for ( idx, mask) in var_reads ( id, index, select, & mut self . ctx ) {
954- keys. extend (
955- self . bit_part
956- . overlapping ( ( id, idx) , & mask)
957- . into_iter ( )
958- . map ( |range| ( id, idx, range) ) ,
959- ) ;
978+ keys. extend ( self . bit_part . overlapping_access ( id, idx, & mask) ) ;
960979 }
961980 keys. sort_unstable ( ) ;
962981 keys. dedup ( ) ;
@@ -966,12 +985,7 @@ impl<'a> SsaProcedure<'a> {
966985 fn write_keys ( & mut self , destination : & AssignDestination ) -> Vec < NodeKey > {
967986 let mut keys = Vec :: new ( ) ;
968987 for ( idx, mask) in dst_writes ( destination, & mut self . ctx ) {
969- keys. extend (
970- self . bit_part
971- . overlapping ( ( destination. id , idx) , & mask)
972- . into_iter ( )
973- . map ( |range| ( destination. id , idx, range) ) ,
974- ) ;
988+ keys. extend ( self . bit_part . overlapping_access ( destination. id , idx, & mask) ) ;
975989 }
976990 keys. sort_unstable ( ) ;
977991 keys. dedup ( ) ;
@@ -1313,8 +1327,8 @@ impl<'a> SsaProcedure<'a> {
13131327 let mut reads = Vec :: new ( ) ;
13141328 for ( idx, mask) in var_reads ( * id, index, select, & mut self . ctx ) {
13151329 let source_mask = & source_mask & mask;
1316- for range in self . bit_part . overlapping ( ( * id, idx) , & source_mask) {
1317- reads. push ( self . current_version ( ( * id , idx , range ) ) ) ;
1330+ for key in self . bit_part . overlapping_access ( * id, idx, & source_mask) {
1331+ reads. push ( self . current_version ( key ) ) ;
13181332 }
13191333 }
13201334 reads
@@ -1725,13 +1739,18 @@ fn dst_writes(dst: &AssignDestination, ctx: &mut Context) -> Vec<(usize, BigUint
17251739 let is_index_const = dst. index . is_const ( ) ;
17261740 let is_select_const = dst. select . is_const ( ) ;
17271741
1728- if ( !is_index_const || dst. index . 0 . is_empty ( ) )
1729- && variable
1730- . r#type
1731- . total_array ( )
1732- . is_some_and ( |total| total > OVERSIZED_ARRAY )
1742+ let mask = if !is_select_const {
1743+ conservative_select_mask ( & dst. select , & variable. r#type , ctx)
1744+ } else {
1745+ let Some ( ( beg, end) ) = dst. select . eval_value ( ctx, & variable. r#type , false ) else {
1746+ return Vec :: new ( ) ;
1747+ } ;
1748+ ValueBigUint :: gen_mask_range ( beg, end)
1749+ } ;
1750+
1751+ if variable. r#type . total_array ( ) . unwrap_or ( 2 ) > 1 && ( !is_index_const || dst. index . 0 . is_empty ( ) )
17331752 {
1734- return Vec :: new ( ) ;
1753+ return vec ! [ ( SPLIT_REMAINDER_INDEX , mask ) ] ;
17351754 }
17361755
17371756 let range = if !is_index_const {
@@ -1743,15 +1762,6 @@ fn dst_writes(dst: &AssignDestination, ctx: &mut Context) -> Vec<(usize, BigUint
17431762 variable. r#type . array . calc_range ( & index)
17441763 } ;
17451764
1746- let mask = if !is_select_const {
1747- conservative_select_mask ( & dst. select , & variable. r#type , ctx)
1748- } else {
1749- let Some ( ( beg, end) ) = dst. select . eval_value ( ctx, & variable. r#type , false ) else {
1750- return Vec :: new ( ) ;
1751- } ;
1752- ValueBigUint :: gen_mask_range ( beg, end)
1753- } ;
1754-
17551765 let mut out = Vec :: new ( ) ;
17561766 if let Some ( ( beg, end) ) = range {
17571767 for i in beg..=end {
@@ -1770,30 +1780,23 @@ fn var_reads(
17701780 let Some ( variable) = ctx. variables . get ( & id) . cloned ( ) else {
17711781 return Vec :: new ( ) ;
17721782 } ;
1773- if ( !index. is_const ( ) || index. 0 . is_empty ( ) )
1774- && variable
1775- . r#type
1776- . total_array ( )
1777- . is_some_and ( |total| total > OVERSIZED_ARRAY )
1778- {
1779- return Vec :: new ( ) ;
1780- }
17811783 let mask = if select. is_const_with_range ( )
17821784 && let Some ( ( beg, end) ) = select. eval_value ( ctx, & variable. r#type , false )
17831785 {
17841786 ValueBigUint :: gen_mask_range ( beg, end)
17851787 } else {
17861788 conservative_select_mask ( select, & variable. r#type , ctx)
17871789 } ;
1790+ if variable. r#type . total_array ( ) . unwrap_or ( 2 ) > 1 && ( !index. is_const ( ) || index. 0 . is_empty ( ) ) {
1791+ return vec ! [ ( SPLIT_REMAINDER_INDEX , mask) ] ;
1792+ }
17881793 if index. is_const ( )
17891794 && let Some ( idx_path) = index. eval_value ( ctx)
17901795 && let Some ( flat) = variable. r#type . array . calc_index ( & idx_path)
17911796 {
17921797 return vec ! [ ( flat, mask) ] ;
17931798 }
1794- // Dynamic index: conservatively treat every element as read.
1795- let total = variable. r#type . total_array ( ) . unwrap_or ( 1 ) ;
1796- ( 0 ..total) . map ( |i| ( i, mask. clone ( ) ) ) . collect ( )
1799+ vec ! [ ( SPLIT_REMAINDER_INDEX , mask) ]
17971800}
17981801
17991802fn conservative_select_mask (
0 commit comments