6464//! methods. It effectively does a reverse walk of the AST; whenever we
6565//! reach a loop node, we iterate until a fixed point is reached.
6666//!
67- //! ## The `users_*` fields
67+ //! ## The `RWU` struct
6868//!
6969//! 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 ):
7171//!
7272//! - `reader`: the `LiveNode` ID of some node which will read the value
7373//! 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) {
536536// Actually we compute just a bit more than just liveness, but we use
537537// the same basic propagation framework in all cases.
538538
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+
539645#[ derive( Copy , Clone ) ]
540646struct Specials {
541647 exit_ln : LiveNode ,
@@ -552,14 +658,7 @@ struct Liveness<'a, 'tcx: 'a> {
552658 tables : & ' a ty:: TypeckTables < ' tcx > ,
553659 s : Specials ,
554660 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 ,
563662
564663 // mappings from loop node ID to LiveNode
565664 // ("break" label should map to loop node ID,
@@ -584,16 +683,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
584683
585684 let num_live_nodes = ir. num_live_nodes ;
586685 let num_vars = ir. num_vars ;
587- let num_users = num_live_nodes * num_vars;
588686
589687 Liveness {
590688 ir,
591689 tables,
592690 s : specials,
593691 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) ,
597693 break_ln : NodeMap ( ) ,
598694 cont_ln : NodeMap ( ) ,
599695 }
@@ -657,16 +753,13 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
657753 ln. get ( ) * self . ir . num_vars + var. get ( )
658754 }
659755
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 > {
662757 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 }
665760 }
666761
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?
670763 fn live_on_exit ( & self , ln : LiveNode , var : Variable )
671764 -> Option < LiveNodeKind > {
672765 let successor = self . successors [ ln. get ( ) ] ;
@@ -675,14 +768,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
675768
676769 fn used_on_entry ( & self , ln : LiveNode , var : Variable ) -> bool {
677770 assert ! ( ln. is_valid( ) ) ;
678- self . users_used [ self . idx ( ln, var) ]
771+ self . rwu_table . get_used ( self . idx ( ln, var) )
679772 }
680773
681774 fn assigned_on_entry ( & self , ln : LiveNode , var : Variable )
682775 -> Option < LiveNodeKind > {
683776 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 }
686779 }
687780
688781 fn assigned_on_exit ( & self , ln : LiveNode , var : Variable )
@@ -725,9 +818,9 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
725818 {
726819 let wr = & mut wr as & mut dyn Write ;
727820 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) ) ;
729822 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) ) ;
731824 write ! ( wr, " precedes {:?}]" , self . successors[ ln. get( ) ] ) ;
732825 }
733826 String :: from_utf8 ( wr) . unwrap ( )
@@ -736,26 +829,17 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
736829 fn init_empty ( & mut self , ln : LiveNode , succ_ln : LiveNode ) {
737830 self . successors [ ln. get ( ) ] = succ_ln;
738831
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.
749835 }
750836
751837 fn init_from_succ ( & mut self , ln : LiveNode , succ_ln : LiveNode ) {
752838 // more efficient version of init_empty() / merge_from_succ()
753839 self . successors [ ln. get ( ) ] = succ_ln;
754840
755841 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) ;
759843 } ) ;
760844 debug ! ( "init_from_succ(ln={}, succ={})" ,
761845 self . ln_str( ln) , self . ln_str( succ_ln) ) ;
@@ -770,35 +854,39 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
770854
771855 let mut changed = false ;
772856 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 ;
777871 changed = true ;
778872 }
873+
874+ if changed {
875+ this. rwu_table . assign_unpacked ( idx, rwu) ;
876+ }
779877 } ) ;
780878
781879 debug ! ( "merge_from_succ(ln={:?}, succ={}, first_merge={}, changed={})" ,
782880 ln, self . ln_str( succ_ln) , first_merge, changed) ;
783881 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- }
793882 }
794883
795884 // Indicates that a local variable was *defined*; we know that no
796885 // uses of the variable can precede the definition (resolve checks
797886 // this) so we just clear out all the data.
798887 fn define ( & mut self , writer : LiveNode , var : Variable ) {
799888 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) ;
802890
803891 debug ! ( "{:?} defines {:?} (idx={}): {}" , writer, var,
804892 idx, self . ln_str( writer) ) ;
@@ -810,21 +898,24 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
810898 ln, acc, var, self . ln_str( ln) ) ;
811899
812900 let idx = self . idx ( ln, var) ;
901+ let mut rwu = self . rwu_table . get ( idx) ;
813902
814903 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;
817906 }
818907
819908 // Important: if we both read/write, must do read second
820909 // or else the write will override.
821910 if ( acc & ACC_READ ) != 0 {
822- self . users_reader [ idx ] = ln;
911+ rwu . reader = ln;
823912 }
824913
825914 if ( acc & ACC_USE ) != 0 {
826- self . users_used [ idx ] = true ;
915+ rwu . used = true ;
827916 }
917+
918+ self . rwu_table . assign_unpacked ( idx, rwu) ;
828919 }
829920
830921 // _______________________________________________________________________
0 commit comments