1
+ use std:: assert_matches:: debug_assert_matches;
1
2
use std:: fmt:: { Debug , Formatter } ;
2
3
use std:: ops:: Range ;
3
4
@@ -350,32 +351,47 @@ pub struct Map<'tcx> {
350
351
projections : FxHashMap < ( PlaceIndex , TrackElem ) , PlaceIndex > ,
351
352
places : IndexVec < PlaceIndex , PlaceInfo < ' tcx > > ,
352
353
value_count : usize ,
354
+ mode : PlaceCollectionMode ,
353
355
// The Range corresponds to a slice into `inner_values_buffer`.
354
356
inner_values : IndexVec < PlaceIndex , Range < usize > > ,
355
357
inner_values_buffer : Vec < ValueIndex > ,
356
358
}
357
359
360
+ #[ derive( Copy , Clone , Debug ) ]
361
+ pub enum PlaceCollectionMode {
362
+ Full { value_limit : Option < usize > } ,
363
+ OnDemand ,
364
+ }
365
+
358
366
impl < ' tcx > Map < ' tcx > {
359
367
/// Returns a map that only tracks places whose type has scalar layout.
360
368
///
361
369
/// This is currently the only way to create a [`Map`]. The way in which the tracked places are
362
370
/// chosen is an implementation detail and may not be relied upon (other than that their type
363
371
/// are scalars).
364
- pub fn new ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > , value_limit : Option < usize > ) -> Self {
365
- let capacity = 4 * body. local_decls . len ( ) + value_limit. unwrap_or ( body. local_decls . len ( ) ) ;
372
+ #[ tracing:: instrument( level = "trace" , skip( tcx, body) ) ]
373
+ pub fn new ( tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > , mode : PlaceCollectionMode ) -> Self {
374
+ tracing:: trace!( def_id=?body. source. def_id( ) ) ;
375
+ let capacity = 4 * body. local_decls . len ( ) ;
366
376
let mut map = Self {
367
377
locals : IndexVec :: from_elem ( None , & body. local_decls ) ,
368
378
projections : FxHashMap :: default ( ) ,
369
379
places : IndexVec :: with_capacity ( capacity) ,
370
380
value_count : 0 ,
371
- inner_values : IndexVec :: with_capacity ( capacity) ,
381
+ mode,
382
+ inner_values : IndexVec :: new ( ) ,
372
383
inner_values_buffer : Vec :: new ( ) ,
373
384
} ;
374
385
map. register_locals ( tcx, body) ;
375
- map. collect_places ( tcx, body) ;
376
- map. propagate_assignments ( tcx, body) ;
377
- map. create_values ( tcx, body, value_limit) ;
378
- map. trim_useless_places ( ) ;
386
+ match mode {
387
+ PlaceCollectionMode :: Full { value_limit } => {
388
+ map. collect_places ( tcx, body) ;
389
+ map. propagate_assignments ( tcx, body) ;
390
+ map. create_values ( tcx, body, value_limit) ;
391
+ map. trim_useless_places ( ) ;
392
+ }
393
+ PlaceCollectionMode :: OnDemand => { }
394
+ }
379
395
debug ! ( "registered {} places ({} nodes in total)" , map. value_count, map. places. len( ) ) ;
380
396
map
381
397
}
@@ -427,12 +443,18 @@ impl<'tcx> Map<'tcx> {
427
443
match rhs {
428
444
Rvalue :: Use ( Operand :: Move ( rhs) | Operand :: Copy ( rhs) )
429
445
| Rvalue :: CopyForDeref ( rhs) => {
430
- let Some ( lhs) = self . register_place ( tcx, body, * lhs) else { continue } ;
431
- let Some ( rhs) = self . register_place ( tcx, body, * rhs) else { continue } ;
446
+ let Some ( lhs) = self . register_place_and_discr ( tcx, body, * lhs) else {
447
+ continue ;
448
+ } ;
449
+ let Some ( rhs) = self . register_place_and_discr ( tcx, body, * rhs) else {
450
+ continue ;
451
+ } ;
432
452
assignments. insert ( ( lhs, rhs) ) ;
433
453
}
434
454
Rvalue :: Aggregate ( kind, fields) => {
435
- let Some ( mut lhs) = self . register_place ( tcx, body, * lhs) else { continue } ;
455
+ let Some ( mut lhs) = self . register_place_and_discr ( tcx, body, * lhs) else {
456
+ continue ;
457
+ } ;
436
458
match * * kind {
437
459
// Do not propagate unions.
438
460
AggregateKind :: Adt ( _, _, _, _, Some ( _) ) => continue ,
@@ -455,7 +477,7 @@ impl<'tcx> Map<'tcx> {
455
477
}
456
478
for ( index, field) in fields. iter_enumerated ( ) {
457
479
if let Some ( rhs) = field. place ( )
458
- && let Some ( rhs) = self . register_place ( tcx, body, rhs)
480
+ && let Some ( rhs) = self . register_place_and_discr ( tcx, body, rhs)
459
481
{
460
482
let lhs = self . register_place_index (
461
483
self . places [ rhs] . ty ,
@@ -512,6 +534,7 @@ impl<'tcx> Map<'tcx> {
512
534
/// Create values for places whose type have scalar layout.
513
535
#[ tracing:: instrument( level = "trace" , skip( self , tcx, body) ) ]
514
536
fn create_values ( & mut self , tcx : TyCtxt < ' tcx > , body : & Body < ' tcx > , value_limit : Option < usize > ) {
537
+ debug_assert_matches ! ( self . mode, PlaceCollectionMode :: Full { .. } ) ;
515
538
let typing_env = body. typing_env ( tcx) ;
516
539
for place_info in self . places . iter_mut ( ) {
517
540
// The user requires a bound on the number of created values.
@@ -550,6 +573,7 @@ impl<'tcx> Map<'tcx> {
550
573
/// Trim useless places.
551
574
#[ tracing:: instrument( level = "trace" , skip( self ) ) ]
552
575
fn trim_useless_places ( & mut self ) {
576
+ debug_assert_matches ! ( self . mode, PlaceCollectionMode :: Full { .. } ) ;
553
577
for opt_place in self . locals . iter_mut ( ) {
554
578
if let Some ( place) = * opt_place
555
579
&& self . inner_values [ place] . is_empty ( )
@@ -562,7 +586,7 @@ impl<'tcx> Map<'tcx> {
562
586
}
563
587
564
588
#[ tracing:: instrument( level = "trace" , skip( self ) , ret) ]
565
- fn register_place_index (
589
+ pub fn register_place_index (
566
590
& mut self ,
567
591
ty : Ty < ' tcx > ,
568
592
base : PlaceIndex ,
@@ -576,49 +600,124 @@ impl<'tcx> Map<'tcx> {
576
600
} )
577
601
}
578
602
579
- #[ tracing:: instrument( level = "trace" , skip( self , tcx, body) ) ]
580
- fn register_place (
603
+ #[ tracing:: instrument( level = "trace" , skip( self , tcx, body) , ret ) ]
604
+ pub fn register_place (
581
605
& mut self ,
582
606
tcx : TyCtxt < ' tcx > ,
583
607
body : & Body < ' tcx > ,
584
608
place : Place < ' tcx > ,
609
+ tail : Option < TrackElem > ,
585
610
) -> Option < PlaceIndex > {
586
611
// Create a place for this projection.
587
612
let mut place_index = self . locals [ place. local ] ?;
588
613
let mut ty = PlaceTy :: from_ty ( body. local_decls [ place. local ] . ty ) ;
589
614
tracing:: trace!( ?place_index, ?ty) ;
590
615
591
- if let ty:: Ref ( _, ref_ty, _) | ty:: RawPtr ( ref_ty, _) = ty. ty . kind ( )
592
- && let ty:: Slice ( ..) = ref_ty. kind ( )
593
- {
594
- self . register_place_index ( tcx. types . usize , place_index, TrackElem :: DerefLen ) ;
595
- } else if ty. ty . is_enum ( ) {
596
- let discriminant_ty = ty. ty . discriminant_ty ( tcx) ;
597
- self . register_place_index ( discriminant_ty, place_index, TrackElem :: Discriminant ) ;
598
- }
599
-
600
616
for proj in place. projection {
601
617
let track_elem = proj. try_into ( ) . ok ( ) ?;
602
618
ty = ty. projection_ty ( tcx, proj) ;
603
619
place_index = self . register_place_index ( ty. ty , place_index, track_elem) ;
604
620
tracing:: trace!( ?proj, ?place_index, ?ty) ;
621
+ }
605
622
606
- if let ty:: Ref ( _, ref_ty, _) | ty:: RawPtr ( ref_ty, _) = ty. ty . kind ( )
607
- && let ty:: Slice ( ..) = ref_ty. kind ( )
608
- {
609
- self . register_place_index ( tcx. types . usize , place_index, TrackElem :: DerefLen ) ;
610
- } else if ty. ty . is_enum ( ) {
611
- let discriminant_ty = ty. ty . discriminant_ty ( tcx) ;
612
- self . register_place_index ( discriminant_ty, place_index, TrackElem :: Discriminant ) ;
613
- }
623
+ if let Some ( tail) = tail {
624
+ let ty = match tail {
625
+ TrackElem :: Discriminant => ty. ty . discriminant_ty ( tcx) ,
626
+ TrackElem :: Variant ( ..) | TrackElem :: Field ( ..) => todo ! ( ) ,
627
+ TrackElem :: DerefLen => tcx. types . usize ,
628
+ } ;
629
+ place_index = self . register_place_index ( ty, place_index, tail) ;
614
630
}
615
631
616
632
Some ( place_index)
617
633
}
618
634
635
+ #[ tracing:: instrument( level = "trace" , skip( self , tcx, body) , ret) ]
636
+ fn register_place_and_discr (
637
+ & mut self ,
638
+ tcx : TyCtxt < ' tcx > ,
639
+ body : & Body < ' tcx > ,
640
+ place : Place < ' tcx > ,
641
+ ) -> Option < PlaceIndex > {
642
+ let place = self . register_place ( tcx, body, place, None ) ?;
643
+ let ty = self . places [ place] . ty ;
644
+
645
+ if let ty:: Ref ( _, ref_ty, _) | ty:: RawPtr ( ref_ty, _) = ty. kind ( )
646
+ && let ty:: Slice ( ..) = ref_ty. kind ( )
647
+ {
648
+ self . register_place_index ( tcx. types . usize , place, TrackElem :: DerefLen ) ;
649
+ } else if ty. is_enum ( ) {
650
+ let discriminant_ty = ty. discriminant_ty ( tcx) ;
651
+ self . register_place_index ( discriminant_ty, place, TrackElem :: Discriminant ) ;
652
+ }
653
+
654
+ Some ( place)
655
+ }
656
+
657
+ #[ tracing:: instrument( level = "trace" , skip( self , tcx, typing_env) , ret) ]
658
+ pub fn register_value (
659
+ & mut self ,
660
+ tcx : TyCtxt < ' tcx > ,
661
+ typing_env : ty:: TypingEnv < ' tcx > ,
662
+ place : PlaceIndex ,
663
+ ) -> Option < ValueIndex > {
664
+ let place_info = & mut self . places [ place] ;
665
+ if let Some ( value) = place_info. value_index {
666
+ return Some ( value) ;
667
+ }
668
+
669
+ if let Ok ( ty) = tcx. try_normalize_erasing_regions ( typing_env, place_info. ty ) {
670
+ place_info. ty = ty;
671
+ }
672
+
673
+ // Allocate a value slot if it doesn't have one, and the user requested one.
674
+ if let Ok ( layout) = tcx. layout_of ( typing_env. as_query_input ( place_info. ty ) )
675
+ && layout. backend_repr . is_scalar ( )
676
+ {
677
+ place_info. value_index = Some ( self . value_count . into ( ) ) ;
678
+ self . value_count += 1 ;
679
+ }
680
+
681
+ place_info. value_index
682
+ }
683
+
684
+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
685
+ pub fn register_copy_tree (
686
+ & mut self ,
687
+ // Tree to copy.
688
+ source : PlaceIndex ,
689
+ // Tree to build.
690
+ target : PlaceIndex ,
691
+ f : & mut impl FnMut ( ValueIndex , ValueIndex ) ,
692
+ ) {
693
+ if let Some ( source_value) = self . places [ source] . value_index {
694
+ let target_value = * self . places [ target] . value_index . get_or_insert_with ( || {
695
+ let value_index = self . value_count . into ( ) ;
696
+ self . value_count += 1 ;
697
+ value_index
698
+ } ) ;
699
+ f ( source_value, target_value)
700
+ }
701
+
702
+ // Iterate over `source` children and recurse.
703
+ let mut source_child_iter = self . places [ source] . first_child ;
704
+ while let Some ( source_child) = source_child_iter {
705
+ source_child_iter = self . places [ source_child] . next_sibling ;
706
+
707
+ // Try to find corresponding child and recurse. Reasoning is similar as above.
708
+ let source_info = & self . places [ source_child] ;
709
+ let source_ty = source_info. ty ;
710
+ let source_elem = source_info. proj_elem . unwrap ( ) ;
711
+ let target_child = self . register_place_index ( source_ty, target, source_elem) ;
712
+ self . register_copy_tree ( source_child, target_child, f) ;
713
+ }
714
+ }
715
+
619
716
/// Precompute the list of values inside `root` and store it inside
620
717
/// as a slice within `inner_values_buffer`.
718
+ #[ tracing:: instrument( level = "trace" , skip( self ) ) ]
621
719
fn cache_preorder_invoke ( & mut self , root : PlaceIndex ) {
720
+ debug_assert_matches ! ( self . mode, PlaceCollectionMode :: Full { .. } ) ;
622
721
let start = self . inner_values_buffer . len ( ) ;
623
722
if let Some ( vi) = self . places [ root] . value_index {
624
723
self . inner_values_buffer . push ( vi) ;
@@ -649,7 +748,7 @@ impl<'tcx> Visitor<'tcx> for PlaceCollector<'_, 'tcx> {
649
748
return ;
650
749
}
651
750
652
- self . map . register_place ( self . tcx , self . body , * place) ;
751
+ self . map . register_place_and_discr ( self . tcx , self . body , * place) ;
653
752
}
654
753
}
655
754
@@ -724,6 +823,7 @@ impl<'tcx> Map<'tcx> {
724
823
///
725
824
/// `tail_elem` allows to support discriminants that are not a place in MIR, but that we track
726
825
/// as such.
826
+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
727
827
pub fn for_each_aliasing_place (
728
828
& self ,
729
829
place : PlaceRef < ' _ > ,
@@ -761,6 +861,7 @@ impl<'tcx> Map<'tcx> {
761
861
}
762
862
763
863
/// Invoke the given function on all the descendants of the given place, except one branch.
864
+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
764
865
fn for_each_variant_sibling (
765
866
& self ,
766
867
parent : PlaceIndex ,
@@ -781,11 +882,22 @@ impl<'tcx> Map<'tcx> {
781
882
}
782
883
783
884
/// Invoke a function on each value in the given place and all descendants.
885
+ #[ tracing:: instrument( level = "trace" , skip( self , f) ) ]
784
886
fn for_each_value_inside ( & self , root : PlaceIndex , f : & mut impl FnMut ( ValueIndex ) ) {
785
- let range = self . inner_values [ root] . clone ( ) ;
786
- let values = & self . inner_values_buffer [ range] ;
787
- for & v in values {
788
- f ( v)
887
+ if let Some ( range) = self . inner_values . get ( root) {
888
+ // Optimized path: we have cached the inner values.
889
+ let values = & self . inner_values_buffer [ range. clone ( ) ] ;
890
+ for & v in values {
891
+ f ( v)
892
+ }
893
+ } else {
894
+ if let Some ( root) = self . places [ root] . value_index {
895
+ f ( root)
896
+ }
897
+
898
+ for child in self . children ( root) {
899
+ self . for_each_value_inside ( child, f) ;
900
+ }
789
901
}
790
902
}
791
903
@@ -798,7 +910,9 @@ impl<'tcx> Map<'tcx> {
798
910
f : & mut impl FnMut ( PlaceIndex , & O ) ,
799
911
) {
800
912
// Fast path is there is nothing to do.
801
- if self . inner_values [ root] . is_empty ( ) {
913
+ if let Some ( value_range) = self . inner_values . get ( root)
914
+ && value_range. is_empty ( )
915
+ {
802
916
return ;
803
917
}
804
918
0 commit comments