mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Auto merge of #144347 - scottmcm:ssa-enums-v0, r=WaffleLapkin
No longer need `alloca`s for consuming `Result<!, i32>` and similar In optimized builds GVN gets rid of these already, but in `opt-level=0` we actually make `alloca`s for this, which particularly impacts `?`-style things that use actually-only-one-variant types like this. While doing so, rewrite `LocalAnalyzer::process_place` to be non-recursive, solving a 6+ year old FIXME. r? codegen
This commit is contained in:
@@ -1,12 +1,13 @@
|
||||
//! An analysis to determine which locals require allocas and
|
||||
//! which do not.
|
||||
|
||||
use rustc_abi as abi;
|
||||
use rustc_data_structures::graph::dominators::Dominators;
|
||||
use rustc_index::bit_set::DenseBitSet;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind, traversal};
|
||||
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::{bug, span_bug};
|
||||
use tracing::debug;
|
||||
|
||||
@@ -99,63 +100,75 @@ fn process_place(
|
||||
context: PlaceContext,
|
||||
location: Location,
|
||||
) {
|
||||
let cx = self.fx.cx;
|
||||
if !place_ref.projection.is_empty() {
|
||||
const COPY_CONTEXT: PlaceContext =
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
|
||||
|
||||
if let Some((place_base, elem)) = place_ref.last_projection() {
|
||||
let mut base_context = if context.is_mutating_use() {
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Projection)
|
||||
} else {
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::Projection)
|
||||
};
|
||||
|
||||
// Allow uses of projections that are ZSTs or from scalar fields.
|
||||
let is_consume = matches!(
|
||||
context,
|
||||
PlaceContext::NonMutatingUse(
|
||||
NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
|
||||
)
|
||||
);
|
||||
if is_consume {
|
||||
let base_ty = place_base.ty(self.fx.mir, cx.tcx());
|
||||
let base_ty = self.fx.monomorphize(base_ty);
|
||||
|
||||
// ZSTs don't require any actual memory access.
|
||||
let elem_ty = base_ty.projection_ty(cx.tcx(), self.fx.monomorphize(elem)).ty;
|
||||
let span = self.fx.mir.local_decls[place_ref.local].source_info.span;
|
||||
if cx.spanned_layout_of(elem_ty, span).is_zst() {
|
||||
return;
|
||||
// `PlaceElem::Index` is the only variant that can mention other `Local`s,
|
||||
// so check for those up-front before any potential short-circuits.
|
||||
for elem in place_ref.projection {
|
||||
if let mir::PlaceElem::Index(index_local) = *elem {
|
||||
self.visit_local(index_local, COPY_CONTEXT, location);
|
||||
}
|
||||
}
|
||||
|
||||
if let mir::ProjectionElem::Field(..) = elem {
|
||||
let layout = cx.spanned_layout_of(base_ty.ty, span);
|
||||
if cx.is_backend_immediate(layout) || cx.is_backend_scalar_pair(layout) {
|
||||
// Recurse with the same context, instead of `Projection`,
|
||||
// potentially stopping at non-operand projections,
|
||||
// which would trigger `not_ssa` on locals.
|
||||
base_context = context;
|
||||
// If our local is already memory, nothing can make it *more* memory
|
||||
// so we don't need to bother checking the projections further.
|
||||
if self.locals[place_ref.local] == LocalKind::Memory {
|
||||
return;
|
||||
}
|
||||
|
||||
if place_ref.is_indirect_first_projection() {
|
||||
// If this starts with a `Deref`, we only need to record a read of the
|
||||
// pointer being dereferenced, as all the subsequent projections are
|
||||
// working on a place which is always supported. (And because we're
|
||||
// looking at codegen MIR, it can only happen as the first projection.)
|
||||
self.visit_local(place_ref.local, COPY_CONTEXT, location);
|
||||
return;
|
||||
}
|
||||
|
||||
if context.is_mutating_use() {
|
||||
// If it's a mutating use it doesn't matter what the projections are,
|
||||
// if there are *any* then we need a place to write. (For example,
|
||||
// `_1 = Foo()` works in SSA but `_2.0 = Foo()` does not.)
|
||||
let mut_projection = PlaceContext::MutatingUse(MutatingUseContext::Projection);
|
||||
self.visit_local(place_ref.local, mut_projection, location);
|
||||
return;
|
||||
}
|
||||
|
||||
// Scan through to ensure the only projections are those which
|
||||
// `FunctionCx::maybe_codegen_consume_direct` can handle.
|
||||
let base_ty = self.fx.monomorphized_place_ty(mir::PlaceRef::from(place_ref.local));
|
||||
let mut layout = self.fx.cx.layout_of(base_ty);
|
||||
for elem in place_ref.projection {
|
||||
layout = match *elem {
|
||||
mir::PlaceElem::Field(fidx, ..) => layout.field(self.fx.cx, fidx.as_usize()),
|
||||
mir::PlaceElem::Downcast(_, vidx)
|
||||
if let abi::Variants::Single { index: single_variant } =
|
||||
layout.variants
|
||||
&& vidx == single_variant =>
|
||||
{
|
||||
layout.for_variant(self.fx.cx, vidx)
|
||||
}
|
||||
mir::PlaceElem::Subtype(subtype_ty) => {
|
||||
let subtype_ty = self.fx.monomorphize(subtype_ty);
|
||||
self.fx.cx.layout_of(subtype_ty)
|
||||
}
|
||||
_ => {
|
||||
self.locals[place_ref.local] = LocalKind::Memory;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let mir::ProjectionElem::Deref = elem {
|
||||
// Deref projections typically only read the pointer.
|
||||
base_context = PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy);
|
||||
}
|
||||
|
||||
self.process_place(&place_base, base_context, location);
|
||||
// HACK(eddyb) this emulates the old `visit_projection_elem`, this
|
||||
// entire `visit_place`-like `process_place` method should be rewritten,
|
||||
// now that we have moved to the "slice of projections" representation.
|
||||
if let mir::ProjectionElem::Index(local) = elem {
|
||||
self.visit_local(
|
||||
local,
|
||||
PlaceContext::NonMutatingUse(NonMutatingUseContext::Copy),
|
||||
location,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
self.visit_local(place_ref.local, context, location);
|
||||
debug_assert!(
|
||||
!self.fx.cx.is_backend_ref(layout),
|
||||
"Post-projection {place_ref:?} layout should be non-Ref, but it's {layout:?}",
|
||||
);
|
||||
}
|
||||
|
||||
// Even with supported projections, we still need to have `visit_local`
|
||||
// check for things that can't be done in SSA (like `SharedBorrow`).
|
||||
self.visit_local(place_ref.local, context, location);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -921,9 +921,10 @@ fn maybe_codegen_consume_direct(
|
||||
|
||||
match self.locals[place_ref.local] {
|
||||
LocalRef::Operand(mut o) => {
|
||||
// Moves out of scalar and scalar pair fields are trivial.
|
||||
for elem in place_ref.projection.iter() {
|
||||
match elem {
|
||||
// We only need to handle the projections that
|
||||
// `LocalAnalyzer::process_place` let make it here.
|
||||
for elem in place_ref.projection {
|
||||
match *elem {
|
||||
mir::ProjectionElem::Field(f, _) => {
|
||||
assert!(
|
||||
!o.layout.ty.is_any_ptr(),
|
||||
@@ -932,17 +933,18 @@ fn maybe_codegen_consume_direct(
|
||||
);
|
||||
o = o.extract_field(self, bx, f.index());
|
||||
}
|
||||
mir::ProjectionElem::Index(_)
|
||||
| mir::ProjectionElem::ConstantIndex { .. } => {
|
||||
// ZSTs don't require any actual memory access.
|
||||
// FIXME(eddyb) deduplicate this with the identical
|
||||
// checks in `codegen_consume` and `extract_field`.
|
||||
let elem = o.layout.field(bx.cx(), 0);
|
||||
if elem.is_zst() {
|
||||
o = OperandRef::zero_sized(elem);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
mir::PlaceElem::Downcast(_, vidx) => {
|
||||
debug_assert_eq!(
|
||||
o.layout.variants,
|
||||
abi::Variants::Single { index: vidx },
|
||||
);
|
||||
let layout = o.layout.for_variant(bx.cx(), vidx);
|
||||
o = OperandRef { val: o.val, layout }
|
||||
}
|
||||
mir::PlaceElem::Subtype(subtype_ty) => {
|
||||
let subtype_ty = self.monomorphize(subtype_ty);
|
||||
let layout = self.cx.layout_of(subtype_ty);
|
||||
o = OperandRef { val: o.val, layout }
|
||||
}
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
//@ compile-flags: -Copt-level=0
|
||||
//@ only-64bit
|
||||
|
||||
#![crate_type = "lib"]
|
||||
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
pub enum Never {}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn make_unmake_result_never(x: i32) -> i32 {
|
||||
// CHECK-LABEL: define i32 @make_unmake_result_never(i32 %x)
|
||||
// CHECK: start:
|
||||
// CHECK-NEXT: ret i32 %x
|
||||
|
||||
let y: Result<i32, Never> = Ok(x);
|
||||
let Ok(z) = y;
|
||||
z
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn extract_control_flow_never(x: ControlFlow<&str, Never>) -> &str {
|
||||
// CHECK-LABEL: define { ptr, i64 } @extract_control_flow_never(ptr align 1 %x.0, i64 %x.1)
|
||||
// CHECK: start:
|
||||
// CHECK-NEXT: %[[P0:.+]] = insertvalue { ptr, i64 } poison, ptr %x.0, 0
|
||||
// CHECK-NEXT: %[[P1:.+]] = insertvalue { ptr, i64 } %[[P0]], i64 %x.1, 1
|
||||
// CHECK-NEXT: ret { ptr, i64 } %[[P1]]
|
||||
|
||||
let ControlFlow::Break(s) = x;
|
||||
s
|
||||
}
|
||||
Reference in New Issue
Block a user