64
64
//! methods. It effectively does a reverse walk of the AST; whenever we
65
65
//! reach a loop node, we iterate until a fixed point is reached.
66
66
//!
67
- //! ## The `users_*` fields
67
+ //! ## The `RWU` struct
68
68
//!
69
69
//! At each live node `N`, we track three pieces of information for each
70
- //! variable `V` (these are in the `users_*` fields ):
70
+ //! variable `V` (these are encapsulated in the `RWU` struct ):
71
71
//!
72
72
//! - `reader`: the `LiveNode` ID of some node which will read the value
73
73
//! that `V` holds on entry to `N`. Formally: a node `M` such
@@ -536,6 +536,112 @@ fn visit_expr<'a, 'tcx>(ir: &mut IrMaps<'a, 'tcx>, expr: &'tcx Expr) {
536
536
// Actually we compute just a bit more than just liveness, but we use
537
537
// the same basic propagation framework in all cases.
538
538
539
+ #[ derive( Clone , Copy ) ]
540
+ struct RWU {
541
+ reader : LiveNode ,
542
+ writer : LiveNode ,
543
+ used : bool
544
+ }
545
+
546
+ /// Conceptually, this is like a `Vec<RWU>`. But the number of `RWU`s can get
547
+ /// very large, so it uses a more compact representation that takes advantage
548
+ /// of the fact that when the number of `RWU`s is large, most of them have an
549
+ /// invalid reader and an invalid writer.
550
+ struct RWUTable {
551
+ /// Each entry in `packed_rwus` is either INV_INV_FALSE, INV_INV_TRUE, or
552
+ /// an index into `unpacked_rwus`. In the common cases, this compacts the
553
+ /// 65 bits of data into 32; in the uncommon cases, it expands the 65 bits
554
+ /// in 96.
555
+ ///
556
+ /// More compact representations are possible -- e.g. use only 2 bits per
557
+ /// packed `RWU` and make the secondary table a HashMap that maps from
558
+ /// indices to `RWU`s -- but this one strikes a good balance between size
559
+ /// and speed.
560
+ packed_rwus : Vec < u32 > ,
561
+ unpacked_rwus : Vec < RWU > ,
562
+ }
563
+
564
+ // A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: false }`.
565
+ const INV_INV_FALSE : u32 = u32:: MAX ;
566
+
567
+ // A constant representing `RWU { reader: invalid_node(); writer: invalid_node(); used: true }`.
568
+ const INV_INV_TRUE : u32 = u32:: MAX - 1 ;
569
+
570
+ impl RWUTable {
571
+ fn new ( num_rwus : usize ) -> RWUTable {
572
+ Self {
573
+ packed_rwus : vec ! [ INV_INV_FALSE ; num_rwus] ,
574
+ unpacked_rwus : vec ! [ ] ,
575
+ }
576
+ }
577
+
578
+ fn get ( & self , idx : usize ) -> RWU {
579
+ let packed_rwu = self . packed_rwus [ idx] ;
580
+ match packed_rwu {
581
+ INV_INV_FALSE => RWU { reader : invalid_node ( ) , writer : invalid_node ( ) , used : false } ,
582
+ INV_INV_TRUE => RWU { reader : invalid_node ( ) , writer : invalid_node ( ) , used : true } ,
583
+ _ => self . unpacked_rwus [ packed_rwu as usize ] ,
584
+ }
585
+ }
586
+
587
+ fn get_reader ( & self , idx : usize ) -> LiveNode {
588
+ let packed_rwu = self . packed_rwus [ idx] ;
589
+ match packed_rwu {
590
+ INV_INV_FALSE | INV_INV_TRUE => invalid_node ( ) ,
591
+ _ => self . unpacked_rwus [ packed_rwu as usize ] . reader ,
592
+ }
593
+ }
594
+
595
+ fn get_writer ( & self , idx : usize ) -> LiveNode {
596
+ let packed_rwu = self . packed_rwus [ idx] ;
597
+ match packed_rwu {
598
+ INV_INV_FALSE | INV_INV_TRUE => invalid_node ( ) ,
599
+ _ => self . unpacked_rwus [ packed_rwu as usize ] . writer ,
600
+ }
601
+ }
602
+
603
+ fn get_used ( & self , idx : usize ) -> bool {
604
+ let packed_rwu = self . packed_rwus [ idx] ;
605
+ match packed_rwu {
606
+ INV_INV_FALSE => false ,
607
+ INV_INV_TRUE => true ,
608
+ _ => self . unpacked_rwus [ packed_rwu as usize ] . used ,
609
+ }
610
+ }
611
+
612
+ #[ inline]
613
+ fn copy_packed ( & mut self , dst_idx : usize , src_idx : usize ) {
614
+ self . packed_rwus [ dst_idx] = self . packed_rwus [ src_idx] ;
615
+ }
616
+
617
+ fn assign_unpacked ( & mut self , idx : usize , rwu : RWU ) {
618
+ if rwu. reader == invalid_node ( ) && rwu. writer == invalid_node ( ) {
619
+ // When we overwrite an indexing entry in `self.packed_rwus` with
620
+ // `INV_INV_{TRUE,FALSE}` we don't remove the corresponding entry
621
+ // from `self.unpacked_rwus`; it's not worth the effort, and we
622
+ // can't have entries shifting around anyway.
623
+ self . packed_rwus [ idx] = if rwu. used {
624
+ INV_INV_TRUE
625
+ } else {
626
+ INV_INV_FALSE
627
+ }
628
+ } else {
629
+ // Add a new RWU to `unpacked_rwus` and make `packed_rwus[idx]`
630
+ // point to it.
631
+ self . packed_rwus [ idx] = self . unpacked_rwus . len ( ) as u32 ;
632
+ self . unpacked_rwus . push ( rwu) ;
633
+ }
634
+ }
635
+
636
+ fn assign_inv_inv ( & mut self , idx : usize ) {
637
+ self . packed_rwus [ idx] = if self . get_used ( idx) {
638
+ INV_INV_TRUE
639
+ } else {
640
+ INV_INV_FALSE
641
+ } ;
642
+ }
643
+ }
644
+
539
645
#[ derive( Copy , Clone ) ]
540
646
struct Specials {
541
647
exit_ln : LiveNode ,
@@ -552,14 +658,7 @@ struct Liveness<'a, 'tcx: 'a> {
552
658
tables : & ' a ty:: TypeckTables < ' tcx > ,
553
659
s : Specials ,
554
660
successors : Vec < LiveNode > ,
555
-
556
- // We used to have a single `users: Vec<Users>` field here, where `Users`
557
- // had `reader`, `writer` and `used` fields. But the number of users can
558
- // get very large, and it's more compact to store the data in three
559
- // separate `Vec`s so that no space is wasted for padding.
560
- users_reader : Vec < LiveNode > ,
561
- users_writer : Vec < LiveNode > ,
562
- users_used : Vec < bool > ,
661
+ rwu_table : RWUTable ,
563
662
564
663
// mappings from loop node ID to LiveNode
565
664
// ("break" label should map to loop node ID,
@@ -584,16 +683,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
584
683
585
684
let num_live_nodes = ir. num_live_nodes ;
586
685
let num_vars = ir. num_vars ;
587
- let num_users = num_live_nodes * num_vars;
588
686
589
687
Liveness {
590
688
ir,
591
689
tables,
592
690
s : specials,
593
691
successors : vec ! [ invalid_node( ) ; num_live_nodes] ,
594
- users_reader : vec ! [ invalid_node( ) ; num_users] ,
595
- users_writer : vec ! [ invalid_node( ) ; num_users] ,
596
- users_used : vec ! [ false ; num_users] ,
692
+ rwu_table : RWUTable :: new ( num_live_nodes * num_vars) ,
597
693
break_ln : NodeMap ( ) ,
598
694
cont_ln : NodeMap ( ) ,
599
695
}
@@ -657,16 +753,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
657
753
ln. get ( ) * self . ir . num_vars + var. get ( )
658
754
}
659
755
660
- fn live_on_entry ( & self , ln : LiveNode , var : Variable )
661
- -> Option < LiveNodeKind > {
756
+ fn live_on_entry ( & self , ln : LiveNode , var : Variable ) -> Option < LiveNodeKind > {
662
757
assert ! ( ln. is_valid( ) ) ;
663
- let reader = self . users_reader [ self . idx ( ln, var) ] ;
664
- if reader. is_valid ( ) { Some ( self . ir . lnk ( reader) ) } else { None }
758
+ let reader = self . rwu_table . get_reader ( self . idx ( ln, var) ) ;
759
+ if reader. is_valid ( ) { Some ( self . ir . lnk ( reader) ) } else { None }
665
760
}
666
761
667
- /*
668
- Is this variable live on entry to any of its successor nodes?
669
- */
762
+ // Is this variable live on entry to any of its successor nodes?
670
763
fn live_on_exit ( & self , ln : LiveNode , var : Variable )
671
764
-> Option < LiveNodeKind > {
672
765
let successor = self . successors [ ln. get ( ) ] ;
@@ -675,14 +768,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
675
768
676
769
fn used_on_entry ( & self , ln : LiveNode , var : Variable ) -> bool {
677
770
assert ! ( ln. is_valid( ) ) ;
678
- self . users_used [ self . idx ( ln, var) ]
771
+ self . rwu_table . get_used ( self . idx ( ln, var) )
679
772
}
680
773
681
774
fn assigned_on_entry ( & self , ln : LiveNode , var : Variable )
682
775
-> Option < LiveNodeKind > {
683
776
assert ! ( ln. is_valid( ) ) ;
684
- let writer = self . users_writer [ self . idx ( ln, var) ] ;
685
- if writer. is_valid ( ) { Some ( self . ir . lnk ( writer) ) } else { None }
777
+ let writer = self . rwu_table . get_writer ( self . idx ( ln, var) ) ;
778
+ if writer. is_valid ( ) { Some ( self . ir . lnk ( writer) ) } else { None }
686
779
}
687
780
688
781
fn assigned_on_exit ( & self , ln : LiveNode , var : Variable )
@@ -725,9 +818,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
725
818
{
726
819
let wr = & mut wr as & mut dyn Write ;
727
820
write ! ( wr, "[ln({:?}) of kind {:?} reads" , ln. get( ) , self . ir. lnk( ln) ) ;
728
- self . write_vars ( wr, ln, |idx| self . users_reader [ idx] ) ;
821
+ self . write_vars ( wr, ln, |idx| self . rwu_table . get_reader ( idx) ) ;
729
822
write ! ( wr, " writes" ) ;
730
- self . write_vars ( wr, ln, |idx| self . users_writer [ idx] ) ;
823
+ self . write_vars ( wr, ln, |idx| self . rwu_table . get_writer ( idx) ) ;
731
824
write ! ( wr, " precedes {:?}]" , self . successors[ ln. get( ) ] ) ;
732
825
}
733
826
String :: from_utf8 ( wr) . unwrap ( )
@@ -736,26 +829,17 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
736
829
fn init_empty ( & mut self , ln : LiveNode , succ_ln : LiveNode ) {
737
830
self . successors [ ln. get ( ) ] = succ_ln;
738
831
739
- // It is not necessary to initialize the
740
- // values to empty because this is the value
741
- // they have when they are created, and the sets
742
- // only grow during iterations.
743
- //
744
- // self.indices(ln) { |idx|
745
- // self.users_reader[idx] = invalid_node();
746
- // self.users_writer[idx] = invalid_node();
747
- // self.users_used[idx] = false;
748
- // }
832
+ // It is not necessary to initialize the RWUs here because they are all
833
+ // set to INV_INV_FALSE when they are created, and the sets only grow
834
+ // during iterations.
749
835
}
750
836
751
837
fn init_from_succ ( & mut self , ln : LiveNode , succ_ln : LiveNode ) {
752
838
// more efficient version of init_empty() / merge_from_succ()
753
839
self . successors [ ln. get ( ) ] = succ_ln;
754
840
755
841
self . indices2 ( ln, succ_ln, |this, idx, succ_idx| {
756
- this. users_reader [ idx] = this. users_reader [ succ_idx] ;
757
- this. users_writer [ idx] = this. users_writer [ succ_idx] ;
758
- this. users_used [ idx] = this. users_used [ succ_idx] ;
842
+ this. rwu_table . copy_packed ( idx, succ_idx) ;
759
843
} ) ;
760
844
debug ! ( "init_from_succ(ln={}, succ={})" ,
761
845
self . ln_str( ln) , self . ln_str( succ_ln) ) ;
@@ -770,35 +854,39 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
770
854
771
855
let mut changed = false ;
772
856
self . indices2 ( ln, succ_ln, |this, idx, succ_idx| {
773
- changed |= copy_if_invalid ( this. users_reader [ succ_idx] , & mut this. users_reader [ idx] ) ;
774
- changed |= copy_if_invalid ( this. users_writer [ succ_idx] , & mut this. users_writer [ idx] ) ;
775
- if this. users_used [ succ_idx] && !this. users_used [ idx] {
776
- this. users_used [ idx] = true ;
857
+ let mut rwu = this. rwu_table . get ( idx) ;
858
+ let succ_rwu = this. rwu_table . get ( succ_idx) ;
859
+ if succ_rwu. reader . is_valid ( ) && !rwu. reader . is_valid ( ) {
860
+ rwu. reader = succ_rwu. reader ;
861
+ changed = true
862
+ }
863
+
864
+ if succ_rwu. writer . is_valid ( ) && !rwu. writer . is_valid ( ) {
865
+ rwu. writer = succ_rwu. writer ;
866
+ changed = true
867
+ }
868
+
869
+ if succ_rwu. used && !rwu. used {
870
+ rwu. used = true ;
777
871
changed = true ;
778
872
}
873
+
874
+ if changed {
875
+ this. rwu_table . assign_unpacked ( idx, rwu) ;
876
+ }
779
877
} ) ;
780
878
781
879
debug ! ( "merge_from_succ(ln={:?}, succ={}, first_merge={}, changed={})" ,
782
880
ln, self . ln_str( succ_ln) , first_merge, changed) ;
783
881
return changed;
784
-
785
- fn copy_if_invalid ( src : LiveNode , dst : & mut LiveNode ) -> bool {
786
- if src. is_valid ( ) && !dst. is_valid ( ) {
787
- * dst = src;
788
- true
789
- } else {
790
- false
791
- }
792
- }
793
882
}
794
883
795
884
// Indicates that a local variable was *defined*; we know that no
796
885
// uses of the variable can precede the definition (resolve checks
797
886
// this) so we just clear out all the data.
798
887
fn define ( & mut self , writer : LiveNode , var : Variable ) {
799
888
let idx = self . idx ( writer, var) ;
800
- self . users_reader [ idx] = invalid_node ( ) ;
801
- self . users_writer [ idx] = invalid_node ( ) ;
889
+ self . rwu_table . assign_inv_inv ( idx) ;
802
890
803
891
debug ! ( "{:?} defines {:?} (idx={}): {}" , writer, var,
804
892
idx, self . ln_str( writer) ) ;
@@ -810,21 +898,24 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
810
898
ln, acc, var, self . ln_str( ln) ) ;
811
899
812
900
let idx = self . idx ( ln, var) ;
901
+ let mut rwu = self . rwu_table . get ( idx) ;
813
902
814
903
if ( acc & ACC_WRITE ) != 0 {
815
- self . users_reader [ idx ] = invalid_node ( ) ;
816
- self . users_writer [ idx ] = ln;
904
+ rwu . reader = invalid_node ( ) ;
905
+ rwu . writer = ln;
817
906
}
818
907
819
908
// Important: if we both read/write, must do read second
820
909
// or else the write will override.
821
910
if ( acc & ACC_READ ) != 0 {
822
- self . users_reader [ idx ] = ln;
911
+ rwu . reader = ln;
823
912
}
824
913
825
914
if ( acc & ACC_USE ) != 0 {
826
- self . users_used [ idx ] = true ;
915
+ rwu . used = true ;
827
916
}
917
+
918
+ self . rwu_table . assign_unpacked ( idx, rwu) ;
828
919
}
829
920
830
921
// _______________________________________________________________________
0 commit comments