Skip to content

Commit e0078cb

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. 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 3f9f20f + f5cbd83 commit e0078cb

File tree

14 files changed

+428
-144
lines changed

14 files changed

+428
-144
lines changed

compiler/rustc_abi/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1821,7 +1821,7 @@ pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
18211821

18221822
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
18231823
Single {
1824-
/// Always `0` for types that cannot have multiple variants.
1824+
/// Always [`FIRST_VARIANT`] for types that cannot have multiple variants.
18251825
index: VariantIdx,
18261826
},
18271827

compiler/rustc_codegen_ssa/src/mir/analyze.rs

Lines changed: 103 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
//! An analysis to determine which locals require allocas and
22
//! which do not.
33
4+
use rustc_abi as abi;
45
use rustc_data_structures::graph::dominators::Dominators;
56
use rustc_index::bit_set::DenseBitSet;
67
use rustc_index::{IndexSlice, IndexVec};
78
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
89
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};
1011
use rustc_middle::{bug, span_bug};
1112
use tracing::debug;
1213

@@ -99,63 +100,117 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> LocalAnalyzer<'a, 'b, 'tcx, Bx>
99100
context: PlaceContext,
100101
location: Location,
101102
) {
102-
let cx = self.fx.cx;
103+
if !place_ref.projection.is_empty() {
104+
const COPY_CONTEXT: PlaceContext =
105+
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
106+
107+
// `PlaceElem::Index` is the only variant that can mention other `Local`s,
108+
// so check for those up-front before any potential short-circuits.
109+
for elem in place_ref.projection {
110+
if let mir::PlaceElem::Index(index_local) = *elem {
111+
self.visit_local(index_local, COPY_CONTEXT, location);
112+
}
113+
}
103114

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-
};
115+
// If our local is already memory, nothing can make it *more* memory
116+
// so we don't need to bother checking the projections further.
117+
if self.locals[place_ref.local] == LocalKind::Memory {
118+
return;
119+
}
110120

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;
121+
if place_ref.is_indirect_first_projection() {
122+
// If this starts with a `Deref`, we only need to record a read of the
123+
// pointer being dereferenced, as all the subsequent projections are
124+
// working on a place which is always supported. (And because we're
125+
// looking at codegen MIR, it can only happen as the first projection.)
126+
self.visit_local(place_ref.local, COPY_CONTEXT, location);
127+
return;
128+
}
129+
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;
127147
}
128148

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))
159+
| (SimdVector { element: a, .. }, SimdVector { element: b, .. }) => {
160+
compatible_initness(a, b)
161+
}
162+
(ScalarPair(a0, a1), Scalar(b)) => {
163+
compatible_initness(a0, b) && compatible_initness(a1, b)
164+
}
165+
(ScalarPair(a0, a1), ScalarPair(b0, b1)) => {
166+
compatible_initness(a0, b0) && compatible_initness(a1, b1)
167+
}
168+
// This arm is a hack; remove it as part of
169+
// <https://github.com/rust-lang/compiler-team/issues/838>
170+
(SimdVector { .. }, Memory { .. }) => true,
171+
_ => bug!("Mismatched layouts in analysis\nsrc: {src:?}\ntgt: {tgt:?}"),
136172
}
137173
}
138-
}
139174

140-
if let mir::ProjectionElem::Deref = elem {
141-
// Deref projections typically only read the pointer.
142-
base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
143-
}
175+
match *elem {
176+
mir::PlaceElem::Field(fidx, ..) => {
177+
let field_layout = layout.field(self.fx.cx, fidx.as_usize());
178+
if compatible_projection(layout, field_layout) {
179+
layout = field_layout;
180+
continue;
181+
}
182+
}
183+
mir::PlaceElem::Downcast(_, vidx) => {
184+
let variant_layout = layout.for_variant(self.fx.cx, vidx);
185+
if compatible_projection(layout, variant_layout) {
186+
layout = variant_layout;
187+
continue;
188+
}
189+
}
144190

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-
);
191+
mir::PlaceElem::Index(..)
192+
| mir::PlaceElem::ConstantIndex { .. }
193+
| mir::PlaceElem::Subslice { .. }
194+
| mir::PlaceElem::OpaqueCast(..)
195+
| mir::PlaceElem::UnwrapUnsafeBinder(..)
196+
| mir::PlaceElem::Subtype(..) => {}
197+
198+
mir::PlaceElem::Deref => bug!("Deref after first position"),
199+
}
200+
201+
// If the above didn't `continue`, we can't handle the projection.
202+
self.locals[place_ref.local] = LocalKind::Memory;
203+
return;
155204
}
156-
} else {
157-
self.visit_local(place_ref.local, context, location);
205+
debug_assert!(
206+
!self.fx.cx.is_backend_ref(layout),
207+
"Post-projection {place_ref:?} layout should be non-Ref, but it's {layout:?}",
208+
);
158209
}
210+
211+
// Even with supported projections, we still need to have `visit_local`
212+
// check for things that can't be done in SSA (like `SharedBorrow`).
213+
self.visit_local(place_ref.local, context, location);
159214
}
160215
}
161216

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::cmp;
22

3-
use rustc_abi::{Align, BackendRepr, ExternAbi, HasDataLayout, Reg, Size, WrappingRange};
3+
use rustc_abi::{Align, BackendRepr, ExternAbi, FieldIdx, HasDataLayout, Reg, Size, WrappingRange};
44
use rustc_ast as ast;
55
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
66
use rustc_data_structures::packed::Pu128;
@@ -1038,7 +1038,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
10381038
let (idx, _) = op.layout.non_1zst_field(bx).expect(
10391039
"not exactly one non-1-ZST field in a `DispatchFromDyn` type",
10401040
);
1041-
op = op.extract_field(self, bx, idx.as_usize());
1041+
op = op.extract_field(self, bx, None, idx);
10421042
}
10431043

10441044
// Now that we have `*dyn Trait` or `&dyn Trait`, split it up into its
@@ -1598,7 +1598,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
15981598
} else {
15991599
// If the tuple is immediate, the elements are as well.
16001600
for i in 0..tuple.layout.fields.count() {
1601-
let op = tuple.extract_field(self, bx, i);
1601+
let op = tuple.extract_field(self, bx, None, FieldIdx::from_usize(i));
16021602
self.codegen_argument(bx, op, llargs, &args[i], lifetime_ends_after_call);
16031603
}
16041604
}

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)