@@ -6,7 +6,7 @@ use rustc_index::bit_set::DenseBitSet;
66use rustc_index:: { IndexSlice , IndexVec } ;
77use rustc_middle:: mir:: visit:: { MutatingUseContext , NonMutatingUseContext , PlaceContext , Visitor } ;
88use rustc_middle:: mir:: { self , DefLocation , Location , TerminatorKind , traversal} ;
9- use rustc_middle:: ty:: layout:: { HasTyCtxt , LayoutOf } ;
9+ use rustc_middle:: ty:: layout:: LayoutOf ;
1010use rustc_middle:: { bug, span_bug} ;
1111use tracing:: debug;
1212
@@ -99,63 +99,64 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> LocalAnalyzer<'a, 'b, 'tcx, Bx>
9999 context : PlaceContext ,
100100 location : Location ,
101101 ) {
102- let cx = self . fx . cx ;
102+ const COPY_CONTEXT : PlaceContext =
103+ PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Copy ) ;
104+
105+ // `PlaceElem::Index` is the only variant that can mention other `Local`s,
106+ // so check for those up-front before any potential short-circuits.
107+ for elem in place_ref. projection {
108+ if let mir:: PlaceElem :: Index ( index_local) = * elem {
109+ self . visit_local ( index_local, COPY_CONTEXT , location) ;
110+ }
111+ }
103112
104- if let Some ( ( place_base, elem) ) = place_ref. last_projection ( ) {
105- let mut base_context = if context. is_mutating_use ( ) {
106- PlaceContext :: MutatingUse ( MutatingUseContext :: Projection )
107- } else {
108- PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Projection )
109- } ;
113+ // If our local is already memory, nothing can make it *more* memory
114+ // so we don't need to bother checking the projections further.
115+ if self . locals [ place_ref. local ] == LocalKind :: Memory {
116+ return ;
117+ }
110118
111- // Allow uses of projections that are ZSTs or from scalar fields.
112- let is_consume = matches ! (
113- context,
114- PlaceContext :: NonMutatingUse (
115- NonMutatingUseContext :: Copy | NonMutatingUseContext :: Move ,
116- )
117- ) ;
118- if is_consume {
119- let base_ty = place_base. ty ( self . fx . mir , cx. tcx ( ) ) ;
120- let base_ty = self . fx . monomorphize ( base_ty) ;
121-
122- // ZSTs don't require any actual memory access.
123- let elem_ty = base_ty. projection_ty ( cx. tcx ( ) , self . fx . monomorphize ( elem) ) . ty ;
124- let span = self . fx . mir . local_decls [ place_ref. local ] . source_info . span ;
125- if cx. spanned_layout_of ( elem_ty, span) . is_zst ( ) {
126- return ;
127- }
119+ if place_ref. is_indirect_first_projection ( ) {
120+ // If this starts with a `Deref`, we only need to record a read of the
121+ // pointer being dereferenced, as all the subsequent projections are
122+ // working on a place which is always supported. (And because we're
123+ // looking at codegen MIR, it can only happen as the first projection.)
124+ self . visit_local ( place_ref. local , COPY_CONTEXT , location) ;
125+ return ;
126+ }
128127
129- if let mir:: ProjectionElem :: Field ( ..) = elem {
130- let layout = cx. spanned_layout_of ( base_ty. ty , span) ;
131- if cx. is_backend_immediate ( layout) || cx. is_backend_scalar_pair ( layout) {
132- // Recurse with the same context, instead of `Projection`,
133- // potentially stopping at non-operand projections,
134- // which would trigger `not_ssa` on locals.
135- base_context = context;
136- }
137- }
138- }
128+ if !place_ref. projection . is_empty ( ) && context. is_mutating_use ( ) {
129+ // If it's a mutating use it doesn't matter what the projections are,
130+ // if there are *any* then we need a place to write. (For example,
131+ // `_1 = Foo()` works in SSA but `_2.0 = Foo()` does not.)
132+ let mut_projection = PlaceContext :: MutatingUse ( MutatingUseContext :: Projection ) ;
133+ self . visit_local ( place_ref. local , mut_projection, location) ;
134+ return ;
135+ }
139136
140- if let mir:: ProjectionElem :: Deref = elem {
141- // Deref projections typically only read the pointer.
142- base_context = PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Copy ) ;
143- }
137+ for elem in place_ref. projection {
138+ // Scan through to ensure the only projections are those which
139+ // `FunctionCx::maybe_codegen_consume_direct` can handle.
140+ match elem {
141+ mir:: PlaceElem :: Field ( ..) | mir:: PlaceElem :: Downcast ( ..) => { }
142+
143+ mir:: PlaceElem :: Index ( ..)
144+ | mir:: PlaceElem :: ConstantIndex { .. }
145+ | mir:: PlaceElem :: Subslice { .. }
146+ | mir:: PlaceElem :: OpaqueCast ( ..)
147+ | mir:: PlaceElem :: UnwrapUnsafeBinder ( ..)
148+ | mir:: PlaceElem :: Subtype ( ..) => {
149+ self . locals [ place_ref. local ] = LocalKind :: Memory ;
150+ return ;
151+ }
144152
145- self . process_place ( & place_base, base_context, location) ;
146- // HACK(eddyb) this emulates the old `visit_projection_elem`, this
147- // entire `visit_place`-like `process_place` method should be rewritten,
148- // now that we have moved to the "slice of projections" representation.
149- if let mir:: ProjectionElem :: Index ( local) = elem {
150- self . visit_local (
151- local,
152- PlaceContext :: NonMutatingUse ( NonMutatingUseContext :: Copy ) ,
153- location,
154- ) ;
153+ mir:: PlaceElem :: Deref => bug ! ( "Deref after first position" ) ,
155154 }
156- } else {
157- self . visit_local ( place_ref. local , context, location) ;
158155 }
156+
157+ // Even with supported projections, we still need to have `visit_local`
158+ // check for things that can't be done in SSA (like `SharedBorrow`).
159+ self . visit_local ( place_ref. local , context, location) ;
159160 }
160161}
161162
@@ -171,8 +172,7 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer
171172 if let Some ( local) = place. as_local ( ) {
172173 self . define ( local, DefLocation :: Assignment ( location) ) ;
173174 if self . locals [ local] != LocalKind :: Memory {
174- let decl_span = self . fx . mir . local_decls [ local] . source_info . span ;
175- if !self . fx . rvalue_creates_operand ( rvalue, decl_span) {
175+ if !self . fx . rvalue_creates_operand ( rvalue) {
176176 self . locals [ local] = LocalKind :: Memory ;
177177 }
178178 }
0 commit comments