Skip to content

Commit 81bafbf

Browse files
committed
Auto merge of #138582 - scottmcm:option-ssa-2, r=<try>
Don't require `alloca`s for consuming simple enums Well, 4 months later I'm finally back to this. Will stay as Draft until #143502 lands, since this builds on that. For example, if you pass an `Option<u32>` to a function, don't immediately write it to an `alloca` then read it again.
2 parents 75d5834 + ae2daed commit 81bafbf

File tree

16 files changed

+542
-222
lines changed

16 files changed

+542
-222
lines changed

compiler/rustc_codegen_ssa/src/mir/analyze.rs

Lines changed: 53 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use rustc_index::bit_set::DenseBitSet;
66
use rustc_index::{IndexSlice, IndexVec};
77
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
88
use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind, traversal};
9-
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
9+
use rustc_middle::ty::layout::LayoutOf;
1010
use rustc_middle::{bug, span_bug};
1111
use 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
}

compiler/rustc_codegen_ssa/src/mir/locals.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use tracing::{debug, warn};
1212
use crate::mir::{FunctionCx, LocalRef};
1313
use crate::traits::BuilderMethods;
1414

15+
#[derive(Debug)]
1516
pub(super) struct Locals<'tcx, V> {
1617
values: IndexVec<mir::Local, LocalRef<'tcx, V>>,
1718
}

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
136136
}
137137
}
138138

139+
#[derive(Debug)]
139140
enum LocalRef<'tcx, V> {
140141
Place(PlaceRef<'tcx, V>),
141142
/// `UnsizedPlace(p)`: `p` itself is a thin pointer (indirect place).

0 commit comments

Comments
 (0)