1+ use std:: assert_matches:: debug_assert_matches;
12use std:: fmt:: { Debug , Formatter } ;
23use std:: ops:: Range ;
34
@@ -352,32 +353,47 @@ pub struct Map<'tcx> {
352353 projections : FxHashMap < ( PlaceIndex , TrackElem ) , PlaceIndex > ,
353354 places : IndexVec < PlaceIndex , PlaceInfo < ' tcx > > ,
354355 value_count : usize ,
356+ mode : PlaceCollectionMode ,
355357 // The Range corresponds to a slice into `inner_values_buffer`.
356358 inner_values : IndexVec < PlaceIndex , Range < usize > > ,
357359 inner_values_buffer : Vec < ValueIndex > ,
358360}
359361
362+ #[ derive( Copy , Clone , Debug ) ]
363+ pub enum PlaceCollectionMode {
364+ Full { value_limit : Option < usize > } ,
365+ OnDemand ,
366+ }
367+
360368impl < ' tcx > Map < ' tcx > {
361369 /// Returns a map that only tracks places whose type has scalar layout.
362370 ///
363371 /// This is currently the only way to create a [`Map`]. The way in which the tracked places are
364372 /// chosen is an implementation detail and may not be relied upon (other than that their type
365373 /// are scalars).
366- pub fn new ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > , value_limit : Option < usize > ) -> Self {
367- let capacity = 4 * body. local_decls . len ( ) + value_limit. unwrap_or ( body. local_decls . len ( ) ) ;
374+ #[ tracing:: instrument( level = "trace" , skip( tcx, body) ) ]
375+ pub fn new ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > , mode : PlaceCollectionMode ) -> Self {
376+ tracing:: trace!( def_id=?body. source. def_id( ) ) ;
377+ let capacity = 4 * body. local_decls . len ( ) ;
368378 let mut map = Self {
369379 locals : IndexVec :: from_elem ( None , & body. local_decls ) ,
370380 projections : FxHashMap :: default ( ) ,
371381 places : IndexVec :: with_capacity ( capacity) ,
372382 value_count : 0 ,
373- inner_values : IndexVec :: with_capacity ( capacity) ,
383+ mode,
384+ inner_values : IndexVec :: new ( ) ,
374385 inner_values_buffer : Vec :: new ( ) ,
375386 } ;
376387 map. register_locals ( tcx, body) ;
377- map. collect_places ( tcx, body) ;
378- map. propagate_assignments ( tcx, body) ;
379- map. create_values ( tcx, body, value_limit) ;
380- map. trim_useless_places ( ) ;
388+ match mode {
389+ PlaceCollectionMode :: Full { value_limit } => {
390+ map. collect_places ( tcx, body) ;
391+ map. propagate_assignments ( tcx, body) ;
392+ map. create_values ( tcx, body, value_limit) ;
393+ map. trim_useless_places ( ) ;
394+ }
395+ PlaceCollectionMode :: OnDemand => { }
396+ }
381397 debug ! ( "registered {} places ({} nodes in total)" , map. value_count, map. places. len( ) ) ;
382398 map
383399 }
@@ -429,12 +445,18 @@ impl<'tcx> Map<'tcx> {
429445 match rhs {
430446 Rvalue :: Use ( Operand :: Move ( rhs) | Operand :: Copy ( rhs) )
431447 | Rvalue :: CopyForDeref ( rhs) => {
432- let Some ( lhs) = self . register_place ( tcx, body, * lhs) else { continue } ;
433- let Some ( rhs) = self . register_place ( tcx, body, * rhs) else { continue } ;
448+ let Some ( lhs) = self . register_place_and_discr ( tcx, body, * lhs) else {
449+ continue ;
450+ } ;
451+ let Some ( rhs) = self . register_place_and_discr ( tcx, body, * rhs) else {
452+ continue ;
453+ } ;
434454 assignments. insert ( ( lhs, rhs) ) ;
435455 }
436456 Rvalue :: Aggregate ( kind, fields) => {
437- let Some ( mut lhs) = self . register_place ( tcx, body, * lhs) else { continue } ;
457+ let Some ( mut lhs) = self . register_place_and_discr ( tcx, body, * lhs) else {
458+ continue ;
459+ } ;
438460 match * * kind {
439461 // Do not propagate unions.
440462 AggregateKind :: Adt ( _, _, _, _, Some ( _) ) => continue ,
@@ -457,7 +479,7 @@ impl<'tcx> Map<'tcx> {
457479 }
458480 for ( index, field) in fields. iter_enumerated ( ) {
459481 if let Some ( rhs) = field. place ( )
460- && let Some ( rhs) = self . register_place ( tcx, body, rhs)
482+ && let Some ( rhs) = self . register_place_and_discr ( tcx, body, rhs)
461483 {
462484 let lhs = self . register_place_index (
463485 self . places [ rhs] . ty ,
@@ -514,6 +536,7 @@ impl<'tcx> Map<'tcx> {
514536 /// Create values for places whose type have scalar layout.
515537 #[ tracing:: instrument( level = "trace" , skip( self , tcx, body) ) ]
516538 fn create_values ( & mut self , tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > , value_limit : Option < usize > ) {
539+ debug_assert_matches ! ( self . mode, PlaceCollectionMode :: Full { .. } ) ;
517540 let typing_env = body. typing_env ( tcx) ;
518541 for place_info in self . places . iter_mut ( ) {
519542 // The user requires a bound on the number of created values.
@@ -552,6 +575,7 @@ impl<'tcx> Map<'tcx> {
552575 /// Trim useless places.
553576 #[ tracing:: instrument( level = "trace" , skip( self ) ) ]
554577 fn trim_useless_places ( & mut self ) {
578+ debug_assert_matches ! ( self . mode, PlaceCollectionMode :: Full { .. } ) ;
555579 for opt_place in self . locals . iter_mut ( ) {
556580 if let Some ( place) = * opt_place
557581 && self . inner_values [ place] . is_empty ( )
@@ -564,7 +588,7 @@ impl<'tcx> Map<'tcx> {
564588 }
565589
566590 #[ tracing:: instrument( level = "trace" , skip( self ) , ret) ]
567- fn register_place_index (
591+ pub fn register_place_index (
568592 & mut self ,
569593 ty : Ty < ' tcx > ,
570594 base : PlaceIndex ,
@@ -578,49 +602,124 @@ impl<'tcx> Map<'tcx> {
578602 } )
579603 }
580604
581- #[ tracing:: instrument( level = "trace" , skip( self , tcx, body) ) ]
582- fn register_place (
605+ #[ tracing:: instrument( level = "trace" , skip( self , tcx, body) , ret ) ]
606+ pub fn register_place (
583607 & mut self ,
584608 tcx : TyCtxt < ' tcx > ,
585609 body : & Body < ' tcx > ,
586610 place : Place < ' tcx > ,
611+ tail : Option < TrackElem > ,
587612 ) -> Option < PlaceIndex > {
588613 // Create a place for this projection.
589614 let mut place_index = self . locals [ place. local ] ?;
590615 let mut ty = PlaceTy :: from_ty ( body. local_decls [ place. local ] . ty ) ;
591616 tracing:: trace!( ?place_index, ?ty) ;
592617
593- if let ty:: Ref ( _, ref_ty, _) | ty:: RawPtr ( ref_ty, _) = ty. ty . kind ( )
594- && let ty:: Slice ( ..) = ref_ty. kind ( )
595- {
596- self . register_place_index ( tcx. types . usize , place_index, TrackElem :: DerefLen ) ;
597- } else if ty. ty . is_enum ( ) {
598- let discriminant_ty = ty. ty . discriminant_ty ( tcx) ;
599- self . register_place_index ( discriminant_ty, place_index, TrackElem :: Discriminant ) ;
600- }
601-
602618 for proj in place. projection {
603619 let track_elem = proj. try_into ( ) . ok ( ) ?;
604620 ty = ty. projection_ty ( tcx, proj) ;
605621 place_index = self . register_place_index ( ty. ty , place_index, track_elem) ;
606622 tracing:: trace!( ?proj, ?place_index, ?ty) ;
623+ }
607624
608- if let ty:: Ref ( _, ref_ty, _) | ty:: RawPtr ( ref_ty, _) = ty. ty . kind ( )
609- && let ty:: Slice ( ..) = ref_ty. kind ( )
610- {
611- self . register_place_index ( tcx. types . usize , place_index, TrackElem :: DerefLen ) ;
612- } else if ty. ty . is_enum ( ) {
613- let discriminant_ty = ty. ty . discriminant_ty ( tcx) ;
614- self . register_place_index ( discriminant_ty, place_index, TrackElem :: Discriminant ) ;
615- }
625+ if let Some ( tail) = tail {
626+ let ty = match tail {
627+ TrackElem :: Discriminant => ty. ty . discriminant_ty ( tcx) ,
628+ TrackElem :: Variant ( ..) | TrackElem :: Field ( ..) => todo ! ( ) ,
629+ TrackElem :: DerefLen => tcx. types . usize ,
630+ } ;
631+ place_index = self . register_place_index ( ty, place_index, tail) ;
616632 }
617633
618634 Some ( place_index)
619635 }
620636
637+ #[ tracing:: instrument( level = "trace" , skip( self , tcx, body) , ret) ]
638+ fn register_place_and_discr (
639+ & mut self ,
640+ tcx : TyCtxt < ' tcx > ,
641+ body : & Body < ' tcx > ,
642+ place : Place < ' tcx > ,
643+ ) -> Option < PlaceIndex > {
644+ let place = self . register_place ( tcx, body, place, None ) ?;
645+ let ty = self . places [ place] . ty ;
646+
647+ if let ty:: Ref ( _, ref_ty, _) | ty:: RawPtr ( ref_ty, _) = ty. kind ( )
648+ && let ty:: Slice ( ..) = ref_ty. kind ( )
649+ {
650+ self . register_place_index ( tcx. types . usize , place, TrackElem :: DerefLen ) ;
651+ } else if ty. is_enum ( ) {
652+ let discriminant_ty = ty. discriminant_ty ( tcx) ;
653+ self . register_place_index ( discriminant_ty, place, TrackElem :: Discriminant ) ;
654+ }
655+
656+ Some ( place)
657+ }
658+
659+ #[ tracing:: instrument( level = "trace" , skip( self , tcx, typing_env) , ret) ]
660+ pub fn register_value (
661+ & mut self ,
662+ tcx : TyCtxt < ' tcx > ,
663+ typing_env : ty:: TypingEnv < ' tcx > ,
664+ place : PlaceIndex ,
665+ ) -> Option < ValueIndex > {
666+ let place_info = & mut self . places [ place] ;
667+ if let Some ( value) = place_info. value_index {
668+ return Some ( value) ;
669+ }
670+
671+ if let Ok ( ty) = tcx. try_normalize_erasing_regions ( typing_env, place_info. ty ) {
672+ place_info. ty = ty;
673+ }
674+
675+ // Allocate a value slot if it doesn't have one, and the user requested one.
676+ if let Ok ( layout) = tcx. layout_of ( typing_env. as_query_input ( place_info. ty ) )
677+ && layout. backend_repr . is_scalar ( )
678+ {
679+ place_info. value_index = Some ( self . value_count . into ( ) ) ;
680+ self . value_count += 1 ;
681+ }
682+
683+ place_info. value_index
684+ }
685+
686+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
687+ pub fn register_copy_tree (
688+ & mut self ,
689+ // Tree to copy.
690+ source : PlaceIndex ,
691+ // Tree to build.
692+ target : PlaceIndex ,
693+ f : & mut impl FnMut ( ValueIndex , ValueIndex ) ,
694+ ) {
695+ if let Some ( source_value) = self . places [ source] . value_index {
696+ let target_value = * self . places [ target] . value_index . get_or_insert_with ( || {
697+ let value_index = self . value_count . into ( ) ;
698+ self . value_count += 1 ;
699+ value_index
700+ } ) ;
701+ f ( source_value, target_value)
702+ }
703+
704+ // Iterate over `source` children and recurse.
705+ let mut source_child_iter = self . places [ source] . first_child ;
706+ while let Some ( source_child) = source_child_iter {
707+ source_child_iter = self . places [ source_child] . next_sibling ;
708+
709+ // Try to find corresponding child and recurse. Reasoning is similar as above.
710+ let source_info = & self . places [ source_child] ;
711+ let source_ty = source_info. ty ;
712+ let source_elem = source_info. proj_elem . unwrap ( ) ;
713+ let target_child = self . register_place_index ( source_ty, target, source_elem) ;
714+ self . register_copy_tree ( source_child, target_child, f) ;
715+ }
716+ }
717+
621718 /// Precompute the list of values inside `root` and store it inside
622719 /// as a slice within `inner_values_buffer`.
720+ #[ tracing:: instrument( level = "trace" , skip( self ) ) ]
623721 fn cache_preorder_invoke ( & mut self , root : PlaceIndex ) {
722+ debug_assert_matches ! ( self . mode, PlaceCollectionMode :: Full { .. } ) ;
624723 let start = self . inner_values_buffer . len ( ) ;
625724 if let Some ( vi) = self . places [ root] . value_index {
626725 self . inner_values_buffer . push ( vi) ;
@@ -651,7 +750,7 @@ impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, 'tcx> {
651750 return ;
652751 }
653752
654- self . map . register_place ( self . tcx , self . body , * place) ;
753+ self . map . register_place_and_discr ( self . tcx , self . body , * place) ;
655754 }
656755}
657756
@@ -726,6 +825,7 @@ impl<'tcx> Map<'tcx> {
726825 ///
727826 /// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track
728827 /// as such.
828+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
729829 pub fn for_each_aliasing_place (
730830 & self ,
731831 place : PlaceRef < ' _ > ,
@@ -763,6 +863,7 @@ impl<'tcx> Map<'tcx> {
763863 }
764864
765865 /// Invoke the given function on all the descendants of the given place, except one branch.
866+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
766867 fn for_each_variant_sibling (
767868 & self ,
768869 parent : PlaceIndex ,
@@ -783,11 +884,22 @@ impl<'tcx> Map<'tcx> {
783884 }
784885
785886 /// Invoke a function on each value in the given place and all descendants.
887+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
786888 fn for_each_value_inside ( & self , root : PlaceIndex , f : & mut impl FnMut ( ValueIndex ) ) {
787- let range = self . inner_values [ root] . clone ( ) ;
788- let values = & self . inner_values_buffer [ range] ;
789- for & v in values {
790- f ( v)
889+ if let Some ( range) = self . inner_values . get ( root) {
890+ // Optimized path: we have cached the inner values.
891+ let values = & self . inner_values_buffer [ range. clone ( ) ] ;
892+ for & v in values {
893+ f ( v)
894+ }
895+ } else {
896+ if let Some ( root) = self . places [ root] . value_index {
897+ f ( root)
898+ }
899+
900+ for child in self . children ( root) {
901+ self . for_each_value_inside ( child, f) ;
902+ }
791903 }
792904 }
793905
@@ -800,7 +912,9 @@ impl<'tcx> Map<'tcx> {
800912 f : & mut impl FnMut ( PlaceIndex , & O ) ,
801913 ) {
802914 // Fast path is there is nothing to do.
803- if self . inner_values [ root] . is_empty ( ) {
915+ if let Some ( value_range) = self . inner_values . get ( root)
916+ && value_range. is_empty ( )
917+ {
804918 return ;
805919 }
806920
0 commit comments