1
+ use std:: assert_matches:: debug_assert_matches;
1
2
use std:: fmt:: { Debug , Formatter } ;
2
3
use std:: ops:: Range ;
3
4
@@ -352,32 +353,47 @@ pub struct Map<'tcx> {
352
353
projections : FxHashMap < ( PlaceIndex , TrackElem ) , PlaceIndex > ,
353
354
places : IndexVec < PlaceIndex , PlaceInfo < ' tcx > > ,
354
355
value_count : usize ,
356
+ mode : PlaceCollectionMode ,
355
357
// The Range corresponds to a slice into `inner_values_buffer`.
356
358
inner_values : IndexVec < PlaceIndex , Range < usize > > ,
357
359
inner_values_buffer : Vec < ValueIndex > ,
358
360
}
359
361
362
+ #[ derive( Copy , Clone , Debug ) ]
363
+ pub enum PlaceCollectionMode {
364
+ Full { value_limit : Option < usize > } ,
365
+ OnDemand ,
366
+ }
367
+
360
368
impl < ' tcx > Map < ' tcx > {
361
369
/// Returns a map that only tracks places whose type has scalar layout.
362
370
///
363
371
/// This is currently the only way to create a [`Map`]. The way in which the tracked places are
364
372
/// chosen is an implementation detail and may not be relied upon (other than that their type
365
373
/// 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 ( ) ;
368
378
let mut map = Self {
369
379
locals : IndexVec :: from_elem ( None , & body. local_decls ) ,
370
380
projections : FxHashMap :: default ( ) ,
371
381
places : IndexVec :: with_capacity ( capacity) ,
372
382
value_count : 0 ,
373
- inner_values : IndexVec :: with_capacity ( capacity) ,
383
+ mode,
384
+ inner_values : IndexVec :: new ( ) ,
374
385
inner_values_buffer : Vec :: new ( ) ,
375
386
} ;
376
387
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
+ }
381
397
debug ! ( "registered {} places ({} nodes in total)" , map. value_count, map. places. len( ) ) ;
382
398
map
383
399
}
@@ -429,12 +445,18 @@ impl<'tcx> Map<'tcx> {
429
445
match rhs {
430
446
Rvalue :: Use ( Operand :: Move ( rhs) | Operand :: Copy ( rhs) )
431
447
| 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
+ } ;
434
454
assignments. insert ( ( lhs, rhs) ) ;
435
455
}
436
456
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
+ } ;
438
460
match * * kind {
439
461
// Do not propagate unions.
440
462
AggregateKind :: Adt ( _, _, _, _, Some ( _) ) => continue ,
@@ -457,7 +479,7 @@ impl<'tcx> Map<'tcx> {
457
479
}
458
480
for ( index, field) in fields. iter_enumerated ( ) {
459
481
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)
461
483
{
462
484
let lhs = self . register_place_index (
463
485
self . places [ rhs] . ty ,
@@ -514,6 +536,7 @@ impl<'tcx> Map<'tcx> {
514
536
/// Create values for places whose type have scalar layout.
515
537
#[ tracing:: instrument( level = "trace" , skip( self , tcx, body) ) ]
516
538
fn create_values ( & mut self , tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > , value_limit : Option < usize > ) {
539
+ debug_assert_matches ! ( self . mode, PlaceCollectionMode :: Full { .. } ) ;
517
540
let typing_env = body. typing_env ( tcx) ;
518
541
for place_info in self . places . iter_mut ( ) {
519
542
// The user requires a bound on the number of created values.
@@ -552,6 +575,7 @@ impl<'tcx> Map<'tcx> {
552
575
/// Trim useless places.
553
576
#[ tracing:: instrument( level = "trace" , skip( self ) ) ]
554
577
fn trim_useless_places ( & mut self ) {
578
+ debug_assert_matches ! ( self . mode, PlaceCollectionMode :: Full { .. } ) ;
555
579
for opt_place in self . locals . iter_mut ( ) {
556
580
if let Some ( place) = * opt_place
557
581
&& self . inner_values [ place] . is_empty ( )
@@ -564,7 +588,7 @@ impl<'tcx> Map<'tcx> {
564
588
}
565
589
566
590
#[ tracing:: instrument( level = "trace" , skip( self ) , ret) ]
567
- fn register_place_index (
591
+ pub fn register_place_index (
568
592
& mut self ,
569
593
ty : Ty < ' tcx > ,
570
594
base : PlaceIndex ,
@@ -578,49 +602,124 @@ impl<'tcx> Map<'tcx> {
578
602
} )
579
603
}
580
604
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 (
583
607
& mut self ,
584
608
tcx : TyCtxt < ' tcx > ,
585
609
body : & Body < ' tcx > ,
586
610
place : Place < ' tcx > ,
611
+ tail : Option < TrackElem > ,
587
612
) -> Option < PlaceIndex > {
588
613
// Create a place for this projection.
589
614
let mut place_index = self . locals [ place. local ] ?;
590
615
let mut ty = PlaceTy :: from_ty ( body. local_decls [ place. local ] . ty ) ;
591
616
tracing:: trace!( ?place_index, ?ty) ;
592
617
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
-
602
618
for proj in place. projection {
603
619
let track_elem = proj. try_into ( ) . ok ( ) ?;
604
620
ty = ty. projection_ty ( tcx, proj) ;
605
621
place_index = self . register_place_index ( ty. ty , place_index, track_elem) ;
606
622
tracing:: trace!( ?proj, ?place_index, ?ty) ;
623
+ }
607
624
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) ;
616
632
}
617
633
618
634
Some ( place_index)
619
635
}
620
636
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
+
621
718
/// Precompute the list of values inside `root` and store it inside
622
719
/// as a slice within `inner_values_buffer`.
720
+ #[ tracing:: instrument( level = "trace" , skip( self ) ) ]
623
721
fn cache_preorder_invoke ( & mut self , root : PlaceIndex ) {
722
+ debug_assert_matches ! ( self . mode, PlaceCollectionMode :: Full { .. } ) ;
624
723
let start = self . inner_values_buffer . len ( ) ;
625
724
if let Some ( vi) = self . places [ root] . value_index {
626
725
self . inner_values_buffer . push ( vi) ;
@@ -651,7 +750,7 @@ impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, 'tcx> {
651
750
return ;
652
751
}
653
752
654
- self . map . register_place ( self . tcx , self . body , * place) ;
753
+ self . map . register_place_and_discr ( self . tcx , self . body , * place) ;
655
754
}
656
755
}
657
756
@@ -726,6 +825,7 @@ impl<'tcx> Map<'tcx> {
726
825
///
727
826
/// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track
728
827
/// as such.
828
+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
729
829
pub fn for_each_aliasing_place (
730
830
& self ,
731
831
place : PlaceRef < ' _ > ,
@@ -763,6 +863,7 @@ impl<'tcx> Map<'tcx> {
763
863
}
764
864
765
865
/// Invoke the given function on all the descendants of the given place, except one branch.
866
+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
766
867
fn for_each_variant_sibling (
767
868
& self ,
768
869
parent : PlaceIndex ,
@@ -783,11 +884,22 @@ impl<'tcx> Map<'tcx> {
783
884
}
784
885
785
886
/// Invoke a function on each value in the given place and all descendants.
887
+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
786
888
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
+ }
791
903
}
792
904
}
793
905
@@ -800,7 +912,9 @@ impl<'tcx> Map<'tcx> {
800
912
f : & mut impl FnMut ( PlaceIndex , & O ) ,
801
913
) {
802
914
// 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
+ {
804
918
return ;
805
919
}
806
920
0 commit comments