summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_codegen_ssa/src/mir
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:20:29 +0000
commit631cd5845e8de329d0e227aaa707d7ea228b8f8f (patch)
treea1b87c8f8cad01cf18f7c5f57a08f102771ed303 /compiler/rustc_codegen_ssa/src/mir
parentAdding debian version 1.69.0+dfsg1-1. (diff)
downloadrustc-631cd5845e8de329d0e227aaa707d7ea228b8f8f.tar.xz
rustc-631cd5845e8de329d0e227aaa707d7ea228b8f8f.zip
Merging upstream version 1.70.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_codegen_ssa/src/mir')
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/analyze.rs21
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/block.rs356
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/debuginfo.rs49
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/mod.rs25
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/operand.rs51
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/place.rs97
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/rvalue.rs274
-rw-r--r--compiler/rustc_codegen_ssa/src/mir/statement.rs7
8 files changed, 500 insertions, 380 deletions
diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
index 95aad10fd..f43f1d64a 100644
--- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs
@@ -5,7 +5,7 @@ use super::FunctionCx;
use crate::traits::*;
use rustc_data_structures::graph::dominators::Dominators;
use rustc_index::bit_set::BitSet;
-use rustc_index::vec::IndexVec;
+use rustc_index::vec::{IndexSlice, IndexVec};
use rustc_middle::mir::traversal;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, Location, TerminatorKind};
@@ -232,7 +232,6 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
| PlaceContext::NonMutatingUse(
NonMutatingUseContext::Inspect
| NonMutatingUseContext::SharedBorrow
- | NonMutatingUseContext::UniqueBorrow
| NonMutatingUseContext::ShallowBorrow
| NonMutatingUseContext::AddressOf
| NonMutatingUseContext::Projection,
@@ -277,14 +276,14 @@ impl CleanupKind {
/// Recover that structure in an analyze pass.
pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKind> {
fn discover_masters<'tcx>(
- result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
+ result: &mut IndexSlice<mir::BasicBlock, CleanupKind>,
mir: &mir::Body<'tcx>,
) {
for (bb, data) in mir.basic_blocks.iter_enumerated() {
match data.terminator().kind {
TerminatorKind::Goto { .. }
| TerminatorKind::Resume
- | TerminatorKind::Abort
+ | TerminatorKind::Terminate
| TerminatorKind::Return
| TerminatorKind::GeneratorDrop
| TerminatorKind::Unreachable
@@ -292,12 +291,11 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
| TerminatorKind::Yield { .. }
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. } => { /* nothing to do */ }
- TerminatorKind::Call { cleanup: unwind, .. }
- | TerminatorKind::InlineAsm { cleanup: unwind, .. }
- | TerminatorKind::Assert { cleanup: unwind, .. }
- | TerminatorKind::DropAndReplace { unwind, .. }
+ TerminatorKind::Call { unwind, .. }
+ | TerminatorKind::InlineAsm { unwind, .. }
+ | TerminatorKind::Assert { unwind, .. }
| TerminatorKind::Drop { unwind, .. } => {
- if let Some(unwind) = unwind {
+ if let mir::UnwindAction::Cleanup(unwind) = unwind {
debug!(
"cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
bb, data, unwind
@@ -309,7 +307,10 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
}
}
- fn propagate<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>, mir: &mir::Body<'tcx>) {
+ fn propagate<'tcx>(
+ result: &mut IndexSlice<mir::BasicBlock, CleanupKind>,
+ mir: &mir::Body<'tcx>,
+ ) {
let mut funclet_succs = IndexVec::from_elem(None, &mir.basic_blocks);
let mut set_successor = |funclet: mir::BasicBlock, succ| match funclet_succs[funclet] {
diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs
index 57a19a4ab..dd8697781 100644
--- a/compiler/rustc_codegen_ssa/src/mir/block.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/block.rs
@@ -16,7 +16,7 @@ use rustc_index::vec::Idx;
use rustc_middle::mir::{self, AssertKind, SwitchTargets};
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, ValidityRequirement};
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
-use rustc_middle::ty::{self, Instance, Ty, TypeVisitableExt};
+use rustc_middle::ty::{self, Instance, Ty};
use rustc_session::config::OptLevel;
use rustc_span::source_map::Span;
use rustc_span::{sym, Symbol};
@@ -147,7 +147,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
}
/// Call `fn_ptr` of `fn_abi` with the arguments `llargs`, the optional
- /// return destination `destination` and the cleanup function `cleanup`.
+ /// return destination `destination` and the unwind action `unwind`.
fn do_call<Bx: BuilderMethods<'a, 'tcx>>(
&self,
fx: &mut FunctionCx<'a, 'tcx, Bx>,
@@ -156,7 +156,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
fn_ptr: Bx::Value,
llargs: &[Bx::Value],
destination: Option<(ReturnDest<'tcx, Bx::Value>, mir::BasicBlock)>,
- cleanup: Option<mir::BasicBlock>,
+ mut unwind: mir::UnwindAction,
copied_constant_arguments: &[PlaceRef<'tcx, <Bx as BackendTypes>::Value>],
mergeable_succ: bool,
) -> MergingSucc {
@@ -164,23 +164,23 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
// do an invoke, otherwise do a call.
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
- let unwind_block = if let Some(cleanup) = cleanup.filter(|_| fn_abi.can_unwind) {
- Some(self.llbb_with_cleanup(fx, cleanup))
- } else if fx.mir[self.bb].is_cleanup
- && fn_abi.can_unwind
- && !base::wants_msvc_seh(fx.cx.tcx().sess)
- {
- // Exception must not propagate out of the execution of a cleanup (doing so
- // can cause undefined behaviour). We insert a double unwind guard for
- // functions that can potentially unwind to protect against this.
- //
- // This is not necessary for SEH which does not use successive unwinding
- // like Itanium EH. EH frames in SEH are different from normal function
- // frames and SEH will abort automatically if an exception tries to
- // propagate out from cleanup.
- Some(fx.double_unwind_guard())
- } else {
- None
+ if !fn_abi.can_unwind {
+ unwind = mir::UnwindAction::Unreachable;
+ }
+
+ let unwind_block = match unwind {
+ mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
+ mir::UnwindAction::Continue => None,
+ mir::UnwindAction::Unreachable => None,
+ mir::UnwindAction::Terminate => {
+ if fx.mir[self.bb].is_cleanup && base::wants_msvc_seh(fx.cx.tcx().sess) {
+ // SEH will abort automatically if an exception tries to
+ // propagate out from cleanup.
+ None
+ } else {
+ Some(fx.terminate_block())
+ }
+ }
};
if let Some(unwind_block) = unwind_block {
@@ -234,7 +234,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
}
}
- /// Generates inline assembly with optional `destination` and `cleanup`.
+ /// Generates inline assembly with optional `destination` and `unwind`.
fn do_inlineasm<Bx: BuilderMethods<'a, 'tcx>>(
&self,
fx: &mut FunctionCx<'a, 'tcx, Bx>,
@@ -244,11 +244,18 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
options: InlineAsmOptions,
line_spans: &[Span],
destination: Option<mir::BasicBlock>,
- cleanup: Option<mir::BasicBlock>,
+ unwind: mir::UnwindAction,
instance: Instance<'_>,
mergeable_succ: bool,
) -> MergingSucc {
- if let Some(cleanup) = cleanup {
+ let unwind_target = match unwind {
+ mir::UnwindAction::Cleanup(cleanup) => Some(self.llbb_with_cleanup(fx, cleanup)),
+ mir::UnwindAction::Terminate => Some(fx.terminate_block()),
+ mir::UnwindAction::Continue => None,
+ mir::UnwindAction::Unreachable => None,
+ };
+
+ if let Some(cleanup) = unwind_target {
let ret_llbb = if let Some(target) = destination {
fx.llbb(target)
} else {
@@ -261,7 +268,7 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
options,
line_spans,
instance,
- Some((ret_llbb, self.llbb_with_cleanup(fx, cleanup), self.funclet(fx))),
+ Some((ret_llbb, cleanup, self.funclet(fx))),
);
MergingSucc::False
} else {
@@ -397,8 +404,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
PassMode::Cast(cast_ty, _) => {
let op = match self.locals[mir::RETURN_PLACE] {
- LocalRef::Operand(Some(op)) => op,
- LocalRef::Operand(None) => bug!("use of return before def"),
+ LocalRef::Operand(op) => op,
+ LocalRef::PendingOperand => bug!("use of return before def"),
LocalRef::Place(cg_place) => OperandRef {
val: Ref(cg_place.llval, None, cg_place.align),
layout: cg_place.layout,
@@ -431,7 +438,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx: &mut Bx,
location: mir::Place<'tcx>,
target: mir::BasicBlock,
- unwind: Option<mir::BasicBlock>,
+ unwind: mir::UnwindAction,
mergeable_succ: bool,
) -> MergingSucc {
let ty = location.ty(self.mir, bx.tcx()).ty;
@@ -552,7 +559,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
expected: bool,
msg: &mir::AssertMessage<'tcx>,
target: mir::BasicBlock,
- cleanup: Option<mir::BasicBlock>,
+ unwind: mir::UnwindAction,
mergeable_succ: bool,
) -> MergingSucc {
let span = terminator.source_info.span;
@@ -563,15 +570,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// with #[rustc_inherit_overflow_checks] and inlined from
// another crate (mostly core::num generic/#[inline] fns),
// while the current crate doesn't use overflow checks.
- if !bx.cx().check_overflow() {
- let overflow_not_to_check = match msg {
- AssertKind::OverflowNeg(..) => true,
- AssertKind::Overflow(op, ..) => op.is_checkable(),
- _ => false,
- };
- if overflow_not_to_check {
- const_cond = Some(expected);
- }
+ if !bx.cx().check_overflow() && msg.is_optional_overflow_check() {
+ const_cond = Some(expected);
}
// Don't codegen the panic block if success if known.
@@ -607,6 +607,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// and `#[track_caller]` adds an implicit third argument.
(LangItem::PanicBoundsCheck, vec![index, len, location])
}
+ AssertKind::MisalignedPointerDereference { ref required, ref found } => {
+ let required = self.codegen_operand(bx, required).immediate();
+ let found = self.codegen_operand(bx, found).immediate();
+ // It's `fn panic_bounds_check(index: usize, len: usize)`,
+ // and `#[track_caller]` adds an implicit third argument.
+ (LangItem::PanicMisalignedPointerDereference, vec![required, found, location])
+ }
_ => {
let msg = bx.const_str(msg.description());
// It's `pub fn panic(expr: &str)`, with the wide reference being passed
@@ -618,12 +625,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let (fn_abi, llfn) = common::build_langcall(bx, Some(span), lang_item);
// Codegen the actual panic invoke/call.
- let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &args, None, cleanup, &[], false);
+ let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &args, None, unwind, &[], false);
assert_eq!(merging_succ, MergingSucc::False);
MergingSucc::False
}
- fn codegen_abort_terminator(
+ fn codegen_terminate_terminator(
&mut self,
helper: TerminatorCodegenHelper<'tcx>,
bx: &mut Bx,
@@ -636,7 +643,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let (fn_abi, llfn) = common::build_langcall(bx, Some(span), LangItem::PanicCannotUnwind);
// Codegen the actual panic invoke/call.
- let merging_succ = helper.do_call(self, bx, fn_abi, llfn, &[], None, None, &[], false);
+ let merging_succ = helper.do_call(
+ self,
+ bx,
+ fn_abi,
+ llfn,
+ &[],
+ None,
+ mir::UnwindAction::Unreachable,
+ &[],
+ false,
+ );
assert_eq!(merging_succ, MergingSucc::False);
}
@@ -649,7 +666,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
instance: Option<Instance<'tcx>>,
source_info: mir::SourceInfo,
target: Option<mir::BasicBlock>,
- cleanup: Option<mir::BasicBlock>,
+ unwind: mir::UnwindAction,
mergeable_succ: bool,
) -> Option<MergingSucc> {
// Emit a panic or a no-op for `assert_*` intrinsics.
@@ -696,7 +713,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
llfn,
&[msg.0, msg.1],
target.as_ref().map(|bb| (ReturnDest::Nothing, *bb)),
- cleanup,
+ unwind,
&[],
mergeable_succ,
)
@@ -719,7 +736,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
args: &[mir::Operand<'tcx>],
destination: mir::Place<'tcx>,
target: Option<mir::BasicBlock>,
- cleanup: Option<mir::BasicBlock>,
+ unwind: mir::UnwindAction,
fn_span: Span,
mergeable_succ: bool,
) -> MergingSucc {
@@ -776,23 +793,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
None => bx.fn_abi_of_fn_ptr(sig, extra_args),
};
- if intrinsic == Some(sym::transmute) {
- return if let Some(target) = target {
- self.codegen_transmute(bx, &args[0], destination);
- helper.funclet_br(self, bx, target, mergeable_succ)
- } else {
- // If we are trying to transmute to an uninhabited type,
- // it is likely there is no allotted destination. In fact,
- // transmuting to an uninhabited type is UB, which means
- // we can do what we like. Here, we declare that transmuting
- // into an uninhabited type is impossible, so anything following
- // it must be unreachable.
- assert_eq!(fn_abi.ret.layout.abi, abi::Abi::Uninhabited);
- bx.unreachable();
- MergingSucc::False
- };
- }
-
if let Some(merging_succ) = self.codegen_panic_intrinsic(
&helper,
bx,
@@ -800,7 +800,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
instance,
source_info,
target,
- cleanup,
+ unwind,
mergeable_succ,
) {
return merging_succ;
@@ -835,7 +835,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
match intrinsic {
None | Some(sym::drop_in_place) => {}
- Some(sym::copy_nonoverlapping) => unreachable!(),
Some(intrinsic) => {
let dest = match ret_dest {
_ if fn_abi.ret.is_indirect() => llargs[0],
@@ -924,7 +923,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
//
// This is also relevant for `Pin<&mut Self>`, where we need to peel the `Pin`.
'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
- && !op.layout.ty.is_region_ptr()
+ && !op.layout.ty.is_ref()
{
for i in 0..op.layout.fields.count() {
let field = op.extract_field(bx, i);
@@ -966,7 +965,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Immediate(_) => {
// See comment above explaining why we peel these newtypes
'descend_newtypes: while !op.layout.ty.is_unsafe_ptr()
- && !op.layout.ty.is_region_ptr()
+ && !op.layout.ty.is_ref()
{
for i in 0..op.layout.fields.count() {
let field = op.extract_field(bx, i);
@@ -1082,7 +1081,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
fn_ptr,
&llargs,
target.as_ref().map(|&target| (ret_dest, target)),
- cleanup,
+ unwind,
&copied_constant_arguments,
false,
);
@@ -1102,7 +1101,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
fn_ptr,
&llargs,
target.as_ref().map(|&target| (ret_dest, target)),
- cleanup,
+ unwind,
&copied_constant_arguments,
mergeable_succ,
)
@@ -1118,7 +1117,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
options: ast::InlineAsmOptions,
line_spans: &[Span],
destination: Option<mir::BasicBlock>,
- cleanup: Option<mir::BasicBlock>,
+ unwind: mir::UnwindAction,
instance: Instance<'_>,
mergeable_succ: bool,
) -> MergingSucc {
@@ -1182,7 +1181,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
options,
line_spans,
destination,
- cleanup,
+ unwind,
instance,
mergeable_succ,
)
@@ -1264,8 +1263,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
MergingSucc::False
}
- mir::TerminatorKind::Abort => {
- self.codegen_abort_terminator(helper, bx, terminator);
+ mir::TerminatorKind::Terminate => {
+ self.codegen_terminate_terminator(helper, bx, terminator);
MergingSucc::False
}
@@ -1292,7 +1291,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
self.codegen_drop_terminator(helper, bx, place, target, unwind, mergeable_succ())
}
- mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => self
+ mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, unwind } => self
.codegen_assert_terminator(
helper,
bx,
@@ -1301,20 +1300,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
expected,
msg,
target,
- cleanup,
+ unwind,
mergeable_succ(),
),
- mir::TerminatorKind::DropAndReplace { .. } => {
- bug!("undesugared DropAndReplace in codegen: {:?}", terminator);
- }
-
mir::TerminatorKind::Call {
ref func,
ref args,
destination,
target,
- cleanup,
+ unwind,
from_hir_call: _,
fn_span,
} => self.codegen_call_terminator(
@@ -1325,7 +1320,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
args,
destination,
target,
- cleanup,
+ unwind,
fn_span,
mergeable_succ(),
),
@@ -1342,7 +1337,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
options,
line_spans,
destination,
- cleanup,
+ unwind,
} => self.codegen_asm_terminator(
helper,
bx,
@@ -1352,7 +1347,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
options,
line_spans,
destination,
- cleanup,
+ unwind,
self.instance,
mergeable_succ(),
),
@@ -1486,7 +1481,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
) -> OperandRef<'tcx, Bx::Value> {
let tcx = bx.tcx();
- let mut span_to_caller_location = |span: Span| {
+ let mut span_to_caller_location = |mut span: Span| {
+ // Remove `Inlined` marks as they pollute `expansion_cause`.
+ while span.is_inlined() {
+ span.remove_mark();
+ }
let topmost = span.ctxt().outer_expn().expansion_cause().unwrap_or(span);
let caller = tcx.sess.source_map().lookup_char_pos(topmost.lo());
let const_loc = tcx.const_caller_location((
@@ -1554,62 +1553,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
fn landing_pad_for_uncached(&mut self, bb: mir::BasicBlock) -> Bx::BasicBlock {
let llbb = self.llbb(bb);
if base::wants_msvc_seh(self.cx.sess()) {
- let funclet;
- let ret_llbb;
- match self.mir[bb].terminator.as_ref().map(|t| &t.kind) {
- // This is a basic block that we're aborting the program for,
- // notably in an `extern` function. These basic blocks are inserted
- // so that we assert that `extern` functions do indeed not panic,
- // and if they do we abort the process.
- //
- // On MSVC these are tricky though (where we're doing funclets). If
- // we were to do a cleanuppad (like below) the normal functions like
- // `longjmp` would trigger the abort logic, terminating the
- // program. Instead we insert the equivalent of `catch(...)` for C++
- // which magically doesn't trigger when `longjmp` files over this
- // frame.
- //
- // Lots more discussion can be found on #48251 but this codegen is
- // modeled after clang's for:
- //
- // try {
- // foo();
- // } catch (...) {
- // bar();
- // }
- Some(&mir::TerminatorKind::Abort) => {
- let cs_llbb =
- Bx::append_block(self.cx, self.llfn, &format!("cs_funclet{:?}", bb));
- let cp_llbb =
- Bx::append_block(self.cx, self.llfn, &format!("cp_funclet{:?}", bb));
- ret_llbb = cs_llbb;
-
- let mut cs_bx = Bx::build(self.cx, cs_llbb);
- let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);
-
- // The "null" here is actually a RTTI type descriptor for the
- // C++ personality function, but `catch (...)` has no type so
- // it's null. The 64 here is actually a bitfield which
- // represents that this is a catch-all block.
- let mut cp_bx = Bx::build(self.cx, cp_llbb);
- let null = cp_bx.const_null(
- cp_bx.type_i8p_ext(cp_bx.cx().data_layout().instruction_address_space),
- );
- let sixty_four = cp_bx.const_i32(64);
- funclet = cp_bx.catch_pad(cs, &[null, sixty_four, null]);
- cp_bx.br(llbb);
- }
- _ => {
- let cleanup_llbb =
- Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb));
- ret_llbb = cleanup_llbb;
- let mut cleanup_bx = Bx::build(self.cx, cleanup_llbb);
- funclet = cleanup_bx.cleanup_pad(None, &[]);
- cleanup_bx.br(llbb);
- }
- }
+ let cleanup_bb = Bx::append_block(self.cx, self.llfn, &format!("funclet_{:?}", bb));
+ let mut cleanup_bx = Bx::build(self.cx, cleanup_bb);
+ let funclet = cleanup_bx.cleanup_pad(None, &[]);
+ cleanup_bx.br(llbb);
self.funclets[bb] = Some(funclet);
- ret_llbb
+ cleanup_bb
} else {
let cleanup_llbb = Bx::append_block(self.cx, self.llfn, "cleanup");
let mut cleanup_bx = Bx::build(self.cx, cleanup_llbb);
@@ -1636,26 +1585,68 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
})
}
- fn double_unwind_guard(&mut self) -> Bx::BasicBlock {
- self.double_unwind_guard.unwrap_or_else(|| {
- assert!(!base::wants_msvc_seh(self.cx.sess()));
+ fn terminate_block(&mut self) -> Bx::BasicBlock {
+ self.terminate_block.unwrap_or_else(|| {
+ let funclet;
+ let llbb;
+ let mut bx;
+ if base::wants_msvc_seh(self.cx.sess()) {
+ // This is a basic block that we're aborting the program for,
+ // notably in an `extern` function. These basic blocks are inserted
+ // so that we assert that `extern` functions do indeed not panic,
+ // and if they do we abort the process.
+ //
+ // On MSVC these are tricky though (where we're doing funclets). If
+ // we were to do a cleanuppad (like below) the normal functions like
+ // `longjmp` would trigger the abort logic, terminating the
+ // program. Instead we insert the equivalent of `catch(...)` for C++
+ // which magically doesn't trigger when `longjmp` files over this
+ // frame.
+ //
+ // Lots more discussion can be found on #48251 but this codegen is
+ // modeled after clang's for:
+ //
+ // try {
+ // foo();
+ // } catch (...) {
+ // bar();
+ // }
+ llbb = Bx::append_block(self.cx, self.llfn, "cs_terminate");
+ let cp_llbb = Bx::append_block(self.cx, self.llfn, "cp_terminate");
+
+ let mut cs_bx = Bx::build(self.cx, llbb);
+ let cs = cs_bx.catch_switch(None, None, &[cp_llbb]);
+
+ // The "null" here is actually a RTTI type descriptor for the
+ // C++ personality function, but `catch (...)` has no type so
+ // it's null. The 64 here is actually a bitfield which
+ // represents that this is a catch-all block.
+ bx = Bx::build(self.cx, cp_llbb);
+ let null =
+ bx.const_null(bx.type_i8p_ext(bx.cx().data_layout().instruction_address_space));
+ let sixty_four = bx.const_i32(64);
+ funclet = Some(bx.catch_pad(cs, &[null, sixty_four, null]));
+ } else {
+ llbb = Bx::append_block(self.cx, self.llfn, "terminate");
+ bx = Bx::build(self.cx, llbb);
- let llbb = Bx::append_block(self.cx, self.llfn, "abort");
- let mut bx = Bx::build(self.cx, llbb);
- self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span));
+ let llpersonality = self.cx.eh_personality();
+ bx.cleanup_landing_pad(llpersonality);
- let llpersonality = self.cx.eh_personality();
- bx.cleanup_landing_pad(llpersonality);
+ funclet = None;
+ }
+
+ self.set_debug_loc(&mut bx, mir::SourceInfo::outermost(self.mir.span));
let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, LangItem::PanicCannotUnwind);
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
- let llret = bx.call(fn_ty, Some(&fn_abi), fn_ptr, &[], None);
+ let llret = bx.call(fn_ty, Some(&fn_abi), fn_ptr, &[], funclet.as_ref());
bx.do_not_inline(llret);
bx.unreachable();
- self.double_unwind_guard = Some(llbb);
+ self.terminate_block = Some(llbb);
llbb
})
}
@@ -1698,7 +1689,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
match self.locals[index] {
LocalRef::Place(dest) => dest,
LocalRef::UnsizedPlace(_) => bug!("return type must be sized"),
- LocalRef::Operand(None) => {
+ LocalRef::PendingOperand => {
// Handle temporary places, specifically `Operand` ones, as
// they don't have `alloca`s.
return if fn_ret.is_indirect() {
@@ -1719,7 +1710,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
ReturnDest::DirectOperand(index)
};
}
- LocalRef::Operand(Some(_)) => {
+ LocalRef::Operand(_) => {
bug!("place local already assigned to");
}
}
@@ -1746,71 +1737,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
- fn codegen_transmute(&mut self, bx: &mut Bx, src: &mir::Operand<'tcx>, dst: mir::Place<'tcx>) {
- if let Some(index) = dst.as_local() {
- match self.locals[index] {
- LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place),
- LocalRef::UnsizedPlace(_) => bug!("transmute must not involve unsized locals"),
- LocalRef::Operand(None) => {
- let dst_layout = bx.layout_of(self.monomorphized_place_ty(dst.as_ref()));
- assert!(!dst_layout.ty.has_erasable_regions());
- let place = PlaceRef::alloca(bx, dst_layout);
- place.storage_live(bx);
- self.codegen_transmute_into(bx, src, place);
- let op = bx.load_operand(place);
- place.storage_dead(bx);
- self.locals[index] = LocalRef::Operand(Some(op));
- self.debug_introduce_local(bx, index);
- }
- LocalRef::Operand(Some(op)) => {
- assert!(op.layout.is_zst(), "assigning to initialized SSAtemp");
- }
- }
- } else {
- let dst = self.codegen_place(bx, dst.as_ref());
- self.codegen_transmute_into(bx, src, dst);
- }
- }
-
- fn codegen_transmute_into(
- &mut self,
- bx: &mut Bx,
- src: &mir::Operand<'tcx>,
- dst: PlaceRef<'tcx, Bx::Value>,
- ) {
- let src = self.codegen_operand(bx, src);
-
- // Special-case transmutes between scalars as simple bitcasts.
- match (src.layout.abi, dst.layout.abi) {
- (abi::Abi::Scalar(src_scalar), abi::Abi::Scalar(dst_scalar)) => {
- // HACK(eddyb) LLVM doesn't like `bitcast`s between pointers and non-pointers.
- let src_is_ptr = matches!(src_scalar.primitive(), abi::Pointer(_));
- let dst_is_ptr = matches!(dst_scalar.primitive(), abi::Pointer(_));
- if src_is_ptr == dst_is_ptr {
- assert_eq!(src.layout.size, dst.layout.size);
-
- // NOTE(eddyb) the `from_immediate` and `to_immediate_scalar`
- // conversions allow handling `bool`s the same as `u8`s.
- let src = bx.from_immediate(src.immediate());
- // LLVM also doesn't like `bitcast`s between pointers in different address spaces.
- let src_as_dst = if src_is_ptr {
- bx.pointercast(src, bx.backend_type(dst.layout))
- } else {
- bx.bitcast(src, bx.backend_type(dst.layout))
- };
- Immediate(bx.to_immediate_scalar(src_as_dst, dst_scalar)).store(bx, dst);
- return;
- }
- }
- _ => {}
- }
-
- let llty = bx.backend_type(src.layout);
- let cast_ptr = bx.pointercast(dst.llval, bx.type_ptr_to(llty));
- let align = src.layout.align.abi.min(dst.align);
- src.val.store(bx, PlaceRef::new_sized_aligned(cast_ptr, src.layout, align));
- }
-
// Stores the return value of a function call into it's final location.
fn store_return(
&mut self,
@@ -1827,7 +1753,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
IndirectOperand(tmp, index) => {
let op = bx.load_operand(tmp);
tmp.storage_dead(bx);
- self.locals[index] = LocalRef::Operand(Some(op));
+ self.locals[index] = LocalRef::Operand(op);
self.debug_introduce_local(bx, index);
}
DirectOperand(index) => {
@@ -1842,7 +1768,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
} else {
OperandRef::from_immediate_or_packed_pair(bx, llval, ret_abi.layout)
};
- self.locals[index] = LocalRef::Operand(Some(op));
+ self.locals[index] = LocalRef::Operand(op);
self.debug_introduce_local(bx, index);
}
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
index 708f3bc0c..d049bafb8 100644
--- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs
@@ -8,7 +8,7 @@ use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_session::config::DebugInfo;
use rustc_span::symbol::{kw, Symbol};
use rustc_span::{BytePos, Span};
-use rustc_target::abi::{Abi, Size, VariantIdx};
+use rustc_target::abi::{Abi, FieldIdx, Size, VariantIdx};
use super::operand::{OperandRef, OperandValue};
use super::place::PlaceRef;
@@ -79,7 +79,7 @@ impl<'tcx, S: Copy, L: Copy> DebugScope<S, L> {
trait DebugInfoOffsetLocation<'tcx, Bx> {
fn deref(&self, bx: &mut Bx) -> Self;
fn layout(&self) -> TyAndLayout<'tcx>;
- fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self;
+ fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self;
fn downcast(&self, bx: &mut Bx, variant: VariantIdx) -> Self;
}
@@ -94,7 +94,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
self.layout
}
- fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self {
+ fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self {
PlaceRef::project_field(*self, bx, field.index())
}
@@ -116,7 +116,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> DebugInfoOffsetLocation<'tcx, Bx>
*self
}
- fn project_field(&self, bx: &mut Bx, field: mir::Field) -> Self {
+ fn project_field(&self, bx: &mut Bx, field: FieldIdx) -> Self {
self.field(bx.cx(), field.index())
}
@@ -165,11 +165,15 @@ fn calculate_debuginfo_offset<
mir::ProjectionElem::Downcast(_, variant) => {
place = place.downcast(bx, variant);
}
- _ => span_bug!(
- var.source_info.span,
- "unsupported var debuginfo place `{:?}`",
- mir::Place { local, projection: var.projection },
- ),
+ _ => {
+ // Sanity check for `can_use_in_debuginfo`.
+ debug_assert!(!elem.can_use_in_debuginfo());
+ span_bug!(
+ var.source_info.span,
+ "unsupported var debuginfo place `{:?}`",
+ mir::Place { local, projection: var.projection },
+ )
+ }
}
}
@@ -241,12 +245,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
pub fn debug_introduce_local(&self, bx: &mut Bx, local: mir::Local) {
let full_debug_info = bx.sess().opts.debuginfo == DebugInfo::Full;
- // FIXME(eddyb) maybe name the return place as `_0` or `return`?
- if local == mir::RETURN_PLACE && !self.mir.local_decls[mir::RETURN_PLACE].is_user_variable()
- {
- return;
- }
-
let vars = match &self.per_local_var_debug_info {
Some(per_local) => &per_local[local],
None => return,
@@ -303,7 +301,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let local_ref = &self.locals[local];
- let name = if bx.sess().fewer_names() {
+ // FIXME Should the return place be named?
+ let name = if bx.sess().fewer_names() || local == mir::RETURN_PLACE {
None
} else {
Some(match whole_local_var.or(fallback_var.clone()) {
@@ -317,7 +316,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
LocalRef::Place(place) | LocalRef::UnsizedPlace(place) => {
bx.set_var_name(place.llval, name);
}
- LocalRef::Operand(Some(operand)) => match operand.val {
+ LocalRef::Operand(operand) => match operand.val {
OperandValue::Ref(x, ..) | OperandValue::Immediate(x) => {
bx.set_var_name(x, name);
}
@@ -328,7 +327,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bx.set_var_name(b, &(name.clone() + ".1"));
}
},
- LocalRef::Operand(None) => {}
+ LocalRef::PendingOperand => {}
}
}
@@ -337,9 +336,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
let base = match local_ref {
- LocalRef::Operand(None) => return,
+ LocalRef::PendingOperand => return,
- LocalRef::Operand(Some(operand)) => {
+ LocalRef::Operand(operand) => {
// Don't spill operands onto the stack in naked functions.
// See: https://github.com/rust-lang/rust/issues/42779
let attrs = bx.tcx().codegen_fn_attrs(self.instance.def_id());
@@ -443,11 +442,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let (var_ty, var_kind) = match var.value {
mir::VarDebugInfoContents::Place(place) => {
let var_ty = self.monomorphized_place_ty(place.as_ref());
- let var_kind = if self.mir.local_kind(place.local) == mir::LocalKind::Arg
+ let var_kind = if let Some(arg_index) = var.argument_index
&& place.projection.is_empty()
- && var.source_info.scope == mir::OUTERMOST_SOURCE_SCOPE
{
- let arg_index = place.local.index() - 1;
+ let arg_index = arg_index as usize;
if target_is_msvc {
// ScalarPair parameters are spilled to the stack so they need to
// be marked as a `LocalVariable` for MSVC debuggers to visualize
@@ -456,13 +454,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
if let Abi::ScalarPair(_, _) = var_ty_layout.abi {
VariableKind::LocalVariable
} else {
- VariableKind::ArgumentVariable(arg_index + 1)
+ VariableKind::ArgumentVariable(arg_index)
}
} else {
// FIXME(eddyb) shouldn't `ArgumentVariable` indices be
// offset in closures to account for the hidden environment?
- // Also, is this `+ 1` needed at all?
- VariableKind::ArgumentVariable(arg_index + 1)
+ VariableKind::ArgumentVariable(arg_index)
}
} else {
VariableKind::LocalVariable
diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs
index 2ec9fdbf4..3dadb33c9 100644
--- a/compiler/rustc_codegen_ssa/src/mir/mod.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs
@@ -73,8 +73,8 @@ pub struct FunctionCx<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> {
/// Cached unreachable block
unreachable_block: Option<Bx::BasicBlock>,
- /// Cached double unwind guarding block
- double_unwind_guard: Option<Bx::BasicBlock>,
+ /// Cached terminate upon unwinding block
+ terminate_block: Option<Bx::BasicBlock>,
/// The location where each MIR arg/var/tmp/ret is stored. This is
/// usually an `PlaceRef` representing an alloca, but not always:
@@ -123,7 +123,10 @@ enum LocalRef<'tcx, V> {
/// Every time it is initialized, we have to reallocate the place
/// and update the fat pointer. That's the reason why it is indirect.
UnsizedPlace(PlaceRef<'tcx, V>),
- Operand(Option<OperandRef<'tcx, V>>),
+ /// The backend [`OperandValue`] has already been generated.
+ Operand(OperandRef<'tcx, V>),
+ /// Will be a `Self::Operand` once we get to its definition.
+ PendingOperand,
}
impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> {
@@ -135,9 +138,9 @@ impl<'a, 'tcx, V: CodegenObject> LocalRef<'tcx, V> {
// Zero-size temporaries aren't always initialized, which
// doesn't matter because they don't contain data, but
// we need something in the operand.
- LocalRef::Operand(Some(OperandRef::new_zst(bx, layout)))
+ LocalRef::Operand(OperandRef::new_zst(bx, layout))
} else {
- LocalRef::Operand(None)
+ LocalRef::PendingOperand
}
}
}
@@ -163,7 +166,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
let start_llbb = Bx::append_block(cx, llfn, "start");
let mut start_bx = Bx::build(cx, start_llbb);
- if mir.basic_blocks.iter().any(|bb| bb.is_cleanup) {
+ if mir.basic_blocks.iter().any(|bb| {
+ bb.is_cleanup || matches!(bb.terminator().unwind(), Some(mir::UnwindAction::Terminate))
+ }) {
start_bx.set_personality_fn(cx.eh_personality());
}
@@ -186,7 +191,7 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
personality_slot: None,
cached_llbbs,
unreachable_block: None,
- double_unwind_guard: None,
+ terminate_block: None,
cleanup_kinds,
landing_pads: IndexVec::from_elem(None, &mir.basic_blocks),
funclets: IndexVec::from_fn_n(|_| None, mir.basic_blocks.len()),
@@ -258,6 +263,10 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// Apply debuginfo to the newly allocated locals.
fx.debug_introduce_locals(&mut start_bx);
+ // The builders will be created separately for each basic block at `codegen_block`.
+ // So drop the builder of `start_llbb` to avoid having two at the same time.
+ drop(start_bx);
+
// Codegen the body of each block using reverse postorder
for (bb, _) in traversal::reverse_postorder(&mir) {
fx.codegen_block(bb);
@@ -333,7 +342,7 @@ fn arg_local_refs<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
// We don't have to cast or keep the argument in the alloca.
// FIXME(eddyb): We should figure out how to use llvm.dbg.value instead
// of putting everything in allocas just so we can use llvm.dbg.declare.
- let local = |op| LocalRef::Operand(Some(op));
+ let local = |op| LocalRef::Operand(op);
match arg.mode {
PassMode::Ignore => {
return local(OperandRef::new_zst(bx, arg.layout));
diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs
index 34a5b638d..b37797fef 100644
--- a/compiler/rustc_codegen_ssa/src/mir/operand.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs
@@ -23,10 +23,26 @@ pub enum OperandValue<V> {
/// to be valid for the operand's lifetime.
/// The second value, if any, is the extra data (vtable or length)
/// which indicates that it refers to an unsized rvalue.
+ ///
+ /// An `OperandValue` has this variant for types which are neither
+ /// `Immediate` nor `Pair`s. The backend value in this variant must be a
+ /// pointer to the *non*-immediate backend type. That pointee type is the
+ /// one returned by [`LayoutTypeMethods::backend_type`].
Ref(V, Option<V>, Align),
- /// A single LLVM value.
+ /// A single LLVM immediate value.
+ ///
+ /// An `OperandValue` *must* be this variant for any type for which
+ /// [`LayoutTypeMethods::is_backend_immediate`] returns `true`.
+ /// The backend value in this variant must be the *immediate* backend type,
+ /// as returned by [`LayoutTypeMethods::immediate_backend_type`].
Immediate(V),
/// A pair of immediate LLVM values. Used by fat pointers too.
+ ///
+ /// An `OperandValue` *must* be this variant for any type for which
+ /// [`LayoutTypeMethods::is_backend_scalar_pair`] returns `true`.
+ /// The backend values in this variant must be the *immediate* backend types,
+ /// as returned by [`LayoutTypeMethods::scalar_pair_element_backend_type`]
+ /// with `immediate: true`.
Pair(V, V),
}
@@ -60,7 +76,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
) -> OperandRef<'tcx, V> {
assert!(layout.is_zst());
OperandRef {
- val: OperandValue::Immediate(bx.const_undef(bx.immediate_backend_type(layout))),
+ val: OperandValue::Immediate(bx.const_poison(bx.immediate_backend_type(layout))),
layout,
}
}
@@ -145,7 +161,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
let llty = bx.cx().backend_type(self.layout);
debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", self, llty);
// Reconstruct the immediate aggregate.
- let mut llpair = bx.cx().const_undef(llty);
+ let mut llpair = bx.cx().const_poison(llty);
let imm_a = bx.from_immediate(a);
let imm_b = bx.from_immediate(b);
llpair = bx.insert_value(llpair, imm_a, 0);
@@ -243,6 +259,31 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
}
impl<'a, 'tcx, V: CodegenObject> OperandValue<V> {
+ /// Returns an `OperandValue` that's generally UB to use in any way.
+ ///
+ /// Depending on the `layout`, returns an `Immediate` or `Pair` containing
+ /// poison value(s), or a `Ref` containing a poison pointer.
+ ///
+ /// Supports sized types only.
+ pub fn poison<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
+ bx: &mut Bx,
+ layout: TyAndLayout<'tcx>,
+ ) -> OperandValue<V> {
+ assert!(layout.is_sized());
+ if bx.cx().is_backend_immediate(layout) {
+ let ibty = bx.cx().immediate_backend_type(layout);
+ OperandValue::Immediate(bx.const_poison(ibty))
+ } else if bx.cx().is_backend_scalar_pair(layout) {
+ let ibty0 = bx.cx().scalar_pair_element_backend_type(layout, 0, true);
+ let ibty1 = bx.cx().scalar_pair_element_backend_type(layout, 1, true);
+ OperandValue::Pair(bx.const_poison(ibty0), bx.const_poison(ibty1))
+ } else {
+ let bty = bx.cx().backend_type(layout);
+ let ptr_bty = bx.cx().type_ptr_to(bty);
+ OperandValue::Ref(bx.const_poison(ptr_bty), None, layout.align.abi)
+ }
+ }
+
pub fn store<Bx: BuilderMethods<'a, 'tcx, Value = V>>(
self,
bx: &mut Bx,
@@ -370,7 +411,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
debug!("maybe_codegen_consume_direct(place_ref={:?})", place_ref);
match self.locals[place_ref.local] {
- LocalRef::Operand(Some(mut o)) => {
+ LocalRef::Operand(mut o) => {
// Moves out of scalar and scalar pair fields are trivial.
for elem in place_ref.projection.iter() {
match elem {
@@ -395,7 +436,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
Some(o)
}
- LocalRef::Operand(None) => {
+ LocalRef::PendingOperand => {
bug!("use of {:?} before def", place_ref);
}
LocalRef::Place(..) | LocalRef::UnsizedPlace(..) => {
diff --git a/compiler/rustc_codegen_ssa/src/mir/place.rs b/compiler/rustc_codegen_ssa/src/mir/place.rs
index cf02f59f6..a58a61cd5 100644
--- a/compiler/rustc_codegen_ssa/src/mir/place.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/place.rs
@@ -211,10 +211,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
) -> V {
let dl = &bx.tcx().data_layout;
let cast_to_layout = bx.cx().layout_of(cast_to);
- let cast_to_size = cast_to_layout.layout.size();
let cast_to = bx.cx().immediate_backend_type(cast_to_layout);
if self.layout.abi.is_uninhabited() {
- return bx.cx().const_undef(cast_to);
+ return bx.cx().const_poison(cast_to);
}
let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants {
Variants::Single { index } => {
@@ -261,21 +260,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
_ => (tag_imm, bx.cx().immediate_backend_type(tag_op.layout)),
};
- let tag_size = tag_scalar.size(bx.cx());
- let max_unsigned = tag_size.unsigned_int_max();
- let max_signed = tag_size.signed_int_max() as u128;
- let min_signed = max_signed + 1;
let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
- let niche_end = niche_start.wrapping_add(relative_max as u128) & max_unsigned;
- let range = tag_scalar.valid_range(bx.cx());
-
- let sle = |lhs: u128, rhs: u128| -> bool {
- // Signed and unsigned comparisons give the same results,
- // except that in signed comparisons an integer with the
- // sign bit set is less than one with the sign bit clear.
- // Toggle the sign bit to do a signed comparison.
- (lhs ^ min_signed) <= (rhs ^ min_signed)
- };
// We have a subrange `niche_start..=niche_end` inside `range`.
// If the value of the tag is inside this subrange, it's a
@@ -291,49 +276,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
// untagged_variant
// }
// However, we will likely be able to emit simpler code.
-
- // Find the least and greatest values in `range`, considered
- // both as signed and unsigned.
- let (low_unsigned, high_unsigned) = if range.start <= range.end {
- (range.start, range.end)
- } else {
- (0, max_unsigned)
- };
- let (low_signed, high_signed) = if sle(range.start, range.end) {
- (range.start, range.end)
- } else {
- (min_signed, max_signed)
- };
-
- let niches_ule = niche_start <= niche_end;
- let niches_sle = sle(niche_start, niche_end);
- let cast_smaller = cast_to_size <= tag_size;
-
- // In the algorithm above, we can change
- // cast(relative_tag) + niche_variants.start()
- // into
- // cast(tag + (niche_variants.start() - niche_start))
- // if either the casted type is no larger than the original
- // type, or if the niche values are contiguous (in either the
- // signed or unsigned sense).
- let can_incr = cast_smaller || niches_ule || niches_sle;
-
- let data_for_boundary_niche = || -> Option<(IntPredicate, u128)> {
- if !can_incr {
- None
- } else if niche_start == low_unsigned {
- Some((IntPredicate::IntULE, niche_end))
- } else if niche_end == high_unsigned {
- Some((IntPredicate::IntUGE, niche_start))
- } else if niche_start == low_signed {
- Some((IntPredicate::IntSLE, niche_end))
- } else if niche_end == high_signed {
- Some((IntPredicate::IntSGE, niche_start))
- } else {
- None
- }
- };
-
let (is_niche, tagged_discr, delta) = if relative_max == 0 {
// Best case scenario: only one tagged variant. This will
// likely become just a comparison and a jump.
@@ -349,40 +291,6 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
let tagged_discr =
bx.cx().const_uint(cast_to, niche_variants.start().as_u32() as u64);
(is_niche, tagged_discr, 0)
- } else if let Some((predicate, constant)) = data_for_boundary_niche() {
- // The niche values are either the lowest or the highest in
- // `range`. We can avoid the first subtraction in the
- // algorithm.
- // The algorithm is now this:
- // is_niche = tag <= niche_end
- // discr = if is_niche {
- // cast(tag + (niche_variants.start() - niche_start))
- // } else {
- // untagged_variant
- // }
- // (the first line may instead be tag >= niche_start,
- // and may be a signed or unsigned comparison)
- // The arithmetic must be done before the cast, so we can
- // have the correct wrapping behavior. See issue #104519 for
- // the consequences of getting this wrong.
- let is_niche =
- bx.icmp(predicate, tag, bx.cx().const_uint_big(tag_llty, constant));
- let delta = (niche_variants.start().as_u32() as u128).wrapping_sub(niche_start);
- let incr_tag = if delta == 0 {
- tag
- } else {
- bx.add(tag, bx.cx().const_uint_big(tag_llty, delta))
- };
-
- let cast_tag = if cast_smaller {
- bx.intcast(incr_tag, cast_to, false)
- } else if niches_ule {
- bx.zext(incr_tag, cast_to)
- } else {
- bx.sext(incr_tag, cast_to)
- };
-
- (is_niche, cast_tag, 0)
} else {
// The special cases don't apply, so we'll have to go with
// the general algorithm.
@@ -558,6 +466,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
bug!("using operand local {:?} as place", place_ref);
}
}
+ LocalRef::PendingOperand => {
+ bug!("using still-pending operand local {:?} as place", place_ref);
+ }
};
for elem in place_ref.projection[base..].iter() {
cg_base = match *elem {
diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
index 3d856986f..d88226f5d 100644
--- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs
@@ -10,10 +10,10 @@ use crate::MemFlags;
use rustc_middle::mir;
use rustc_middle::mir::Operand;
use rustc_middle::ty::cast::{CastTy, IntTy};
-use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
+use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf, TyAndLayout};
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
use rustc_span::source_map::{Span, DUMMY_SP};
-use rustc_target::abi::VariantIdx;
+use rustc_target::abi::{self, FIRST_VARIANT};
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
#[instrument(level = "trace", skip(self, bx))]
@@ -72,6 +72,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
+ mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, _ty) => {
+ let src = self.codegen_operand(bx, operand);
+ self.codegen_transmute(bx, src, dest);
+ }
+
mir::Rvalue::Repeat(ref elem, count) => {
let cg_elem = self.codegen_operand(bx, elem);
@@ -113,21 +118,21 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
let variant_dest = dest.project_downcast(bx, variant_index);
(variant_index, variant_dest, active_field_index)
}
- _ => (VariantIdx::from_u32(0), dest, None),
+ _ => (FIRST_VARIANT, dest, None),
};
if active_field_index.is_some() {
assert_eq!(operands.len(), 1);
}
- for (i, operand) in operands.iter().enumerate() {
+ for (i, operand) in operands.iter_enumerated() {
let op = self.codegen_operand(bx, operand);
// Do not generate stores and GEPis for zero-sized fields.
if !op.layout.is_zst() {
let field_index = active_field_index.unwrap_or(i);
let field = if let mir::AggregateKind::Array(_) = **kind {
- let llindex = bx.cx().const_usize(field_index as u64);
+ let llindex = bx.cx().const_usize(field_index.as_u32().into());
variant_dest.project_index(bx, llindex)
} else {
- variant_dest.project_field(bx, field_index)
+ variant_dest.project_field(bx, field_index.as_usize())
};
op.val.store(bx, field);
}
@@ -143,6 +148,156 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
}
}
+ fn codegen_transmute(
+ &mut self,
+ bx: &mut Bx,
+ src: OperandRef<'tcx, Bx::Value>,
+ dst: PlaceRef<'tcx, Bx::Value>,
+ ) {
+ // The MIR validator enforces no unsized transmutes.
+ debug_assert!(src.layout.is_sized());
+ debug_assert!(dst.layout.is_sized());
+
+ if let Some(val) = self.codegen_transmute_operand(bx, src, dst.layout) {
+ val.store(bx, dst);
+ return;
+ }
+
+ match src.val {
+ OperandValue::Ref(..) => {
+ span_bug!(
+ self.mir.span,
+ "Operand path should have handled transmute \
+ from `Ref` {src:?} to place {dst:?}"
+ );
+ }
+ OperandValue::Immediate(..) | OperandValue::Pair(..) => {
+ // When we have immediate(s), the alignment of the source is irrelevant,
+ // so we can store them using the destination's alignment.
+ let llty = bx.backend_type(src.layout);
+ let cast_ptr = bx.pointercast(dst.llval, bx.type_ptr_to(llty));
+ src.val.store(bx, PlaceRef::new_sized_aligned(cast_ptr, src.layout, dst.align));
+ }
+ }
+ }
+
+ /// Attempts to transmute an `OperandValue` to another `OperandValue`.
+ ///
+ /// Returns `None` for cases that can't work in that framework, such as for
+ /// `Immediate`->`Ref` that needs an `alloc` to get the location.
+ fn codegen_transmute_operand(
+ &mut self,
+ bx: &mut Bx,
+ operand: OperandRef<'tcx, Bx::Value>,
+ cast: TyAndLayout<'tcx>,
+ ) -> Option<OperandValue<Bx::Value>> {
+ // Check for transmutes that are always UB.
+ if operand.layout.size != cast.size
+ || operand.layout.abi.is_uninhabited()
+ || cast.abi.is_uninhabited()
+ {
+ if !operand.layout.abi.is_uninhabited() {
+ // Since this is known statically and the input could have existed
+ // without already having hit UB, might as well trap for it.
+ bx.abort();
+ }
+
+ // Because this transmute is UB, return something easy to generate,
+ // since it's fine that later uses of the value are probably UB.
+ return Some(OperandValue::poison(bx, cast));
+ }
+
+ let operand_kind = self.value_kind(operand.layout);
+ let cast_kind = self.value_kind(cast);
+
+ match operand.val {
+ OperandValue::Ref(ptr, meta, align) => {
+ debug_assert_eq!(meta, None);
+ debug_assert!(matches!(operand_kind, OperandValueKind::Ref));
+ let cast_bty = bx.backend_type(cast);
+ let cast_ptr = bx.pointercast(ptr, bx.type_ptr_to(cast_bty));
+ let fake_place = PlaceRef::new_sized_aligned(cast_ptr, cast, align);
+ Some(bx.load_operand(fake_place).val)
+ }
+ OperandValue::Immediate(imm) => {
+ let OperandValueKind::Immediate(in_scalar) = operand_kind else {
+ bug!("Found {operand_kind:?} for operand {operand:?}");
+ };
+ if let OperandValueKind::Immediate(out_scalar) = cast_kind {
+ match (in_scalar, out_scalar) {
+ (ScalarOrZst::Zst, ScalarOrZst::Zst) => {
+ Some(OperandRef::new_zst(bx, cast).val)
+ }
+ (ScalarOrZst::Scalar(in_scalar), ScalarOrZst::Scalar(out_scalar))
+ if in_scalar.size(self.cx) == out_scalar.size(self.cx) =>
+ {
+ let cast_bty = bx.backend_type(cast);
+ Some(OperandValue::Immediate(
+ self.transmute_immediate(bx, imm, in_scalar, out_scalar, cast_bty),
+ ))
+ }
+ _ => None,
+ }
+ } else {
+ None
+ }
+ }
+ OperandValue::Pair(imm_a, imm_b) => {
+ let OperandValueKind::Pair(in_a, in_b) = operand_kind else {
+ bug!("Found {operand_kind:?} for operand {operand:?}");
+ };
+ if let OperandValueKind::Pair(out_a, out_b) = cast_kind
+ && in_a.size(self.cx) == out_a.size(self.cx)
+ && in_b.size(self.cx) == out_b.size(self.cx)
+ {
+ let out_a_ibty = bx.scalar_pair_element_backend_type(cast, 0, false);
+ let out_b_ibty = bx.scalar_pair_element_backend_type(cast, 1, false);
+ Some(OperandValue::Pair(
+ self.transmute_immediate(bx, imm_a, in_a, out_a, out_a_ibty),
+ self.transmute_immediate(bx, imm_b, in_b, out_b, out_b_ibty),
+ ))
+ } else {
+ None
+ }
+ }
+ }
+ }
+
+ /// Transmutes one of the immediates from an [`OperandValue::Immediate`]
+ /// or an [`OperandValue::Pair`] to an immediate of the target type.
+ ///
+ /// `to_backend_ty` must be the *non*-immediate backend type (so it will be
+ /// `i8`, not `i1`, for `bool`-like types.)
+ fn transmute_immediate(
+ &self,
+ bx: &mut Bx,
+ mut imm: Bx::Value,
+ from_scalar: abi::Scalar,
+ to_scalar: abi::Scalar,
+ to_backend_ty: Bx::Type,
+ ) -> Bx::Value {
+ debug_assert_eq!(from_scalar.size(self.cx), to_scalar.size(self.cx));
+
+ use abi::Primitive::*;
+ imm = bx.from_immediate(imm);
+ imm = match (from_scalar.primitive(), to_scalar.primitive()) {
+ (Int(..) | F32 | F64, Int(..) | F32 | F64) => bx.bitcast(imm, to_backend_ty),
+ (Pointer(..), Pointer(..)) => bx.pointercast(imm, to_backend_ty),
+ (Int(..), Pointer(..)) => bx.inttoptr(imm, to_backend_ty),
+ (Pointer(..), Int(..)) => bx.ptrtoint(imm, to_backend_ty),
+ (F32 | F64, Pointer(..)) => {
+ let int_imm = bx.bitcast(imm, bx.cx().type_isize());
+ bx.inttoptr(int_imm, to_backend_ty)
+ }
+ (Pointer(..), F32 | F64) => {
+ let int_imm = bx.ptrtoint(imm, bx.cx().type_isize());
+ bx.bitcast(int_imm, to_backend_ty)
+ }
+ };
+ imm = bx.to_immediate_scalar(imm, to_scalar);
+ imm
+ }
+
pub fn codegen_rvalue_unsized(
&mut self,
bx: &mut Bx,
@@ -295,7 +450,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
assert!(bx.cx().is_backend_immediate(cast));
let ll_t_out = bx.cx().immediate_backend_type(cast);
if operand.layout.abi.is_uninhabited() {
- let val = OperandValue::Immediate(bx.cx().const_undef(ll_t_out));
+ let val = OperandValue::Immediate(bx.cx().const_poison(ll_t_out));
return OperandRef { val, layout: cast };
}
let r_t_in =
@@ -344,6 +499,11 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
};
OperandValue::Immediate(newval)
}
+ mir::CastKind::Transmute => {
+ self.codegen_transmute_operand(bx, operand, cast).unwrap_or_else(|| {
+ bug!("Unsupported transmute-as-operand of {operand:?} to {cast:?}");
+ })
+ }
};
OperandRef { val, layout: cast }
}
@@ -462,8 +622,20 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
mir::Rvalue::ThreadLocalRef(def_id) => {
assert!(bx.cx().tcx().is_static(def_id));
- let static_ = bx.get_static(def_id);
let layout = bx.layout_of(bx.cx().tcx().static_ptr_ty(def_id));
+ let static_ = if !def_id.is_local() && bx.cx().tcx().needs_thread_local_shim(def_id)
+ {
+ let instance = ty::Instance {
+ def: ty::InstanceDef::ThreadLocalShim(def_id),
+ substs: ty::InternalSubsts::empty(),
+ };
+ let fn_ptr = bx.get_fn_addr(instance);
+ let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
+ let fn_ty = bx.fn_decl_backend_type(&fn_abi);
+ bx.call(fn_ty, Some(fn_abi), fn_ptr, &[], None)
+ } else {
+ bx.get_static(def_id)
+ };
OperandRef { val: OperandValue::Immediate(static_), layout }
}
mir::Rvalue::Use(ref operand) => self.codegen_operand(bx, operand),
@@ -491,7 +663,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// ZST are passed as operands and require special handling
// because codegen_place() panics if Local is operand.
if let Some(index) = place.as_local() {
- if let LocalRef::Operand(Some(op)) = self.locals[index] {
+ if let LocalRef::Operand(op) = self.locals[index] {
if let ty::Array(_, n) = op.layout.ty.kind() {
let n = n.eval_target_usize(bx.cx().tcx(), ty::ParamEnv::reveal_all());
return bx.cx().const_usize(n);
@@ -663,17 +835,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
};
bx.checked_binop(oop, input_ty, lhs, rhs)
}
- mir::BinOp::Shl | mir::BinOp::Shr => {
- let lhs_llty = bx.cx().val_ty(lhs);
- let rhs_llty = bx.cx().val_ty(rhs);
- let invert_mask = common::shift_mask_val(bx, lhs_llty, rhs_llty, true);
- let outer_bits = bx.and(rhs, invert_mask);
-
- let of = bx.icmp(IntPredicate::IntNE, outer_bits, bx.cx().const_null(rhs_llty));
- let val = self.codegen_scalar_binop(bx, op, lhs, rhs, input_ty);
-
- (val, of)
- }
_ => bug!("Operator `{:?}` is not a checkable operator", op),
};
@@ -684,6 +845,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>, span: Span) -> bool {
match *rvalue {
+ mir::Rvalue::Cast(mir::CastKind::Transmute, ref operand, cast_ty) => {
+ let operand_ty = operand.ty(self.mir, self.cx.tcx());
+ let cast_layout = self.cx.layout_of(self.monomorphize(cast_ty));
+ let operand_layout = self.cx.layout_of(self.monomorphize(operand_ty));
+
+ match (self.value_kind(operand_layout), self.value_kind(cast_layout)) {
+ // Can always load from a pointer as needed
+ (OperandValueKind::Ref, _) => true,
+
+ // Need to generate an `alloc` to get a pointer from an immediate
+ (OperandValueKind::Immediate(..) | OperandValueKind::Pair(..), OperandValueKind::Ref) => false,
+
+ // When we have scalar immediates, we can only convert things
+ // where the sizes match, to avoid endianness questions.
+ (OperandValueKind::Immediate(a), OperandValueKind::Immediate(b)) =>
+ a.size(self.cx) == b.size(self.cx),
+ (OperandValueKind::Pair(a0, a1), OperandValueKind::Pair(b0, b1)) =>
+ a0.size(self.cx) == b0.size(self.cx) && a1.size(self.cx) == b1.size(self.cx),
+
+ // Send mixings between scalars and pairs through the memory route
+ // FIXME: Maybe this could use insertvalue/extractvalue instead?
+ (OperandValueKind::Immediate(..), OperandValueKind::Pair(..)) |
+ (OperandValueKind::Pair(..), OperandValueKind::Immediate(..)) => false,
+ }
+ }
mir::Rvalue::Ref(..) |
mir::Rvalue::CopyForDeref(..) |
mir::Rvalue::AddressOf(..) |
@@ -708,4 +894,52 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
// (*) this is only true if the type is suitable
}
+
+ /// Gets which variant of [`OperandValue`] is expected for a particular type.
+ fn value_kind(&self, layout: TyAndLayout<'tcx>) -> OperandValueKind {
+ if self.cx.is_backend_immediate(layout) {
+ debug_assert!(!self.cx.is_backend_scalar_pair(layout));
+ OperandValueKind::Immediate(match layout.abi {
+ abi::Abi::Scalar(s) => ScalarOrZst::Scalar(s),
+ abi::Abi::Vector { element, .. } => ScalarOrZst::Scalar(element),
+ _ if layout.is_zst() => ScalarOrZst::Zst,
+ x => span_bug!(self.mir.span, "Couldn't translate {x:?} as backend immediate"),
+ })
+ } else if self.cx.is_backend_scalar_pair(layout) {
+ let abi::Abi::ScalarPair(s1, s2) = layout.abi else {
+ span_bug!(
+ self.mir.span,
+ "Couldn't translate {:?} as backend scalar pair",
+ layout.abi,
+ );
+ };
+ OperandValueKind::Pair(s1, s2)
+ } else {
+ OperandValueKind::Ref
+ }
+ }
+}
+
+/// The variants of this match [`OperandValue`], giving details about the
+/// backend values that will be held in that other type.
+#[derive(Debug, Copy, Clone)]
+enum OperandValueKind {
+ Ref,
+ Immediate(ScalarOrZst),
+ Pair(abi::Scalar, abi::Scalar),
+}
+
+#[derive(Debug, Copy, Clone)]
+enum ScalarOrZst {
+ Zst,
+ Scalar(abi::Scalar),
+}
+
+impl ScalarOrZst {
+ pub fn size(self, cx: &impl abi::HasDataLayout) -> abi::Size {
+ match self {
+ ScalarOrZst::Zst => abi::Size::ZERO,
+ ScalarOrZst::Scalar(s) => s.size(cx),
+ }
+ }
}
diff --git a/compiler/rustc_codegen_ssa/src/mir/statement.rs b/compiler/rustc_codegen_ssa/src/mir/statement.rs
index 60fbceb34..3fd7397ad 100644
--- a/compiler/rustc_codegen_ssa/src/mir/statement.rs
+++ b/compiler/rustc_codegen_ssa/src/mir/statement.rs
@@ -18,12 +18,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
LocalRef::UnsizedPlace(cg_indirect_dest) => {
self.codegen_rvalue_unsized(bx, cg_indirect_dest, rvalue)
}
- LocalRef::Operand(None) => {
+ LocalRef::PendingOperand => {
let operand = self.codegen_rvalue_operand(bx, rvalue);
- self.locals[index] = LocalRef::Operand(Some(operand));
+ self.locals[index] = LocalRef::Operand(operand);
self.debug_introduce_local(bx, index);
}
- LocalRef::Operand(Some(op)) => {
+ LocalRef::Operand(op) => {
if !op.layout.is_zst() {
span_bug!(
statement.source_info.span,
@@ -92,6 +92,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
| mir::StatementKind::Retag { .. }
| mir::StatementKind::AscribeUserType(..)
| mir::StatementKind::ConstEvalCounter
+ | mir::StatementKind::PlaceMention(..)
| mir::StatementKind::Nop => {}
}
}