|
1 | 1 | //! An analysis to determine which locals require allocas and
|
2 | 2 | //! which do not.
|
3 | 3 |
|
| 4 | +use rustc_abi as abi; |
4 | 5 | use rustc_data_structures::graph::dominators::Dominators;
|
5 | 6 | use rustc_index::bit_set::DenseBitSet;
|
6 | 7 | use rustc_index::{IndexSlice, IndexVec};
|
7 | 8 | use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
8 | 9 | use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind, traversal};
|
9 |
| -use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; |
| 10 | +use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; |
10 | 11 | use rustc_middle::{bug, span_bug};
|
11 | 12 | use tracing::debug;
|
12 | 13 |
|
@@ -99,63 +100,112 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> LocalAnalyzer<'a, 'b, 'tcx, Bx>
|
99 | 100 | context: PlaceContext,
|
100 | 101 | location: Location,
|
101 | 102 | ) {
|
102 |
| - let cx = self.fx.cx; |
| 103 | + const COPY_CONTEXT: PlaceContext = |
| 104 | + PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy); |
| 105 | + |
| 106 | + // `PlaceElem::Index` is the only variant that can mention other `Local`s, |
| 107 | + // so check for those up-front before any potential short-circuits. |
| 108 | + for elem in place_ref.projection { |
| 109 | + if let mir::PlaceElem::Index(index_local) = *elem { |
| 110 | + self.visit_local(index_local, COPY_CONTEXT, location); |
| 111 | + } |
| 112 | + } |
103 | 113 |
|
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 |
| - }; |
| 114 | + // If our local is already memory, nothing can make it *more* memory |
| 115 | + // so we don't need to bother checking the projections further. |
| 116 | + if self.locals[place_ref.local] == LocalKind::Memory { |
| 117 | + return; |
| 118 | + } |
110 | 119 |
|
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; |
| 120 | + if place_ref.is_indirect_first_projection() { |
| 121 | + // If this starts with a `Deref`, we only need to record a read of the |
| 122 | + // pointer being dereferenced, as all the subsequent projections are |
| 123 | + // working on a place which is always supported. (And because we're |
| 124 | + // looking at codegen MIR, it can only happen as the first projection.) |
| 125 | + self.visit_local(place_ref.local, COPY_CONTEXT, location); |
| 126 | + return; |
| 127 | + } |
| 128 | + |
| 129 | + if !place_ref.projection.is_empty() { |
| 130 | + if context.is_mutating_use() { |
| 131 | + // If it's a mutating use it doesn't matter what the projections are, |
| 132 | + // if there are *any* then we need a place to write. (For example, |
| 133 | + // `_1 = Foo()` works in SSA but `_2.0 = Foo()` does not.) |
| 134 | + let mut_projection = PlaceContext::MutatingUse(MutatingUseContext::Projection); |
| 135 | + self.visit_local(place_ref.local, mut_projection, location); |
| 136 | + return; |
| 137 | + } |
| 138 | + |
| 139 | + // Scan through to ensure the only projections are those which |
| 140 | + // `FunctionCx::maybe_codegen_consume_direct` can handle. |
| 141 | + let base_ty = self.fx.monomorphized_place_ty(mir::PlaceRef::from(place_ref.local)); |
| 142 | + let mut layout = self.fx.cx.layout_of(base_ty); |
| 143 | + for elem in place_ref.projection { |
| 144 | + if layout.is_zst() { |
| 145 | + // Any further projections must still be ZSTs, so we're good. |
| 146 | + break; |
127 | 147 | }
|
128 | 148 |
|
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; |
| 149 | + #[track_caller] |
| 150 | + fn compatible_projection(src: TyAndLayout<'_>, tgt: TyAndLayout<'_>) -> bool { |
| 151 | + fn compatible_initness(a: abi::Scalar, b: abi::Scalar) -> bool { |
| 152 | + !a.is_uninit_valid() || b.is_uninit_valid() |
| 153 | + } |
| 154 | + |
| 155 | + use abi::BackendRepr::*; |
| 156 | + match (src.backend_repr, tgt.backend_repr) { |
| 157 | + _ if tgt.is_zst() => true, |
| 158 | + (Scalar(a), Scalar(b)) | (SimdVector { element: a, .. }, SimdVector { element: b, .. }) => |
| 159 | + compatible_initness(a, b), |
| 160 | + (ScalarPair(a0, a1), Scalar(b)) => |
| 161 | + compatible_initness(a0, b) && |
| 162 | + compatible_initness(a1, b), |
| 163 | + (ScalarPair(a0, a1), ScalarPair(b0, b1)) => |
| 164 | + compatible_initness(a0, b0) && |
| 165 | + compatible_initness(a1, b1), |
| 166 | + // This arm is a hack; remove it as part of |
| 167 | + // <https://github.com/rust-lang/compiler-team/issues/838> |
| 168 | + (SimdVector { .. }, Memory { .. }) => true, |
| 169 | + _ => bug!("Mismatched layouts in analysis\nsrc: {src:?}\ntgt: {tgt:?}"), |
136 | 170 | }
|
137 | 171 | }
|
138 |
| - } |
139 | 172 |
|
140 |
| - if let mir::ProjectionElem::Deref = elem { |
141 |
| - // Deref projections typically only read the pointer. |
142 |
| - base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy); |
143 |
| - } |
| 173 | + match *elem { |
| 174 | + mir::PlaceElem::Field(fidx, ..) => { |
| 175 | + let field_layout = layout.field(self.fx.cx, fidx.as_usize()); |
| 176 | + if compatible_projection(layout, field_layout) |
| 177 | + { |
| 178 | + layout = field_layout; |
| 179 | + continue; |
| 180 | + } |
| 181 | + } |
| 182 | + mir::PlaceElem::Downcast(_, vidx) => { |
| 183 | + let variant_layout = layout.for_variant(self.fx.cx, vidx); |
| 184 | + if compatible_projection(layout, variant_layout) { |
| 185 | + layout = variant_layout; |
| 186 | + continue; |
| 187 | + } |
| 188 | + } |
144 | 189 |
|
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 |
| - ); |
| 190 | + mir::PlaceElem::Index(..) |
| 191 | + | mir::PlaceElem::ConstantIndex { .. } |
| 192 | + | mir::PlaceElem::Subslice { .. } |
| 193 | + | mir::PlaceElem::OpaqueCast(..) |
| 194 | + | mir::PlaceElem::UnwrapUnsafeBinder(..) |
| 195 | + | mir::PlaceElem::Subtype(..) => {} |
| 196 | + |
| 197 | + mir::PlaceElem::Deref => bug!("Deref after first position"), |
| 198 | + } |
| 199 | + |
| 200 | + // If the above didn't `continue`, we can't handle the projection. |
| 201 | + self.locals[place_ref.local] = LocalKind::Memory; |
| 202 | + return; |
155 | 203 | }
|
156 |
| - } else { |
157 |
| - self.visit_local(place_ref.local, context, location); |
158 | 204 | }
|
| 205 | + |
| 206 | + // Even with supported projections, we still need to have `visit_local` |
| 207 | + // check for things that can't be done in SSA (like `SharedBorrow`). |
| 208 | + self.visit_local(place_ref.local, context, location); |
159 | 209 | }
|
160 | 210 | }
|
161 | 211 |
|
|
0 commit comments