1
- use std:: fmt:: Debug ;
2
- use std:: iter;
3
-
4
1
use hir:: def_id:: DefId ;
5
2
use rustc_abi:: Integer :: { I8 , I32 } ;
6
3
use rustc_abi:: Primitive :: { self , Float , Int , Pointer } ;
7
4
use rustc_abi:: {
8
- AddressSpace , BackendRepr , FIRST_VARIANT , FieldIdx , FieldsShape , HasDataLayout , Integer ,
9
- Layout , LayoutCalculator , LayoutCalculatorError , LayoutData , Niche , ReprOptions , Scalar , Size ,
10
- StructKind , TagEncoding , VariantIdx , Variants , WrappingRange ,
5
+ AddressSpace , BackendRepr , FIRST_VARIANT , FieldIdx , FieldsShape , HasDataLayout , Layout ,
6
+ LayoutCalculatorError , LayoutData , Niche , ReprOptions , Scalar , Size , StructKind , TagEncoding ,
7
+ VariantIdx , Variants , WrappingRange ,
11
8
} ;
12
9
use rustc_hashes:: Hash64 ;
13
- use rustc_index:: bit_set:: { BitMatrix , DenseBitSet } ;
14
- use rustc_index:: { Idx , IndexSlice , IndexVec } ;
10
+ use rustc_index:: IndexVec ;
15
11
use rustc_middle:: bug;
16
12
use rustc_middle:: query:: Providers ;
17
13
use rustc_middle:: ty:: layout:: {
@@ -23,7 +19,7 @@ use rustc_middle::ty::{
23
19
} ;
24
20
use rustc_session:: { DataTypeKind , FieldInfo , FieldKind , SizeKind , VariantInfo } ;
25
21
use rustc_span:: { Symbol , sym} ;
26
- use tracing:: { debug, instrument, trace } ;
22
+ use tracing:: { debug, instrument} ;
27
23
use { rustc_abi as abi, rustc_hir as hir} ;
28
24
29
25
use crate :: errors:: { NonPrimitiveSimdType , OversizedSimdType , ZeroLengthSimdType } ;
@@ -403,23 +399,24 @@ fn layout_of_uncached<'tcx>(
403
399
. map ( |ty| cx. layout_of ( ty) )
404
400
. try_collect :: < IndexVec < _ , _ > > ( ) ?;
405
401
406
- let layout = coroutine_layout (
407
- & cx. calc ,
408
- & local_layouts,
409
- prefix_layouts,
410
- & info. variant_fields ,
411
- & info. storage_conflicts ,
412
- |tag| TyAndLayout {
413
- ty : tag. primitive ( ) . to_ty ( tcx) ,
414
- layout : tcx. mk_layout ( LayoutData :: scalar ( cx, tag) ) ,
415
- } ,
416
- )
417
- . map ( |mut layout| {
418
- // this is similar to how ReprOptions populates its field_shuffle_seed
419
- layout. randomization_seed = tcx. def_path_hash ( def_id) . 0 . to_smaller_hash ( ) ;
420
- debug ! ( "coroutine layout ({:?}): {:#?}" , ty, layout) ;
421
- layout
422
- } ) ;
402
+ let layout = cx
403
+ . calc
404
+ . coroutine (
405
+ & local_layouts,
406
+ prefix_layouts,
407
+ & info. variant_fields ,
408
+ & info. storage_conflicts ,
409
+ |tag| TyAndLayout {
410
+ ty : tag. primitive ( ) . to_ty ( tcx) ,
411
+ layout : tcx. mk_layout ( LayoutData :: scalar ( cx, tag) ) ,
412
+ } ,
413
+ )
414
+ . map ( |mut layout| {
415
+ // this is similar to how ReprOptions populates its field_shuffle_seed
416
+ layout. randomization_seed = tcx. def_path_hash ( def_id) . 0 . to_smaller_hash ( ) ;
417
+ debug ! ( "coroutine layout ({:?}): {:#?}" , ty, layout) ;
418
+ layout
419
+ } ) ;
423
420
map_layout ( layout) ?
424
421
}
425
422
@@ -614,314 +611,6 @@ fn layout_of_uncached<'tcx>(
614
611
} )
615
612
}
616
613
617
- /// Overlap eligibility and variant assignment for each CoroutineSavedLocal.
618
- #[ derive( Clone , Debug , PartialEq ) ]
619
- enum SavedLocalEligibility < VariantIdx , FieldIdx > {
620
- Unassigned ,
621
- Assigned ( VariantIdx ) ,
622
- Ineligible ( Option < FieldIdx > ) ,
623
- }
624
-
625
- // When laying out coroutines, we divide our saved local fields into two
626
- // categories: overlap-eligible and overlap-ineligible.
627
- //
628
- // Those fields which are ineligible for overlap go in a "prefix" at the
629
- // beginning of the layout, and always have space reserved for them.
630
- //
631
- // Overlap-eligible fields are only assigned to one variant, so we lay
632
- // those fields out for each variant and put them right after the
633
- // prefix.
634
- //
635
- // Finally, in the layout details, we point to the fields from the
636
- // variants they are assigned to. It is possible for some fields to be
637
- // included in multiple variants. No field ever "moves around" in the
638
- // layout; its offset is always the same.
639
- //
640
- // Also included in the layout are the upvars and the discriminant.
641
- // These are included as fields on the "outer" layout; they are not part
642
- // of any variant.
643
-
644
- /// Compute the eligibility and assignment of each local.
645
- fn coroutine_saved_local_eligibility < VariantIdx : Idx , FieldIdx : Idx , LocalIdx : Idx > (
646
- nb_locals : usize ,
647
- variant_fields : & IndexSlice < VariantIdx , IndexVec < FieldIdx , LocalIdx > > ,
648
- storage_conflicts : & BitMatrix < LocalIdx , LocalIdx > ,
649
- ) -> ( DenseBitSet < LocalIdx > , IndexVec < LocalIdx , SavedLocalEligibility < VariantIdx , FieldIdx > > ) {
650
- use SavedLocalEligibility :: * ;
651
-
652
- let mut assignments: IndexVec < LocalIdx , _ > = IndexVec :: from_elem_n ( Unassigned , nb_locals) ;
653
-
654
- // The saved locals not eligible for overlap. These will get
655
- // "promoted" to the prefix of our coroutine.
656
- let mut ineligible_locals = DenseBitSet :: new_empty ( nb_locals) ;
657
-
658
- // Figure out which of our saved locals are fields in only
659
- // one variant. The rest are deemed ineligible for overlap.
660
- for ( variant_index, fields) in variant_fields. iter_enumerated ( ) {
661
- for local in fields {
662
- match assignments[ * local] {
663
- Unassigned => {
664
- assignments[ * local] = Assigned ( variant_index) ;
665
- }
666
- Assigned ( idx) => {
667
- // We've already seen this local at another suspension
668
- // point, so it is no longer a candidate.
669
- trace ! (
670
- "removing local {:?} in >1 variant ({:?}, {:?})" ,
671
- local, variant_index, idx
672
- ) ;
673
- ineligible_locals. insert ( * local) ;
674
- assignments[ * local] = Ineligible ( None ) ;
675
- }
676
- Ineligible ( _) => { }
677
- }
678
- }
679
- }
680
-
681
- // Next, check every pair of eligible locals to see if they
682
- // conflict.
683
- for local_a in storage_conflicts. rows ( ) {
684
- let conflicts_a = storage_conflicts. count ( local_a) ;
685
- if ineligible_locals. contains ( local_a) {
686
- continue ;
687
- }
688
-
689
- for local_b in storage_conflicts. iter ( local_a) {
690
- // local_a and local_b are storage live at the same time, therefore they
691
- // cannot overlap in the coroutine layout. The only way to guarantee
692
- // this is if they are in the same variant, or one is ineligible
693
- // (which means it is stored in every variant).
694
- if ineligible_locals. contains ( local_b) || assignments[ local_a] == assignments[ local_b] {
695
- continue ;
696
- }
697
-
698
- // If they conflict, we will choose one to make ineligible.
699
- // This is not always optimal; it's just a greedy heuristic that
700
- // seems to produce good results most of the time.
701
- let conflicts_b = storage_conflicts. count ( local_b) ;
702
- let ( remove, other) =
703
- if conflicts_a > conflicts_b { ( local_a, local_b) } else { ( local_b, local_a) } ;
704
- ineligible_locals. insert ( remove) ;
705
- assignments[ remove] = Ineligible ( None ) ;
706
- trace ! ( "removing local {:?} due to conflict with {:?}" , remove, other) ;
707
- }
708
- }
709
-
710
- // Count the number of variants in use. If only one of them, then it is
711
- // impossible to overlap any locals in our layout. In this case it's
712
- // always better to make the remaining locals ineligible, so we can
713
- // lay them out with the other locals in the prefix and eliminate
714
- // unnecessary padding bytes.
715
- {
716
- let mut used_variants = DenseBitSet :: new_empty ( variant_fields. len ( ) ) ;
717
- for assignment in & assignments {
718
- if let Assigned ( idx) = assignment {
719
- used_variants. insert ( * idx) ;
720
- }
721
- }
722
- if used_variants. count ( ) < 2 {
723
- for assignment in assignments. iter_mut ( ) {
724
- * assignment = Ineligible ( None ) ;
725
- }
726
- ineligible_locals. insert_all ( ) ;
727
- }
728
- }
729
-
730
- // Write down the order of our locals that will be promoted to the prefix.
731
- {
732
- for ( idx, local) in ineligible_locals. iter ( ) . enumerate ( ) {
733
- assignments[ local] = Ineligible ( Some ( FieldIdx :: new ( idx) ) ) ;
734
- }
735
- }
736
- debug ! ( "coroutine saved local assignments: {:?}" , assignments) ;
737
-
738
- ( ineligible_locals, assignments)
739
- }
740
-
741
- /// Compute the full coroutine layout.
742
- fn coroutine_layout <
743
- ' a ,
744
- F : core:: ops:: Deref < Target = & ' a LayoutData < FieldIdx , VariantIdx > > + core:: fmt:: Debug + Copy ,
745
- VariantIdx : Idx ,
746
- FieldIdx : Idx ,
747
- LocalIdx : Idx ,
748
- > (
749
- calc : & LayoutCalculator < impl HasDataLayout > ,
750
- local_layouts : & IndexSlice < LocalIdx , F > ,
751
- mut prefix_layouts : IndexVec < FieldIdx , F > ,
752
- variant_fields : & IndexSlice < VariantIdx , IndexVec < FieldIdx , LocalIdx > > ,
753
- storage_conflicts : & BitMatrix < LocalIdx , LocalIdx > ,
754
- tag_to_layout : impl Fn ( Scalar ) -> F ,
755
- ) -> Result < LayoutData < FieldIdx , VariantIdx > , LayoutCalculatorError < F > > {
756
- use SavedLocalEligibility :: * ;
757
-
758
- let ( ineligible_locals, assignments) =
759
- coroutine_saved_local_eligibility ( local_layouts. len ( ) , variant_fields, storage_conflicts) ;
760
-
761
- // Build a prefix layout, including "promoting" all ineligible
762
- // locals as part of the prefix. We compute the layout of all of
763
- // these fields at once to get optimal packing.
764
- let tag_index = prefix_layouts. len ( ) ;
765
-
766
- // `variant_fields` already accounts for the reserved variants, so no need to add them.
767
- let max_discr = ( variant_fields. len ( ) - 1 ) as u128 ;
768
- let discr_int = Integer :: fit_unsigned ( max_discr) ;
769
- let tag = Scalar :: Initialized {
770
- value : Primitive :: Int ( discr_int, /* signed = */ false ) ,
771
- valid_range : WrappingRange { start : 0 , end : max_discr } ,
772
- } ;
773
-
774
- let promoted_layouts = ineligible_locals. iter ( ) . map ( |local| local_layouts[ local] ) ;
775
- prefix_layouts. push ( tag_to_layout ( tag) ) ;
776
- prefix_layouts. extend ( promoted_layouts) ;
777
- let prefix =
778
- calc. univariant ( & prefix_layouts, & ReprOptions :: default ( ) , StructKind :: AlwaysSized ) ?;
779
-
780
- let ( prefix_size, prefix_align) = ( prefix. size , prefix. align ) ;
781
-
782
- // Split the prefix layout into the "outer" fields (upvars and
783
- // discriminant) and the "promoted" fields. Promoted fields will
784
- // get included in each variant that requested them in
785
- // CoroutineLayout.
786
- debug ! ( "prefix = {:#?}" , prefix) ;
787
- let ( outer_fields, promoted_offsets, promoted_memory_index) = match prefix. fields {
788
- FieldsShape :: Arbitrary { mut offsets, memory_index } => {
789
- let mut inverse_memory_index = memory_index. invert_bijective_mapping ( ) ;
790
-
791
- // "a" (`0..b_start`) and "b" (`b_start..`) correspond to
792
- // "outer" and "promoted" fields respectively.
793
- let b_start = FieldIdx :: new ( tag_index + 1 ) ;
794
- let offsets_b = IndexVec :: from_raw ( offsets. raw . split_off ( b_start. index ( ) ) ) ;
795
- let offsets_a = offsets;
796
-
797
- // Disentangle the "a" and "b" components of `inverse_memory_index`
798
- // by preserving the order but keeping only one disjoint "half" each.
799
- // FIXME(eddyb) build a better abstraction for permutations, if possible.
800
- let inverse_memory_index_b: IndexVec < u32 , FieldIdx > = inverse_memory_index
801
- . iter ( )
802
- . filter_map ( |& i| i. index ( ) . checked_sub ( b_start. index ( ) ) . map ( FieldIdx :: new) )
803
- . collect ( ) ;
804
- inverse_memory_index. raw . retain ( |& i| i. index ( ) < b_start. index ( ) ) ;
805
- let inverse_memory_index_a = inverse_memory_index;
806
-
807
- // Since `inverse_memory_index_{a,b}` each only refer to their
808
- // respective fields, they can be safely inverted
809
- let memory_index_a = inverse_memory_index_a. invert_bijective_mapping ( ) ;
810
- let memory_index_b = inverse_memory_index_b. invert_bijective_mapping ( ) ;
811
-
812
- let outer_fields =
813
- FieldsShape :: Arbitrary { offsets : offsets_a, memory_index : memory_index_a } ;
814
- ( outer_fields, offsets_b, memory_index_b)
815
- }
816
- _ => unreachable ! ( ) ,
817
- } ;
818
-
819
- let mut size = prefix. size ;
820
- let mut align = prefix. align ;
821
- let variants = variant_fields
822
- . iter_enumerated ( )
823
- . map ( |( index, variant_fields) | {
824
- // Only include overlap-eligible fields when we compute our variant layout.
825
- let variant_only_tys = variant_fields
826
- . iter ( )
827
- . filter ( |local| match assignments[ * * local] {
828
- Unassigned => unreachable ! ( ) ,
829
- Assigned ( v) if v == index => true ,
830
- Assigned ( _) => unreachable ! ( "assignment does not match variant" ) ,
831
- Ineligible ( _) => false ,
832
- } )
833
- . map ( |local| local_layouts[ * local] ) ;
834
-
835
- let mut variant = calc. univariant (
836
- & variant_only_tys. collect :: < IndexVec < _ , _ > > ( ) ,
837
- & ReprOptions :: default ( ) ,
838
- StructKind :: Prefixed ( prefix_size, prefix_align. abi ) ,
839
- ) ?;
840
- variant. variants = Variants :: Single { index } ;
841
-
842
- let FieldsShape :: Arbitrary { offsets, memory_index } = variant. fields else {
843
- unreachable ! ( ) ;
844
- } ;
845
-
846
- // Now, stitch the promoted and variant-only fields back together in
847
- // the order they are mentioned by our CoroutineLayout.
848
- // Because we only use some subset (that can differ between variants)
849
- // of the promoted fields, we can't just pick those elements of the
850
- // `promoted_memory_index` (as we'd end up with gaps).
851
- // So instead, we build an "inverse memory_index", as if all of the
852
- // promoted fields were being used, but leave the elements not in the
853
- // subset as `invalid_field_idx`, which we can filter out later to
854
- // obtain a valid (bijective) mapping.
855
- let invalid_field_idx = promoted_memory_index. len ( ) + memory_index. len ( ) ;
856
- let mut combined_inverse_memory_index =
857
- IndexVec :: from_elem_n ( FieldIdx :: new ( invalid_field_idx) , invalid_field_idx) ;
858
-
859
- let mut offsets_and_memory_index = iter:: zip ( offsets, memory_index) ;
860
- let combined_offsets = variant_fields
861
- . iter_enumerated ( )
862
- . map ( |( i, local) | {
863
- let ( offset, memory_index) = match assignments[ * local] {
864
- Unassigned => unreachable ! ( ) ,
865
- Assigned ( _) => {
866
- let ( offset, memory_index) = offsets_and_memory_index. next ( ) . unwrap ( ) ;
867
- ( offset, promoted_memory_index. len ( ) as u32 + memory_index)
868
- }
869
- Ineligible ( field_idx) => {
870
- let field_idx = field_idx. unwrap ( ) ;
871
- ( promoted_offsets[ field_idx] , promoted_memory_index[ field_idx] )
872
- }
873
- } ;
874
- combined_inverse_memory_index[ memory_index] = i;
875
- offset
876
- } )
877
- . collect ( ) ;
878
-
879
- // Remove the unused slots and invert the mapping to obtain the
880
- // combined `memory_index` (also see previous comment).
881
- combined_inverse_memory_index. raw . retain ( |& i| i. index ( ) != invalid_field_idx) ;
882
- let combined_memory_index = combined_inverse_memory_index. invert_bijective_mapping ( ) ;
883
-
884
- variant. fields = FieldsShape :: Arbitrary {
885
- offsets : combined_offsets,
886
- memory_index : combined_memory_index,
887
- } ;
888
-
889
- size = size. max ( variant. size ) ;
890
- align = align. max ( variant. align ) ;
891
- Ok ( variant)
892
- } )
893
- . collect :: < Result < IndexVec < VariantIdx , _ > , _ > > ( ) ?;
894
-
895
- size = size. align_to ( align. abi ) ;
896
-
897
- let uninhabited = prefix. uninhabited || variants. iter ( ) . all ( |v| v. is_uninhabited ( ) ) ;
898
- let abi = BackendRepr :: Memory { sized : true } ;
899
-
900
- Ok ( LayoutData {
901
- variants : Variants :: Multiple {
902
- tag,
903
- tag_encoding : TagEncoding :: Direct ,
904
- tag_field : tag_index,
905
- variants,
906
- } ,
907
- fields : outer_fields,
908
- backend_repr : abi,
909
- // Suppress niches inside coroutines. If the niche is inside a field that is aliased (due to
910
- // self-referentiality), getting the discriminant can cause aliasing violations.
911
- // `UnsafeCell` blocks niches for the same reason, but we don't yet have `UnsafePinned` that
912
- // would do the same for us here.
913
- // See <https://github.com/rust-lang/rust/issues/63818>, <https://github.com/rust-lang/miri/issues/3780>.
914
- // FIXME: Remove when <https://github.com/rust-lang/rust/issues/125735> is implemented and aliased coroutine fields are wrapped in `UnsafePinned`.
915
- largest_niche : None ,
916
- uninhabited,
917
- size,
918
- align,
919
- max_repr_align : None ,
920
- unadjusted_abi_align : align. abi ,
921
- randomization_seed : Default :: default ( ) ,
922
- } )
923
- }
924
-
925
614
fn record_layout_for_printing < ' tcx > ( cx : & LayoutCx < ' tcx > , layout : TyAndLayout < ' tcx > ) {
926
615
// Ignore layouts that are done with non-empty environments or
927
616
// non-monomorphic layouts, as the user only wants to see the stuff
0 commit comments