Auto merge of #88439 - cynecx:unwind_asm, r=Amanieu

Unwinding support for inline assembly

r? `@Amanieu`
This commit is contained in:
bors 2021-12-04 05:59:16 +00:00
commit 887999d163
55 changed files with 623 additions and 232 deletions

View File

@ -1981,7 +1981,7 @@ pub enum InlineAsmRegOrRegClass {
bitflags::bitflags! {
#[derive(Encodable, Decodable, HashStable_Generic)]
pub struct InlineAsmOptions: u8 {
pub struct InlineAsmOptions: u16 {
const PURE = 1 << 0;
const NOMEM = 1 << 1;
const READONLY = 1 << 2;
@ -1990,6 +1990,7 @@ bitflags::bitflags! {
const NOSTACK = 1 << 5;
const ATT_SYNTAX = 1 << 6;
const RAW = 1 << 7;
const MAY_UNWIND = 1 << 8;
}
}

View File

@ -49,6 +49,17 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
.struct_span_err(sp, "the `att_syntax` option is only supported on x86")
.emit();
}
if asm.options.contains(InlineAsmOptions::MAY_UNWIND)
&& !self.sess.features_untracked().asm_unwind
{
feature_err(
&self.sess.parse_sess,
sym::asm_unwind,
sp,
"the `may_unwind` option is unstable",
)
.emit();
}
let mut clobber_abis = FxHashMap::default();
if let Some(asm_arch) = asm_arch {

View File

@ -2338,6 +2338,9 @@ impl<'a> State<'a> {
if opts.contains(InlineAsmOptions::RAW) {
options.push("raw");
}
if opts.contains(InlineAsmOptions::MAY_UNWIND) {
options.push("may_unwind");
}
s.commasep(Inconsistent, &options, |s, &opt| {
s.word(opt);
});

View File

@ -5,7 +5,7 @@ use rustc_middle::ty::RegionVid;
use rustc_middle::ty::TyCtxt;
use rustc_mir_dataflow::impls::{EverInitializedPlaces, MaybeUninitializedPlaces};
use rustc_mir_dataflow::ResultsVisitable;
use rustc_mir_dataflow::{self, fmt::DebugWithContext, GenKill};
use rustc_mir_dataflow::{self, fmt::DebugWithContext, CallReturnPlaces, GenKill};
use rustc_mir_dataflow::{Analysis, Direction, Results};
use std::fmt;
use std::iter;
@ -434,9 +434,7 @@ impl<'tcx> rustc_mir_dataflow::GenKillAnalysis<'tcx> for Borrows<'_, 'tcx> {
&self,
_trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
_dest_place: mir::Place<'tcx>,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
}
}

View File

@ -17,7 +17,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
PlaceContext::MutatingUse(MutatingUseContext::Store) |
// This is potentially both a def and a use...
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
PlaceContext::MutatingUse(MutatingUseContext::LlvmAsmOutput) |
// We let Call define the result in both the success and
// unwind cases. This is not really correct, however it
@ -26,6 +26,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
// the def in call only to the input from the success
// path and not the unwind path. -nmatsakis
PlaceContext::MutatingUse(MutatingUseContext::Call) |
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
PlaceContext::MutatingUse(MutatingUseContext::Yield) |
// Storage live and storage dead aren't proper defines, but we can ignore

View File

@ -199,6 +199,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
options: _,
line_spans: _,
destination: _,
cleanup: _,
} => {
for op in operands {
match *op {

View File

@ -791,6 +791,7 @@ impl<'cx, 'tcx> rustc_mir_dataflow::ResultsVisitor<'cx, 'tcx> for MirBorrowckCtx
options: _,
line_spans: _,
destination: _,
cleanup: _,
} => {
for op in operands {
match *op {

View File

@ -1828,10 +1828,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.assert_iscleanup(body, block_data, unwind, true);
}
}
TerminatorKind::InlineAsm { destination, .. } => {
TerminatorKind::InlineAsm { destination, cleanup, .. } => {
if let Some(target) = destination {
self.assert_iscleanup(body, block_data, target, is_cleanup);
}
if let Some(cleanup) = cleanup {
if is_cleanup {
span_mirbug!(self, block_data, "cleanup on cleanup block")
}
self.assert_iscleanup(body, block_data, cleanup, true);
}
}
}
}

View File

@ -420,6 +420,8 @@ fn parse_options<'a>(
try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX);
} else if p.eat_keyword(kw::Raw) {
try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::RAW);
} else if p.eat_keyword(sym::may_unwind) {
try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::MAY_UNWIND);
} else {
return p.unexpected();
}

View File

@ -1,6 +1,7 @@
//! Codegen of a single function
use cranelift_codegen::binemit::{NullStackMapSink, NullTrapSink};
use rustc_ast::InlineAsmOptions;
use rustc_index::vec::IndexVec;
use rustc_middle::ty::adjustment::PointerCast;
use rustc_middle::ty::layout::FnAbiOf;
@ -239,7 +240,8 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
fx.add_comment(inst, terminator_head);
}
fx.set_debug_loc(bb_data.terminator().source_info);
let source_info = bb_data.terminator().source_info;
fx.set_debug_loc(source_info);
match &bb_data.terminator().kind {
TerminatorKind::Goto { target } => {
@ -295,19 +297,19 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
let len = codegen_operand(fx, len).load_scalar(fx);
let index = codegen_operand(fx, index).load_scalar(fx);
let location = fx
.get_caller_location(bb_data.terminator().source_info.span)
.get_caller_location(source_info.span)
.load_scalar(fx);
codegen_panic_inner(
fx,
rustc_hir::LangItem::PanicBoundsCheck,
&[index, len, location],
bb_data.terminator().source_info.span,
source_info.span,
);
}
_ => {
let msg_str = msg.description();
codegen_panic(fx, msg_str, bb_data.terminator().source_info.span);
codegen_panic(fx, msg_str, source_info.span);
}
}
}
@ -378,10 +380,18 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
options,
destination,
line_spans: _,
cleanup: _,
} => {
if options.contains(InlineAsmOptions::MAY_UNWIND) {
fx.tcx.sess.span_fatal(
source_info.span,
"cranelift doesn't support unwinding from inline assembly.",
);
}
crate::inline_asm::codegen_inline_asm(
fx,
bb_data.terminator().source_info.span,
source_info.span,
template,
operands,
*options,
@ -415,7 +425,7 @@ fn codegen_fn_content(fx: &mut FunctionCx<'_, '_, '_>) {
}
TerminatorKind::Drop { place, target, unwind: _ } => {
let drop_place = codegen_place(fx, *place);
crate::abi::codegen_drop(fx, bb_data.terminator().source_info.span, drop_place);
crate::abi::codegen_drop(fx, source_info.span, drop_place);
let target_block = fx.get_block(*target);
fx.bcx.ins().jump(target_block, &[]);

View File

@ -118,7 +118,14 @@ impl<'a, 'gcc, 'tcx> AsmBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tcx> {
true
}
fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, _span: &[Span], _instance: Instance<'_>) {
fn codegen_inline_asm(&mut self, template: &[InlineAsmTemplatePiece], rust_operands: &[InlineAsmOperandRef<'tcx, Self>], options: InlineAsmOptions, span: &[Span], _instance: Instance<'_>, _dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>) {
if options.contains(InlineAsmOptions::MAY_UNWIND) {
self.sess()
.struct_span_err(span[0], "GCC backend does not support unwinding from inline asm")
.emit();
return;
}
let asm_arch = self.tcx.sess.asm_arch.unwrap();
let is_x86 = matches!(asm_arch, InlineAsmArch::X86 | InlineAsmArch::X86_64);
let att_dialect = is_x86 && options.contains(InlineAsmOptions::ATT_SYNTAX);

View File

@ -1,6 +1,8 @@
use crate::builder::Builder;
use crate::common::Funclet;
use crate::context::CodegenCx;
use crate::llvm;
use crate::llvm_util;
use crate::type_::Type;
use crate::type_of::LayoutLlvmExt;
use crate::value::Value;
@ -98,6 +100,8 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
ia.alignstack,
ia.dialect,
&[span],
false,
None,
);
if r.is_none() {
return false;
@ -121,6 +125,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
options: InlineAsmOptions,
line_spans: &[Span],
instance: Instance<'_>,
dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>,
) {
let asm_arch = self.tcx.sess.asm_arch.unwrap();
@ -355,6 +360,8 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> {
alignstack,
dialect,
line_spans,
options.contains(InlineAsmOptions::MAY_UNWIND),
dest_catch_funclet,
)
.unwrap_or_else(|| span_bug!(line_spans[0], "LLVM asm constraint validation failed"));
@ -447,9 +454,16 @@ pub(crate) fn inline_asm_call(
alignstack: bool,
dia: LlvmAsmDialect,
line_spans: &[Span],
unwind: bool,
dest_catch_funclet: Option<(
&'ll llvm::BasicBlock,
&'ll llvm::BasicBlock,
Option<&Funclet<'ll>>,
)>,
) -> Option<&'ll Value> {
let volatile = if volatile { llvm::True } else { llvm::False };
let alignstack = if alignstack { llvm::True } else { llvm::False };
let can_throw = if unwind { llvm::True } else { llvm::False };
let argtys = inputs
.iter()
@ -466,6 +480,13 @@ pub(crate) fn inline_asm_call(
let constraints_ok = llvm::LLVMRustInlineAsmVerify(fty, cons.as_ptr().cast(), cons.len());
debug!("constraint verification result: {:?}", constraints_ok);
if constraints_ok {
if unwind && llvm_util::get_version() < (13, 0, 0) {
bx.cx.sess().span_fatal(
line_spans[0],
"unwinding from inline assembly is only supported on llvm >= 13.",
);
}
let v = llvm::LLVMRustInlineAsm(
fty,
asm.as_ptr().cast(),
@ -475,8 +496,14 @@ pub(crate) fn inline_asm_call(
volatile,
alignstack,
llvm::AsmDialect::from_generic(dia),
can_throw,
);
let call = bx.call(fty, v, inputs, None);
let call = if let Some((dest, catch, funclet)) = dest_catch_funclet {
bx.invoke(fty, v, inputs, dest, catch, funclet)
} else {
bx.call(fty, v, inputs, None)
};
// Store mark in a metadata node so we can map LLVM errors
// back to source locations. See #17552.

View File

@ -350,6 +350,8 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> {
false,
ast::LlvmAsmDialect::Att,
&[span],
false,
None,
)
.unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`"));

View File

@ -1847,6 +1847,7 @@ extern "C" {
SideEffects: Bool,
AlignStack: Bool,
Dialect: AsmDialect,
CanThrow: Bool,
) -> &Value;
pub fn LLVMRustInlineAsmVerify(
Ty: &Type,

View File

@ -211,6 +211,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
PlaceContext::MutatingUse(
MutatingUseContext::Store
| MutatingUseContext::LlvmAsmOutput
| MutatingUseContext::AsmOutput
| MutatingUseContext::Borrow
| MutatingUseContext::AddressOf
@ -275,9 +276,9 @@ pub fn cleanup_kinds(mir: &mir::Body<'_>) -> IndexVec<mir::BasicBlock, CleanupKi
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::Yield { .. }
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::InlineAsm { .. } => { /* nothing to do */ }
| TerminatorKind::FalseUnwind { .. } => { /* nothing to do */ }
TerminatorKind::Call { cleanup: unwind, .. }
| TerminatorKind::InlineAsm { cleanup: unwind, .. }
| TerminatorKind::Assert { cleanup: unwind, .. }
| TerminatorKind::DropAndReplace { unwind, .. }
| TerminatorKind::Drop { unwind, .. } => {

View File

@ -10,6 +10,7 @@ use crate::traits::*;
use crate::MemFlags;
use rustc_ast as ast;
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
use rustc_hir::lang_items::LangItem;
use rustc_index::vec::Idx;
use rustc_middle::mir::AssertKind;
@ -174,6 +175,45 @@ impl<'a, 'tcx> TerminatorCodegenHelper<'tcx> {
}
}
}
/// Generates inline assembly with optional `destination` and `cleanup`.
fn do_inlineasm<Bx: BuilderMethods<'a, 'tcx>>(
&self,
fx: &mut FunctionCx<'a, 'tcx, Bx>,
bx: &mut Bx,
template: &[InlineAsmTemplatePiece],
operands: &[InlineAsmOperandRef<'tcx, Bx>],
options: InlineAsmOptions,
line_spans: &[Span],
destination: Option<mir::BasicBlock>,
cleanup: Option<mir::BasicBlock>,
instance: Instance<'_>,
) {
if let Some(cleanup) = cleanup {
let ret_llbb = if let Some(target) = destination {
fx.llbb(target)
} else {
fx.unreachable_block()
};
bx.codegen_inline_asm(
template,
&operands,
options,
line_spans,
instance,
Some((ret_llbb, self.llblock(fx, cleanup), self.funclet(fx))),
);
} else {
bx.codegen_inline_asm(template, &operands, options, line_spans, instance, None);
if let Some(target) = destination {
self.funclet_br(fx, bx, target);
} else {
bx.unreachable();
}
}
}
}
/// Codegen implementations for some terminator variants.
@ -877,6 +917,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>,
instance: Instance<'_>,
) {
let span = terminator.source_info.span;
@ -931,13 +972,17 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
})
.collect();
bx.codegen_inline_asm(template, &operands, options, line_spans, instance);
if let Some(target) = destination {
helper.funclet_br(self, &mut bx, target);
} else {
bx.unreachable();
}
helper.do_inlineasm(
self,
&mut bx,
template,
&operands,
options,
line_spans,
destination,
cleanup,
instance,
);
}
}
@ -1041,6 +1086,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
options,
line_spans,
destination,
cleanup,
} => {
self.codegen_asm_terminator(
helper,
@ -1051,6 +1097,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
options,
line_spans,
destination,
cleanup,
self.instance,
);
}

View File

@ -59,6 +59,7 @@ pub trait AsmBuilderMethods<'tcx>: BackendTypes {
options: InlineAsmOptions,
line_spans: &[Span],
instance: Instance<'_>,
dest_catch_funclet: Option<(Self::BasicBlock, Self::BasicBlock, Option<&Self::Funclet>)>,
);
}

View File

@ -7,6 +7,7 @@ use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{self, BasicBlock, Local, Location, Statement, StatementKind};
use rustc_mir_dataflow::fmt::DebugWithContext;
use rustc_mir_dataflow::JoinSemiLattice;
use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces};
use rustc_span::DUMMY_SP;
use std::fmt;
@ -80,18 +81,18 @@ where
fn apply_call_return_effect(
&mut self,
_block: BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
// We cannot reason about another function's internals, so use conservative type-based
// qualification for the result of a function call.
let return_ty = return_place.ty(self.ccx.body, self.ccx.tcx).ty;
let qualif = Q::in_any_value_of_ty(self.ccx, return_ty);
return_places.for_each(|place| {
// We cannot reason about another function's internals, so use conservative type-based
// qualification for the result of a function call.
let return_ty = place.ty(self.ccx.body, self.ccx.tcx).ty;
let qualif = Q::in_any_value_of_ty(self.ccx, return_ty);
if !return_place.is_indirect() {
self.assign_qualif_direct(&return_place, qualif);
}
if !place.is_indirect() {
self.assign_qualif_direct(&place, qualif);
}
});
}
fn address_of_allows_mutation(&self, _mt: mir::Mutability, _place: mir::Place<'tcx>) -> bool {
@ -329,7 +330,7 @@ impl JoinSemiLattice for State {
}
}
impl<Q> rustc_mir_dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
impl<Q> AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
where
Q: Qualif,
{
@ -349,7 +350,7 @@ where
}
}
impl<Q> rustc_mir_dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
impl<Q> Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q>
where
Q: Qualif,
{
@ -375,10 +376,8 @@ where
&self,
state: &mut Self::Domain,
block: BasicBlock,
func: &mir::Operand<'tcx>,
args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
self.transfer_function(state).apply_call_return_effect(block, func, args, return_place)
self.transfer_function(state).apply_call_return_effect(block, return_places)
}
}

View File

@ -495,10 +495,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
self.check_edge(location, *unwind, EdgeKind::Unwind);
}
}
TerminatorKind::InlineAsm { destination, .. } => {
TerminatorKind::InlineAsm { destination, cleanup, .. } => {
if let Some(destination) = destination {
self.check_edge(location, *destination, EdgeKind::Normal);
}
if let Some(cleanup) = cleanup {
self.check_edge(location, *cleanup, EdgeKind::Unwind);
}
}
// Nothing to validate for these.
TerminatorKind::Resume

View File

@ -290,6 +290,8 @@ declare_features! (
(active, asm_experimental_arch, "1.58.0", Some(72016), None),
/// Allows using `sym` operands in inline assembly.
(active, asm_sym, "1.58.0", Some(72016), None),
/// Allows the `may_unwind` option in inline assembly.
(active, asm_unwind, "1.58.0", Some(72016), None),
/// Allows the user of associated type bounds.
(active, associated_type_bounds, "1.34.0", Some(52662), None),
/// Allows associated type defaults.

View File

@ -1433,6 +1433,9 @@ impl<'a> State<'a> {
if opts.contains(ast::InlineAsmOptions::RAW) {
options.push("raw");
}
if opts.contains(ast::InlineAsmOptions::MAY_UNWIND) {
options.push("may_unwind");
}
s.commasep(Inconsistent, &options, |s, &opt| {
s.word(opt);
});

View File

@ -445,11 +445,20 @@ extern "C" LLVMValueRef
LLVMRustInlineAsm(LLVMTypeRef Ty, char *AsmString, size_t AsmStringLen,
char *Constraints, size_t ConstraintsLen,
LLVMBool HasSideEffects, LLVMBool IsAlignStack,
LLVMRustAsmDialect Dialect) {
LLVMRustAsmDialect Dialect, LLVMBool CanThrow) {
#if LLVM_VERSION_GE(13, 0)
return wrap(InlineAsm::get(unwrap<FunctionType>(Ty),
StringRef(AsmString, AsmStringLen),
StringRef(Constraints, ConstraintsLen),
HasSideEffects, IsAlignStack, fromRust(Dialect)));
HasSideEffects, IsAlignStack,
fromRust(Dialect), CanThrow));
#else
return wrap(InlineAsm::get(unwrap<FunctionType>(Ty),
StringRef(AsmString, AsmStringLen),
StringRef(Constraints, ConstraintsLen),
HasSideEffects, IsAlignStack,
fromRust(Dialect)));
#endif
}
extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints,

View File

@ -260,6 +260,10 @@ pub enum TerminatorKind<'tcx> {
/// Destination block after the inline assembly returns, unless it is
/// diverging (InlineAsmOptions::NORETURN).
destination: Option<BasicBlock>,
/// Cleanup to be done if the inline assembly unwinds. This is present
/// if and only if InlineAsmOptions::MAY_UNWIND is set.
cleanup: Option<BasicBlock>,
},
}
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
@ -309,7 +313,7 @@ impl<'tcx> TerminatorKind<'tcx> {
| Return
| Unreachable
| Call { destination: None, cleanup: None, .. }
| InlineAsm { destination: None, .. } => None.into_iter().chain(&[]),
| InlineAsm { destination: None, cleanup: None, .. } => None.into_iter().chain(&[]),
Goto { target: ref t }
| Call { destination: None, cleanup: Some(ref t), .. }
| Call { destination: Some((_, ref t)), cleanup: None, .. }
@ -318,13 +322,17 @@ impl<'tcx> TerminatorKind<'tcx> {
| Drop { target: ref t, unwind: None, .. }
| Assert { target: ref t, cleanup: None, .. }
| FalseUnwind { real_target: ref t, unwind: None }
| InlineAsm { destination: Some(ref t), .. } => Some(t).into_iter().chain(&[]),
| InlineAsm { destination: Some(ref t), cleanup: None, .. }
| InlineAsm { destination: None, cleanup: Some(ref t), .. } => {
Some(t).into_iter().chain(&[])
}
Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. }
| Yield { resume: ref t, drop: Some(ref u), .. }
| DropAndReplace { target: ref t, unwind: Some(ref u), .. }
| Drop { target: ref t, unwind: Some(ref u), .. }
| Assert { target: ref t, cleanup: Some(ref u), .. }
| FalseUnwind { real_target: ref t, unwind: Some(ref u) } => {
| FalseUnwind { real_target: ref t, unwind: Some(ref u) }
| InlineAsm { destination: Some(ref t), cleanup: Some(ref u), .. } => {
Some(t).into_iter().chain(slice::from_ref(u))
}
SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets[..]),
@ -343,7 +351,7 @@ impl<'tcx> TerminatorKind<'tcx> {
| Return
| Unreachable
| Call { destination: None, cleanup: None, .. }
| InlineAsm { destination: None, .. } => None.into_iter().chain(&mut []),
| InlineAsm { destination: None, cleanup: None, .. } => None.into_iter().chain(&mut []),
Goto { target: ref mut t }
| Call { destination: None, cleanup: Some(ref mut t), .. }
| Call { destination: Some((_, ref mut t)), cleanup: None, .. }
@ -352,13 +360,17 @@ impl<'tcx> TerminatorKind<'tcx> {
| Drop { target: ref mut t, unwind: None, .. }
| Assert { target: ref mut t, cleanup: None, .. }
| FalseUnwind { real_target: ref mut t, unwind: None }
| InlineAsm { destination: Some(ref mut t), .. } => Some(t).into_iter().chain(&mut []),
| InlineAsm { destination: Some(ref mut t), cleanup: None, .. }
| InlineAsm { destination: None, cleanup: Some(ref mut t), .. } => {
Some(t).into_iter().chain(&mut [])
}
Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. }
| Yield { resume: ref mut t, drop: Some(ref mut u), .. }
| DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. }
| Drop { target: ref mut t, unwind: Some(ref mut u), .. }
| Assert { target: ref mut t, cleanup: Some(ref mut u), .. }
| FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => {
| FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) }
| InlineAsm { destination: Some(ref mut t), cleanup: Some(ref mut u), .. } => {
Some(t).into_iter().chain(slice::from_mut(u))
}
SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets[..]),
@ -378,13 +390,13 @@ impl<'tcx> TerminatorKind<'tcx> {
| TerminatorKind::GeneratorDrop
| TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::InlineAsm { .. } => None,
| TerminatorKind::FalseEdge { .. } => None,
TerminatorKind::Call { cleanup: ref unwind, .. }
| TerminatorKind::Assert { cleanup: ref unwind, .. }
| TerminatorKind::DropAndReplace { ref unwind, .. }
| TerminatorKind::Drop { ref unwind, .. }
| TerminatorKind::FalseUnwind { ref unwind, .. } => Some(unwind),
| TerminatorKind::FalseUnwind { ref unwind, .. }
| TerminatorKind::InlineAsm { cleanup: ref unwind, .. } => Some(unwind),
}
}
@ -398,13 +410,13 @@ impl<'tcx> TerminatorKind<'tcx> {
| TerminatorKind::GeneratorDrop
| TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. }
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::InlineAsm { .. } => None,
| TerminatorKind::FalseEdge { .. } => None,
TerminatorKind::Call { cleanup: ref mut unwind, .. }
| TerminatorKind::Assert { cleanup: ref mut unwind, .. }
| TerminatorKind::DropAndReplace { ref mut unwind, .. }
| TerminatorKind::Drop { ref mut unwind, .. }
| TerminatorKind::FalseUnwind { ref mut unwind, .. } => Some(unwind),
| TerminatorKind::FalseUnwind { ref mut unwind, .. }
| TerminatorKind::InlineAsm { cleanup: ref mut unwind, .. } => Some(unwind),
}
}
@ -583,8 +595,12 @@ impl<'tcx> TerminatorKind<'tcx> {
FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()],
FalseUnwind { unwind: None, .. } => vec!["real".into()],
InlineAsm { destination: Some(_), .. } => vec!["".into()],
InlineAsm { destination: None, .. } => vec![],
InlineAsm { destination: Some(_), cleanup: Some(_), .. } => {
vec!["return".into(), "unwind".into()]
}
InlineAsm { destination: Some(_), cleanup: None, .. } => vec!["return".into()],
InlineAsm { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()],
InlineAsm { destination: None, cleanup: None, .. } => vec![],
}
}
}

View File

@ -84,13 +84,16 @@ impl<'tcx> TypeFoldable<'tcx> for Terminator<'tcx> {
FalseEdge { real_target, imaginary_target }
}
FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
InlineAsm { template, operands, options, line_spans, destination } => InlineAsm {
template,
operands: operands.try_fold_with(folder)?,
options,
line_spans,
destination,
},
InlineAsm { template, operands, options, line_spans, destination, cleanup } => {
InlineAsm {
template,
operands: operands.try_fold_with(folder)?,
options,
line_spans,
destination,
cleanup,
}
}
};
Ok(Terminator { source_info: self.source_info, kind })
}

View File

@ -412,7 +412,7 @@ macro_rules! make_mir_visitor {
for output in & $($mutability)? asm.outputs[..] {
self.visit_place(
output,
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput),
PlaceContext::MutatingUse(MutatingUseContext::LlvmAsmOutput),
location
);
}
@ -581,6 +581,7 @@ macro_rules! make_mir_visitor {
options: _,
line_spans: _,
destination: _,
cleanup: _,
} => {
for op in operands {
match op {
@ -590,7 +591,7 @@ macro_rules! make_mir_visitor {
InlineAsmOperand::Out { place: Some(place), .. } => {
self.visit_place(
place,
PlaceContext::MutatingUse(MutatingUseContext::Store),
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput),
location,
);
}
@ -599,7 +600,7 @@ macro_rules! make_mir_visitor {
if let Some(out_place) = out_place {
self.visit_place(
out_place,
PlaceContext::MutatingUse(MutatingUseContext::Store),
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput),
location,
);
}
@ -1178,8 +1179,10 @@ pub enum MutatingUseContext {
/// Appears as LHS of an assignment.
Store,
/// Can often be treated as a `Store`, but needs to be separate because
/// ASM is allowed to read outputs as well, so a `Store`-`AsmOutput` sequence
/// ASM is allowed to read outputs as well, so a `Store`-`LlvmAsmOutput` sequence
/// cannot be simplified the way a `Store`-`Store` can be.
LlvmAsmOutput,
/// Output operand of an inline assembly block.
AsmOutput,
/// Destination of a call.
Call,
@ -1268,6 +1271,7 @@ impl PlaceContext {
PlaceContext::MutatingUse(
MutatingUseContext::Store
| MutatingUseContext::Call
| MutatingUseContext::LlvmAsmOutput
| MutatingUseContext::AsmOutput,
)
)

View File

@ -467,8 +467,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} else {
Some(destination_block)
},
cleanup: None,
},
);
if options.contains(InlineAsmOptions::MAY_UNWIND) {
this.diverge_from(block);
}
destination_block.unit()
}

View File

@ -1034,6 +1034,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| TerminatorKind::Call { .. }
| TerminatorKind::DropAndReplace { .. }
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::InlineAsm { .. }
),
"diverge_from called on block with terminator that cannot unwind."
);
@ -1373,7 +1374,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
| TerminatorKind::DropAndReplace { unwind, .. }
| TerminatorKind::FalseUnwind { unwind, .. }
| TerminatorKind::Call { cleanup: unwind, .. }
| TerminatorKind::Assert { cleanup: unwind, .. } => {
| TerminatorKind::Assert { cleanup: unwind, .. }
| TerminatorKind::InlineAsm { cleanup: unwind, .. } => {
*unwind = Some(to);
}
TerminatorKind::Goto { .. }
@ -1384,8 +1386,7 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
| TerminatorKind::Unreachable
| TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdge { .. }
| TerminatorKind::InlineAsm { .. } => {
| TerminatorKind::FalseEdge { .. } => {
span_bug!(term.source_info.span, "cannot unwind from {:?}", term.kind)
}
}

View File

@ -4,7 +4,9 @@ use rustc_middle::ty::TyCtxt;
use std::ops::RangeInclusive;
use super::visitor::{ResultsVisitable, ResultsVisitor};
use super::{Analysis, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget};
use super::{
Analysis, CallReturnPlaces, Effect, EffectIndex, GenKillAnalysis, GenKillSet, SwitchIntTarget,
};
pub trait Direction {
fn is_forward() -> bool;
@ -235,14 +237,26 @@ impl Direction for Backward {
// Apply terminator-specific edge effects.
//
// FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
mir::TerminatorKind::Call {
destination: Some((return_place, dest)),
ref func,
ref args,
..
mir::TerminatorKind::Call { destination: Some((return_place, dest)), .. }
if dest == bb =>
{
let mut tmp = exit_state.clone();
analysis.apply_call_return_effect(
&mut tmp,
pred,
CallReturnPlaces::Call(return_place),
);
propagate(pred, &tmp);
}
mir::TerminatorKind::InlineAsm {
destination: Some(dest), ref operands, ..
} if dest == bb => {
let mut tmp = exit_state.clone();
analysis.apply_call_return_effect(&mut tmp, pred, func, args, return_place);
analysis.apply_call_return_effect(
&mut tmp,
pred,
CallReturnPlaces::InlineAsm(operands),
);
propagate(pred, &tmp);
}
@ -258,6 +272,7 @@ impl Direction for Backward {
| mir::TerminatorKind::Drop { unwind: Some(unwind), .. }
| mir::TerminatorKind::DropAndReplace { unwind: Some(unwind), .. }
| mir::TerminatorKind::FalseUnwind { unwind: Some(unwind), .. }
| mir::TerminatorKind::InlineAsm { cleanup: Some(unwind), .. }
if unwind == bb =>
{
if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
@ -467,7 +482,7 @@ impl Direction for Forward {
propagate(target, exit_state);
}
Call { cleanup, destination, ref func, ref args, from_hir_call: _, fn_span: _ } => {
Call { cleanup, destination, func: _, args: _, from_hir_call: _, fn_span: _ } => {
if let Some(unwind) = cleanup {
if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
propagate(unwind, exit_state);
@ -477,13 +492,37 @@ impl Direction for Forward {
if let Some((dest_place, target)) = destination {
// N.B.: This must be done *last*, otherwise the unwind path will see the call
// return effect.
analysis.apply_call_return_effect(exit_state, bb, func, args, dest_place);
analysis.apply_call_return_effect(
exit_state,
bb,
CallReturnPlaces::Call(dest_place),
);
propagate(target, exit_state);
}
}
InlineAsm { template: _, operands: _, options: _, line_spans: _, destination } => {
InlineAsm {
template: _,
ref operands,
options: _,
line_spans: _,
destination,
cleanup,
} => {
if let Some(unwind) = cleanup {
if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
propagate(unwind, exit_state);
}
}
if let Some(target) = destination {
// N.B.: This must be done *last*, otherwise the unwind path will see the call
// return effect.
analysis.apply_call_return_effect(
exit_state,
bb,
CallReturnPlaces::InlineAsm(operands),
);
propagate(target, exit_state);
}
}

View File

@ -10,7 +10,7 @@ use rustc_middle::mir::graphviz_safe_def_name;
use rustc_middle::mir::{self, BasicBlock, Body, Location};
use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext};
use super::{Analysis, Direction, Results, ResultsRefCursor, ResultsVisitor};
use super::{Analysis, CallReturnPlaces, Direction, Results, ResultsRefCursor, ResultsVisitor};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OutputStyle {
@ -231,16 +231,15 @@ where
// for the basic block itself. That way, we could display terminator-specific effects for
// backward dataflow analyses as well as effects for `SwitchInt` terminators.
match terminator.kind {
mir::TerminatorKind::Call {
destination: Some((return_place, _)),
ref func,
ref args,
..
} => {
mir::TerminatorKind::Call { destination: Some((return_place, _)), .. } => {
self.write_row(w, "", "(on successful return)", |this, w, fmt| {
let state_on_unwind = this.results.get().clone();
this.results.apply_custom_effect(|analysis, state| {
analysis.apply_call_return_effect(state, block, func, args, return_place);
analysis.apply_call_return_effect(
state,
block,
CallReturnPlaces::Call(return_place),
);
});
write!(
@ -278,6 +277,31 @@ where
})?;
}
mir::TerminatorKind::InlineAsm { destination: Some(_), ref operands, .. } => {
self.write_row(w, "", "(on successful return)", |this, w, fmt| {
let state_on_unwind = this.results.get().clone();
this.results.apply_custom_effect(|analysis, state| {
analysis.apply_call_return_effect(
state,
block,
CallReturnPlaces::InlineAsm(operands),
);
});
write!(
w,
r#"<td balign="left" colspan="{colspan}" {fmt} align="left">{diff}</td>"#,
colspan = this.style.num_state_columns(),
fmt = fmt,
diff = diff_pretty(
this.results.get(),
&state_on_unwind,
this.results.analysis()
),
)
})?;
}
_ => {}
};

View File

@ -160,9 +160,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
&self,
state: &mut Self::Domain,
block: BasicBlock,
func: &mir::Operand<'tcx>,
args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
);
/// Updates the current dataflow state with the effect of resuming from a `Yield` terminator.
@ -276,9 +274,7 @@ pub trait GenKillAnalysis<'tcx>: Analysis<'tcx> {
&self,
trans: &mut impl GenKill<Self::Idx>,
block: BasicBlock,
func: &mir::Operand<'tcx>,
args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
);
/// See `Analysis::apply_yield_resume_effect`.
@ -347,11 +343,9 @@ where
&self,
state: &mut A::Domain,
block: BasicBlock,
func: &mir::Operand<'tcx>,
args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
self.call_return_effect(state, block, func, args, return_place);
self.call_return_effect(state, block, return_places);
}
fn apply_yield_resume_effect(
@ -542,5 +536,29 @@ pub trait SwitchIntEdgeEffects<D> {
fn apply(&mut self, apply_edge_effect: impl FnMut(&mut D, SwitchIntTarget));
}
/// List of places that are written to after a successful (non-unwind) return
/// from a `Call` or `InlineAsm`.
pub enum CallReturnPlaces<'a, 'tcx> {
Call(mir::Place<'tcx>),
InlineAsm(&'a [mir::InlineAsmOperand<'tcx>]),
}
impl<'tcx> CallReturnPlaces<'_, 'tcx> {
pub fn for_each(&self, mut f: impl FnMut(mir::Place<'tcx>)) {
match *self {
Self::Call(place) => f(place),
Self::InlineAsm(operands) => {
for op in operands {
match *op {
mir::InlineAsmOperand::Out { place: Some(place), .. }
| mir::InlineAsmOperand::InOut { out_place: Some(place), .. } => f(place),
_ => {}
}
}
}
}
}
}
#[cfg(test)]
mod tests;

View File

@ -220,9 +220,7 @@ impl<D: Direction> Analysis<'tcx> for MockAnalysis<'tcx, D> {
&self,
_state: &mut Self::Domain,
_block: BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
_return_place: mir::Place<'tcx>,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
}
}

View File

@ -1,6 +1,6 @@
use super::*;
use crate::{AnalysisDomain, GenKill, GenKillAnalysis};
use crate::{AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis};
use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::*;
@ -84,9 +84,7 @@ impl GenKillAnalysis<'tcx> for MaybeBorrowedLocals {
&self,
_trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
_dest_place: mir::Place<'tcx>,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
}
}

View File

@ -2,7 +2,7 @@
//!
//! A local will be maybe initialized if *any* projections of that local might be initialized.
use crate::GenKill;
use crate::{CallReturnPlaces, GenKill};
use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::{PlaceContext, Visitor};
@ -53,11 +53,9 @@ impl crate::GenKillAnalysis<'tcx> for MaybeInitializedLocals {
&self,
trans: &mut impl GenKill<Self::Idx>,
_block: BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
trans.gen(return_place.local)
return_places.for_each(|place| trans.gen(place.local));
}
/// See `Analysis::apply_yield_resume_effect`.
@ -83,7 +81,11 @@ where
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, NonUseContext};
match context {
// These are handled specially in `call_return_effect` and `yield_resume_effect`.
PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => {}
PlaceContext::MutatingUse(
MutatingUseContext::Call
| MutatingUseContext::AsmOutput
| MutatingUseContext::Yield,
) => {}
// Otherwise, when a place is mutated, we must consider it possibly initialized.
PlaceContext::MutatingUse(_) => self.trans.gen(local),

View File

@ -2,7 +2,7 @@ use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, Local, Location};
use crate::{AnalysisDomain, Backward, GenKill, GenKillAnalysis};
use crate::{AnalysisDomain, Backward, CallReturnPlaces, GenKill, GenKillAnalysis};
/// A [live-variable dataflow analysis][liveness].
///
@ -94,13 +94,13 @@ impl GenKillAnalysis<'tcx> for MaybeLiveLocals {
&self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
dest_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
if let Some(local) = dest_place.as_local() {
trans.kill(local);
}
return_places.for_each(|place| {
if let Some(local) = place.as_local() {
trans.kill(local);
}
});
}
fn yield_resume_effect(
@ -167,12 +167,16 @@ impl DefUse {
// destination place for a `Call` return or `Yield` resume respectively. Since this is
// only a `Def` when the function returns successfully, we handle this case separately
// in `call_return_effect` above.
PlaceContext::MutatingUse(MutatingUseContext::Call | MutatingUseContext::Yield) => None,
PlaceContext::MutatingUse(
MutatingUseContext::Call
| MutatingUseContext::AsmOutput
| MutatingUseContext::Yield,
) => None,
// All other contexts are uses...
PlaceContext::MutatingUse(
MutatingUseContext::AddressOf
| MutatingUseContext::AsmOutput
| MutatingUseContext::LlvmAsmOutput
| MutatingUseContext::Borrow
| MutatingUseContext::Drop
| MutatingUseContext::Retag,

View File

@ -11,7 +11,7 @@ use rustc_middle::ty::{self, TyCtxt};
use crate::drop_flag_effects_for_function_entry;
use crate::drop_flag_effects_for_location;
use crate::elaborate_drops::DropFlagState;
use crate::framework::SwitchIntEdgeEffects;
use crate::framework::{CallReturnPlaces, SwitchIntEdgeEffects};
use crate::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
use crate::on_lookup_result_bits;
use crate::MoveDataParamEnv;
@ -354,21 +354,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
&self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
dest_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
// when a call returns successfully, that means we need to set
// the bits for that dest_place to 1 (initialized).
on_lookup_result_bits(
self.tcx,
self.body,
self.move_data(),
self.move_data().rev_lookup.find(dest_place.as_ref()),
|mpi| {
trans.gen(mpi);
},
);
return_places.for_each(|place| {
// when a call returns successfully, that means we need to set
// the bits for that dest_place to 1 (initialized).
on_lookup_result_bits(
self.tcx,
self.body,
self.move_data(),
self.move_data().rev_lookup.find(place.as_ref()),
|mpi| {
trans.gen(mpi);
},
);
});
}
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
@ -472,21 +472,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
&self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
dest_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
// when a call returns successfully, that means we need to set
// the bits for that dest_place to 0 (initialized).
on_lookup_result_bits(
self.tcx,
self.body,
self.move_data(),
self.move_data().rev_lookup.find(dest_place.as_ref()),
|mpi| {
trans.kill(mpi);
},
);
return_places.for_each(|place| {
// when a call returns successfully, that means we need to set
// the bits for that dest_place to 0 (initialized).
on_lookup_result_bits(
self.tcx,
self.body,
self.move_data(),
self.move_data().rev_lookup.find(place.as_ref()),
|mpi| {
trans.kill(mpi);
},
);
});
}
fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
@ -591,21 +591,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
&self,
trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
dest_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
// when a call returns successfully, that means we need to set
// the bits for that dest_place to 1 (initialized).
on_lookup_result_bits(
self.tcx,
self.body,
self.move_data(),
self.move_data().rev_lookup.find(dest_place.as_ref()),
|mpi| {
trans.gen(mpi);
},
);
return_places.for_each(|place| {
// when a call returns successfully, that means we need to set
// the bits for that dest_place to 1 (initialized).
on_lookup_result_bits(
self.tcx,
self.body,
self.move_data(),
self.move_data().rev_lookup.find(place.as_ref()),
|mpi| {
trans.gen(mpi);
},
);
});
}
}
@ -679,9 +679,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
&self,
trans: &mut impl GenKill<Self::Idx>,
block: mir::BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
_dest_place: mir::Place<'tcx>,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
let move_data = self.move_data();
let init_loc_map = &move_data.init_loc_map;

View File

@ -1,7 +1,7 @@
pub use super::*;
use crate::storage::AlwaysLiveLocals;
use crate::{GenKill, Results, ResultsRefCursor};
use crate::{CallReturnPlaces, GenKill, Results, ResultsRefCursor};
use rustc_middle::mir::visit::{NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use std::cell::RefCell;
@ -68,9 +68,7 @@ impl crate::GenKillAnalysis<'tcx> for MaybeStorageLive {
&self,
_trans: &mut impl GenKill<Self::Idx>,
_block: BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
_return_place: mir::Place<'tcx>,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
// Nothing to do when a call returns successfully
}
@ -226,7 +224,7 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
terminator: &mir::Terminator<'tcx>,
loc: Location,
) {
match &terminator.kind {
match terminator.kind {
// For call terminators the destination requires storage for the call
// and after the call returns successfully, but not after a panic.
// Since `propagate_call_unwind` doesn't exist, we have to kill the
@ -235,6 +233,11 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
trans.kill(place.local);
}
// The same applies to InlineAsm outputs.
TerminatorKind::InlineAsm { ref operands, .. } => {
CallReturnPlaces::InlineAsm(operands).for_each(|place| trans.kill(place.local));
}
// Nothing to do for these. Match exhaustively so this fails to compile when new
// variants are added.
TerminatorKind::Call { destination: None, .. }
@ -247,7 +250,6 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::GeneratorDrop
| TerminatorKind::Goto { .. }
| TerminatorKind::InlineAsm { .. }
| TerminatorKind::Resume
| TerminatorKind::Return
| TerminatorKind::SwitchInt { .. }
@ -261,11 +263,9 @@ impl<'mir, 'tcx> crate::GenKillAnalysis<'tcx> for MaybeRequiresStorage<'mir, 'tc
&self,
trans: &mut impl GenKill<Self::Idx>,
_block: BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
return_places: CallReturnPlaces<'_, 'tcx>,
) {
trans.gen(return_place.local);
return_places.for_each(|place| trans.gen(place.local));
}
fn yield_resume_effect(

View File

@ -28,9 +28,9 @@ pub use self::drop_flag_effects::{
on_lookup_result_bits,
};
pub use self::framework::{
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, Direction, Engine,
Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor, ResultsRefCursor,
ResultsVisitable, ResultsVisitor,
fmt, graphviz, lattice, visit_results, Analysis, AnalysisDomain, Backward, CallReturnPlaces,
Direction, Engine, Forward, GenKill, GenKillAnalysis, JoinSemiLattice, Results, ResultsCursor,
ResultsRefCursor, ResultsVisitable, ResultsVisitor,
};
use self::move_paths::MoveData;

View File

@ -419,6 +419,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
options: _,
line_spans: _,
destination: _,
cleanup: _,
} => {
for op in operands {
match *op {

View File

@ -208,6 +208,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> {
MutatingUseContext::Store
| MutatingUseContext::Drop
| MutatingUseContext::AsmOutput
| MutatingUseContext::LlvmAsmOutput
)
);
// If this is just an assignment, determine if the assigned type needs dropping.

View File

@ -1022,6 +1022,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
// These are just stores, where the storing is not propagatable, but there may be later
// mutations of the same local via `Store`
| MutatingUse(MutatingUseContext::Call)
| MutatingUse(MutatingUseContext::AsmOutput)
// Actual store that can possibly even propagate a value
| MutatingUse(MutatingUseContext::Store) => {
if !self.found_assignment.insert(local) {
@ -1052,7 +1053,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
// These could be propagated with a smarter analysis or just some careful thinking about
// whether they'd be fine right now.
MutatingUse(MutatingUseContext::AsmOutput)
MutatingUse(MutatingUseContext::LlvmAsmOutput)
| MutatingUse(MutatingUseContext::Yield)
| MutatingUse(MutatingUseContext::Drop)
| MutatingUse(MutatingUseContext::Retag)

View File

@ -624,6 +624,7 @@ impl Conflicts<'a> {
options: _,
line_spans: _,
destination: _,
cleanup: _,
} => {
// The intended semantics here aren't documented, we just assume that nothing that
// could be written to by the assembly may overlap with any other operands.

View File

@ -441,6 +441,13 @@ impl Inliner<'tcx> {
}
}
TerminatorKind::Resume => cost += RESUME_PENALTY,
TerminatorKind::InlineAsm { cleanup, .. } => {
cost += INSTR_COST;
if cleanup.is_some() {
cost += LANDINGPAD_PENALTY;
}
}
_ => cost += INSTR_COST,
}
@ -954,9 +961,13 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
{
bug!("False unwinds should have been removed before inlining")
}
TerminatorKind::InlineAsm { ref mut destination, .. } => {
TerminatorKind::InlineAsm { ref mut destination, ref mut cleanup, .. } => {
if let Some(ref mut tgt) = *destination {
*tgt = self.map_block(*tgt);
} else if !self.in_cleanup_block {
// Unless this inline asm is in a cleanup block, add an unwind edge to
// the original call's cleanup block
*cleanup = self.cleanup_block;
}
}
}

View File

@ -332,6 +332,7 @@ symbols! {
asm_const,
asm_experimental_arch,
asm_sym,
asm_unwind,
assert,
assert_inhabited,
assert_macro,
@ -817,6 +818,7 @@ symbols! {
maxnumf32,
maxnumf64,
may_dangle,
may_unwind,
maybe_uninit,
maybe_uninit_uninit,
maybe_uninit_zeroed,

View File

@ -454,7 +454,7 @@ operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
reg_operand := dir_spec "(" reg_spec ")" operand_expr
operand := reg_operand / "const" const_expr / "sym" path
clobber_abi := "clobber_abi(" <abi> *["," <abi>] [","] ")"
option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw"
option := "pure" / "nomem" / "readonly" / "preserves_flags" / "noreturn" / "nostack" / "att_syntax" / "raw" / "may_unwind"
options := "options(" option *["," option] [","] ")"
asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) *("," clobber_abi) *("," options) [","] ")"
```
@ -829,6 +829,7 @@ Currently the following options are defined:
- `noreturn`: The `asm` block never returns, and its return type is defined as `!` (never). Behavior is undefined if execution falls through past the end of the asm code. A `noreturn` asm block behaves just like a function which doesn't return; notably, local variables in scope are not dropped before it is invoked.
- `nostack`: The `asm` block does not push data to the stack, or write to the stack red-zone (if supported by the target). If this option is *not* used then the stack pointer is guaranteed to be suitably aligned (according to the target ABI) for a function call.
- `att_syntax`: This option is only valid on x86, and causes the assembler to use the `.att_syntax prefix` mode of the GNU assembler. Register operands are substituted in with a leading `%`.
- `may_unwind`: The `asm` block may unwind the stack and be part of the stack unwinding process (This option is only supported by the LLVM backend right now).
- `raw`: This causes the template string to be parsed as a raw assembly string, with no special handling for `{` and `}`. This is primarily useful when including raw assembly code from an external file using `include_str!`.
The compiler performs some additional checks on options:

View File

@ -0,0 +1,25 @@
// min-llvm-version: 13.0.0
// compile-flags: -O
// only-x86_64
#![crate_type = "rlib"]
#![feature(asm, asm_unwind)]
#[no_mangle]
pub extern "C" fn panicky() {}
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
println!();
}
}
// CHECK-LABEL: @may_unwind
#[no_mangle]
pub unsafe fn may_unwind() {
let _m = Foo;
// CHECK: invoke void asm sideeffect alignstack inteldialect unwind ""
asm!("", options(may_unwind));
}

View File

@ -36,41 +36,41 @@ LL | asm!("{}", out(reg) foo, clobber_abi("C"));
| |
| generic outputs
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
--> $DIR/bad-options.rs:28:25
|
LL | global_asm!("", options(nomem));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `readonly`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `readonly`
--> $DIR/bad-options.rs:30:25
|
LL | global_asm!("", options(readonly));
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `noreturn`
--> $DIR/bad-options.rs:32:25
|
LL | global_asm!("", options(noreturn));
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `pure`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `pure`
--> $DIR/bad-options.rs:34:25
|
LL | global_asm!("", options(pure));
| ^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nostack`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nostack`
--> $DIR/bad-options.rs:36:25
|
LL | global_asm!("", options(nostack));
| ^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `preserves_flags`
--> $DIR/bad-options.rs:38:25
|
LL | global_asm!("", options(preserves_flags));
| ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: invalid ABI for `clobber_abi`
--> $DIR/bad-options.rs:20:18

View File

@ -0,0 +1,37 @@
// min-llvm-version: 13.0.0
// only-aarch64
// run-pass
// needs-asm-support
#![feature(asm, asm_sym, asm_unwind)]
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
struct Foo<'a>(&'a mut bool);
impl Drop for Foo<'_> {
fn drop(&mut self) {
*self.0 = false;
}
}
extern "C" fn panicky() {
resume_unwind(Box::new(()));
}
fn main() {
let flag = &mut true;
catch_unwind(AssertUnwindSafe(|| {
let _foo = Foo(flag);
unsafe {
asm!(
"bl {}",
sym panicky,
clobber_abi("C"),
options(may_unwind)
);
}
}))
.expect_err("expected a panic");
assert_eq!(*flag, false);
}

View File

@ -64,11 +64,11 @@ error: argument to `sym` must be a path expression
LL | asm!("{}", sym foo + bar);
| ^^^^^^^^^
error: expected one of `)`, `att_syntax`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
--> $DIR/parse-error.rs:31:26
|
LL | asm!("", options(foo));
| ^^^ expected one of 9 possible tokens
| ^^^ expected one of 10 possible tokens
error: expected one of `)` or `,`, found `foo`
--> $DIR/parse-error.rs:33:32
@ -76,11 +76,11 @@ error: expected one of `)` or `,`, found `foo`
LL | asm!("", options(nomem foo));
| ^^^ expected one of `)` or `,`
error: expected one of `)`, `att_syntax`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
--> $DIR/parse-error.rs:35:33
|
LL | asm!("", options(nomem, foo));
| ^^^ expected one of 9 possible tokens
| ^^^ expected one of 10 possible tokens
error: arguments are not allowed after options
--> $DIR/parse-error.rs:37:31
@ -260,23 +260,23 @@ error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
LL | global_asm!("{}", const(reg) FOO);
| ^^^ expected one of `,`, `.`, `?`, or an operator
error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `FOO`
--> $DIR/parse-error.rs:100:25
|
LL | global_asm!("", options(FOO));
| ^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
--> $DIR/parse-error.rs:102:25
|
LL | global_asm!("", options(nomem FOO));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
--> $DIR/parse-error.rs:104:25
|
LL | global_asm!("", options(nomem, FOO));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: arguments are not allowed after options
--> $DIR/parse-error.rs:106:30

View File

@ -0,0 +1,9 @@
// min-llvm-version: 13.0.0
// run-pass
// needs-asm-support
#![feature(asm, asm_unwind)]
fn main() {
unsafe { asm!("", options(may_unwind)) };
}

View File

@ -45,41 +45,41 @@ LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C"));
| | clobber_abi
| generic outputs
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
--> $DIR/bad-options.rs:31:25
|
LL | global_asm!("", options(nomem));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `readonly`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `readonly`
--> $DIR/bad-options.rs:33:25
|
LL | global_asm!("", options(readonly));
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `noreturn`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `noreturn`
--> $DIR/bad-options.rs:35:25
|
LL | global_asm!("", options(noreturn));
| ^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `pure`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `pure`
--> $DIR/bad-options.rs:37:25
|
LL | global_asm!("", options(pure));
| ^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nostack`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nostack`
--> $DIR/bad-options.rs:39:25
|
LL | global_asm!("", options(nostack));
| ^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `preserves_flags`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `preserves_flags`
--> $DIR/bad-options.rs:41:25
|
LL | global_asm!("", options(preserves_flags));
| ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^^^^^^^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: invalid ABI for `clobber_abi`
--> $DIR/bad-options.rs:20:18

View File

@ -0,0 +1,37 @@
// min-llvm-version: 13.0.0
// only-x86_64
// run-pass
// needs-asm-support
#![feature(asm, asm_sym, asm_unwind)]
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
struct Foo<'a>(&'a mut bool);
impl Drop for Foo<'_> {
fn drop(&mut self) {
*self.0 = false;
}
}
extern "C" fn panicky() {
resume_unwind(Box::new(()));
}
fn main() {
let flag = &mut true;
catch_unwind(AssertUnwindSafe(|| {
let _foo = Foo(flag);
unsafe {
asm!(
"call {}",
sym panicky,
clobber_abi("C"),
options(may_unwind)
);
}
}))
.expect_err("expected a panic");
assert_eq!(*flag, false);
}

View File

@ -64,11 +64,11 @@ error: argument to `sym` must be a path expression
LL | asm!("{}", sym foo + bar);
| ^^^^^^^^^
error: expected one of `)`, `att_syntax`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
--> $DIR/parse-error.rs:31:26
|
LL | asm!("", options(foo));
| ^^^ expected one of 9 possible tokens
| ^^^ expected one of 10 possible tokens
error: expected one of `)` or `,`, found `foo`
--> $DIR/parse-error.rs:33:32
@ -76,11 +76,11 @@ error: expected one of `)` or `,`, found `foo`
LL | asm!("", options(nomem foo));
| ^^^ expected one of `)` or `,`
error: expected one of `)`, `att_syntax`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
error: expected one of `)`, `att_syntax`, `may_unwind`, `nomem`, `noreturn`, `nostack`, `preserves_flags`, `pure`, `raw`, or `readonly`, found `foo`
--> $DIR/parse-error.rs:35:33
|
LL | asm!("", options(nomem, foo));
| ^^^ expected one of 9 possible tokens
| ^^^ expected one of 10 possible tokens
error: arguments are not allowed after options
--> $DIR/parse-error.rs:37:31
@ -266,23 +266,23 @@ error: expected one of `,`, `.`, `?`, or an operator, found `FOO`
LL | global_asm!("{}", const(reg) FOO);
| ^^^ expected one of `,`, `.`, `?`, or an operator
error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `FOO`
--> $DIR/parse-error.rs:102:25
|
LL | global_asm!("", options(FOO));
| ^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
--> $DIR/parse-error.rs:104:25
|
LL | global_asm!("", options(nomem FOO));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: expected one of `)`, `att_syntax`, or `raw`, found `nomem`
error: expected one of `)`, `att_syntax`, `may_unwind`, or `raw`, found `nomem`
--> $DIR/parse-error.rs:106:25
|
LL | global_asm!("", options(nomem, FOO));
| ^^^^^ expected one of `)`, `att_syntax`, or `raw`
| ^^^^^ expected one of `)`, `att_syntax`, `may_unwind`, or `raw`
error: arguments are not allowed after options
--> $DIR/parse-error.rs:108:30

View File

@ -0,0 +1,10 @@
// only-x86_64
#![feature(asm)]
fn main() {
unsafe {
asm!("", options(may_unwind));
//~^ ERROR the `may_unwind` option is unstable
}
}

View File

@ -0,0 +1,12 @@
error[E0658]: the `may_unwind` option is unstable
--> $DIR/feature-gate-asm_unwind.rs:7:9
|
LL | asm!("", options(may_unwind));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #72016 <https://github.com/rust-lang/rust/issues/72016> for more information
= help: add `#![feature(asm_unwind)]` to the crate attributes to enable
error: aborting due to previous error
For more information about this error, try `rustc --explain E0658`.

View File

@ -15,7 +15,7 @@ use rustc_middle::mir::{
Mutability,
};
use rustc_middle::ty::{self, fold::TypeVisitor, Ty, TyCtxt};
use rustc_mir_dataflow::{Analysis, AnalysisDomain, GenKill, GenKillAnalysis, ResultsCursor};
use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces, GenKill, GenKillAnalysis, ResultsCursor};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{BytePos, Span};
use rustc_span::sym;
@ -499,11 +499,9 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeStorageLive {
fn call_return_effect(
&self,
_in_out: &mut impl GenKill<Self::Idx>,
_trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock,
_func: &mir::Operand<'tcx>,
_args: &[mir::Operand<'tcx>],
_return_place: mir::Place<'tcx>,
_return_places: CallReturnPlaces<'_, 'tcx>,
) {
// Nothing to do when a call returns successfully
}