@@ -32,11 +32,11 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates {
3232 let typing_env = body. typing_env ( tcx) ;
3333 loop {
3434 debug ! ( ?excluded) ;
35- let escaping = escaping_locals ( tcx, & excluded, body) ;
35+ let escaping = escaping_locals ( tcx, & excluded, typing_env , body) ;
3636 debug ! ( ?escaping) ;
3737 let replacements = compute_flattening ( tcx, typing_env, body, escaping) ;
3838 debug ! ( ?replacements) ;
39- let all_dead_locals = replace_flattened_locals ( tcx, body, replacements) ;
39+ let all_dead_locals = replace_flattened_locals ( tcx, typing_env , body, replacements) ;
4040 if !all_dead_locals. is_empty ( ) {
4141 excluded. union ( & all_dead_locals) ;
4242 excluded = {
@@ -65,6 +65,7 @@ impl<'tcx> crate::MirPass<'tcx> for ScalarReplacementOfAggregates {
6565fn escaping_locals < ' tcx > (
6666 tcx : TyCtxt < ' tcx > ,
6767 excluded : & DenseBitSet < Local > ,
68+ typing_env : ty:: TypingEnv < ' tcx > ,
6869 body : & Body < ' tcx > ,
6970) -> DenseBitSet < Local > {
7071 let is_excluded_ty = |ty : Ty < ' tcx > | {
@@ -88,20 +89,24 @@ fn escaping_locals<'tcx>(
8889
8990 let mut set = DenseBitSet :: new_empty ( body. local_decls . len ( ) ) ;
9091 set. insert_range ( RETURN_PLACE ..Local :: arg ( body. arg_count ) ) ;
91- for ( local, decl) in body. local_decls ( ) . iter_enumerated ( ) {
92+ for ( local, decl) in body. local_decls . iter_enumerated ( ) {
9293 if excluded. contains ( local) || is_excluded_ty ( decl. ty ) {
9394 set. insert ( local) ;
9495 }
9596 }
96- let mut visitor = EscapeVisitor { set } ;
97+ let mut visitor = EscapeVisitor { tcx , typing_env , set, decls : & body . local_decls } ;
9798 visitor. visit_body ( body) ;
9899 return visitor. set ;
99100
100- struct EscapeVisitor {
101+ struct EscapeVisitor < ' tcx , ' a > {
102+ tcx : TyCtxt < ' tcx > ,
103+ typing_env : ty:: TypingEnv < ' tcx > ,
101104 set : DenseBitSet < Local > ,
105+ /// This is used to look at the field types of a transmuted local.
106+ decls : & ' a LocalDecls < ' tcx > ,
102107 }
103108
104- impl < ' tcx > Visitor < ' tcx > for EscapeVisitor {
109+ impl < ' tcx > Visitor < ' tcx > for EscapeVisitor < ' tcx , ' _ > {
105110 fn visit_local ( & mut self , local : Local , _: PlaceContext , _: Location ) {
106111 self . set . insert ( local) ;
107112 }
@@ -114,6 +119,28 @@ fn escaping_locals<'tcx>(
114119 self . super_place ( place, context, location) ;
115120 }
116121
122+ fn visit_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
123+ // A transmute to a field type is either the same as a read of that
124+ // field or it's UB for a size mismatch, so we can allow SRoA the same
125+ // as if it had been written `Use(op)` with a field projection.
126+ if let Rvalue :: Cast ( CastKind :: Transmute , op, to_ty) = rvalue
127+ && let Some ( place) = op. place ( )
128+ && let Some ( local) = place. as_local ( )
129+ && !self . set . contains ( local)
130+ && find_matching_struct_field (
131+ self . tcx ,
132+ self . typing_env ,
133+ * to_ty,
134+ self . decls [ local] . ty ,
135+ )
136+ . is_some ( )
137+ {
138+ return ;
139+ }
140+
141+ self . super_rvalue ( rvalue, location)
142+ }
143+
117144 fn visit_assign (
118145 & mut self ,
119146 lvalue : & Place < ' tcx > ,
@@ -147,6 +174,27 @@ fn escaping_locals<'tcx>(
147174 }
148175}
149176
177+ fn find_matching_struct_field < ' tcx > (
178+ tcx : TyCtxt < ' tcx > ,
179+ typing_env : ty:: TypingEnv < ' tcx > ,
180+ needle_ty : Ty < ' tcx > ,
181+ haystack_ty : Ty < ' tcx > ,
182+ ) -> Option < FieldIdx > {
183+ if let ty:: Adt ( adt_def, adt_args) = haystack_ty. kind ( )
184+ && adt_def. is_struct ( )
185+ {
186+ for ( idx, data) in adt_def. non_enum_variant ( ) . fields . iter_enumerated ( ) {
187+ let field_ty = data. ty ( tcx, adt_args) ;
188+ let field_ty = tcx. normalize_erasing_regions ( typing_env, field_ty) ;
189+ if field_ty == needle_ty {
190+ return Some ( idx) ;
191+ }
192+ }
193+ }
194+
195+ None
196+ }
197+
150198#[ derive( Default , Debug ) ]
151199struct ReplacementMap < ' tcx > {
152200 /// Pre-computed list of all "new" locals for each "old" local. This is used to expand storage
@@ -211,6 +259,7 @@ fn compute_flattening<'tcx>(
211259/// Perform the replacement computed by `compute_flattening`.
212260fn replace_flattened_locals < ' tcx > (
213261 tcx : TyCtxt < ' tcx > ,
262+ typing_env : ty:: TypingEnv < ' tcx > ,
214263 body : & mut Body < ' tcx > ,
215264 replacements : ReplacementMap < ' tcx > ,
216265) -> DenseBitSet < Local > {
@@ -227,6 +276,7 @@ fn replace_flattened_locals<'tcx>(
227276
228277 let mut visitor = ReplacementVisitor {
229278 tcx,
279+ typing_env,
230280 local_decls : & body. local_decls ,
231281 replacements : & replacements,
232282 all_dead_locals,
@@ -249,7 +299,9 @@ fn replace_flattened_locals<'tcx>(
249299
250300struct ReplacementVisitor < ' tcx , ' ll > {
251301 tcx : TyCtxt < ' tcx > ,
252- /// This is only used to compute the type for `VarDebugInfoFragment`.
302+ typing_env : ty:: TypingEnv < ' tcx > ,
303+ /// This is used to compute the type for `VarDebugInfoFragment`
304+ /// and to look at the field types of a transmuted local.
253305 local_decls : & ' ll LocalDecls < ' tcx > ,
254306 /// Work to do.
255307 replacements : & ' ll ReplacementMap < ' tcx > ,
@@ -430,6 +482,37 @@ impl<'tcx, 'll> MutVisitor<'tcx> for ReplacementVisitor<'tcx, 'll> {
430482 self . super_statement ( statement, location)
431483 }
432484
485+ fn visit_rvalue ( & mut self , rvalue : & mut Rvalue < ' tcx > , location : Location ) {
486+ // We have `other = transmute(move? a)`
487+ // We replace it with
488+ // ```
489+ // other = move? a_i
490+ // ```
491+ // for the one relevant field.
492+ if let Rvalue :: Cast ( CastKind :: Transmute , ref op, to_ty) = * rvalue
493+ && let Some ( op_place) = op. place ( )
494+ && let Some ( op_local) = op_place. as_local ( )
495+ && let is_move = matches ! ( op, Operand :: Move ( ..) )
496+ && let Some ( op_final_locals) = & self . replacements . fragments [ op_local]
497+ {
498+ let field_idx = find_matching_struct_field (
499+ self . tcx ,
500+ self . typing_env ,
501+ to_ty,
502+ self . local_decls [ op_local] . ty ,
503+ )
504+ . unwrap ( ) ;
505+ let ( new_local_ty, new_local) = op_final_locals[ field_idx] . unwrap ( ) ;
506+ assert_eq ! ( new_local_ty, to_ty) ;
507+ let new_place = Place :: from ( new_local) ;
508+ let new_op = if is_move { Operand :: Move ( new_place) } else { Operand :: Copy ( new_place) } ;
509+ * rvalue = Rvalue :: Use ( new_op, WithRetag :: Yes ) ;
510+ return ;
511+ }
512+
513+ self . super_rvalue ( rvalue, location) ;
514+ }
515+
433516 fn visit_local ( & mut self , local : & mut Local , _: PlaceContext , _: Location ) {
434517 assert ! ( !self . all_dead_locals. contains( * local) ) ;
435518 }
0 commit comments