diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 4d4c6217ad07a..03b957adeda9d 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -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; } } diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index cfa97ff84ec49..9f27ace25ab4b 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -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 { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index f1f2387866d0d..0dc1f093947e3 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -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); }); diff --git a/compiler/rustc_borrowck/src/dataflow.rs b/compiler/rustc_borrowck/src/dataflow.rs index 7db8d4520d406..15372ec1534fe 100644 --- a/compiler/rustc_borrowck/src/dataflow.rs +++ b/compiler/rustc_borrowck/src/dataflow.rs @@ -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, _block: mir::BasicBlock, - _func: &mir::Operand<'tcx>, - _args: &[mir::Operand<'tcx>], - _dest_place: mir::Place<'tcx>, + _return_places: CallReturnPlaces<'_, 'tcx>, ) { } } diff --git a/compiler/rustc_borrowck/src/def_use.rs b/compiler/rustc_borrowck/src/def_use.rs index 689ec249a2fb4..70acbc9ee2dbc 100644 --- a/compiler/rustc_borrowck/src/def_use.rs +++ b/compiler/rustc_borrowck/src/def_use.rs @@ -17,7 +17,7 @@ pub fn categorize(context: PlaceContext) -> Option { 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 { // 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 diff --git a/compiler/rustc_borrowck/src/invalidation.rs b/compiler/rustc_borrowck/src/invalidation.rs index efd34f4e0a58e..c03e4d8a44890 100644 --- a/compiler/rustc_borrowck/src/invalidation.rs +++ b/compiler/rustc_borrowck/src/invalidation.rs @@ -199,6 +199,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> { options: _, line_spans: _, destination: _, + cleanup: _, } => { for op in operands { match *op { diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index ceaae9d66cd53..88fab269109f9 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -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 { diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index da26d9c7b8779..6a263bd63ada8 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -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); + } } } } diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index 41662f46f1152..b374769cbea25 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -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(); } diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index 1b30edd293862..371c71de62fbb 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -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, &[]); diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 7c3ed3c5ee9db..6a3b94a0d7018 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -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); diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 02096f4abfa5f..83c5cb6f1cf51 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -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. diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index a7e34b080594b..5e7d7552daf10 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -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`")); diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index 2ef4c871825cb..d9a6723fe27fd 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1847,6 +1847,7 @@ extern "C" { SideEffects: Bool, AlignStack: Bool, Dialect: AsmDialect, + CanThrow: Bool, ) -> &Value; pub fn LLVMRustInlineAsmVerify( Ty: &Type, diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index 8d75b2e7a3d4c..0447c02fdecc4 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -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 { /* 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, .. } => { diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index c8f388bfa1d5a..e914e49326932 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -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>( + &self, + fx: &mut FunctionCx<'a, 'tcx, Bx>, + bx: &mut Bx, + template: &[InlineAsmTemplatePiece], + operands: &[InlineAsmOperandRef<'tcx, Bx>], + options: InlineAsmOptions, + line_spans: &[Span], + destination: Option, + cleanup: Option, + 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, + cleanup: Option, 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, ); } diff --git a/compiler/rustc_codegen_ssa/src/traits/asm.rs b/compiler/rustc_codegen_ssa/src/traits/asm.rs index 31f539e1b03db..65f3c754d2dcc 100644 --- a/compiler/rustc_codegen_ssa/src/traits/asm.rs +++ b/compiler/rustc_codegen_ssa/src/traits/asm.rs @@ -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>)>, ); } diff --git a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs index b70b38754c956..249b4ea0602c1 100644 --- a/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs +++ b/compiler/rustc_const_eval/src/transform/check_consts/resolver.rs @@ -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 rustc_mir_dataflow::AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> +impl AnalysisDomain<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> where Q: Qualif, { @@ -349,7 +350,7 @@ where } } -impl rustc_mir_dataflow::Analysis<'tcx> for FlowSensitiveAnalysis<'_, '_, 'tcx, Q> +impl 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) } } diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index b4bb6390db486..448a04f76b10a 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -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 diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 7860f92f96f96..640c4bba6dafb 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -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. diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 9c2927111a66c..18bc5bb64d6de 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -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); }); diff --git a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp index 94d2a4b8e4c9b..3fbf020c552d7 100644 --- a/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp @@ -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(Ty), + StringRef(AsmString, AsmStringLen), + StringRef(Constraints, ConstraintsLen), + HasSideEffects, IsAlignStack, + fromRust(Dialect), CanThrow)); +#else return wrap(InlineAsm::get(unwrap(Ty), StringRef(AsmString, AsmStringLen), StringRef(Constraints, ConstraintsLen), - HasSideEffects, IsAlignStack, fromRust(Dialect))); + HasSideEffects, IsAlignStack, + fromRust(Dialect))); +#endif } extern "C" bool LLVMRustInlineAsmVerify(LLVMTypeRef Ty, char *Constraints, diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index e78b6fd092de2..a82f98d28e723 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -260,6 +260,10 @@ pub enum TerminatorKind<'tcx> { /// Destination block after the inline assembly returns, unless it is /// diverging (InlineAsmOptions::NORETURN). destination: Option, + + /// Cleanup to be done if the inline assembly unwinds. This is present + /// if and only if InlineAsmOptions::MAY_UNWIND is set. + cleanup: Option, }, } #[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![], } } } diff --git a/compiler/rustc_middle/src/mir/type_foldable.rs b/compiler/rustc_middle/src/mir/type_foldable.rs index ad8b9d323eed5..901f3bf4f7d41 100644 --- a/compiler/rustc_middle/src/mir/type_foldable.rs +++ b/compiler/rustc_middle/src/mir/type_foldable.rs @@ -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 }) } diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 4c23ab49fa29f..d783b6330e8e5 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -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, ) ) diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index 53868f2855763..abec67af08bff 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -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() } diff --git a/compiler/rustc_mir_build/src/build/scope.rs b/compiler/rustc_mir_build/src/build/scope.rs index 8dadbf5f02bd1..fc46c54c2fc35 100644 --- a/compiler/rustc_mir_build/src/build/scope.rs +++ b/compiler/rustc_mir_build/src/build/scope.rs @@ -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) } } diff --git a/compiler/rustc_mir_dataflow/src/framework/direction.rs b/compiler/rustc_mir_dataflow/src/framework/direction.rs index 8a9ced91eb376..6131ee7981829 100644 --- a/compiler/rustc_mir_dataflow/src/framework/direction.rs +++ b/compiler/rustc_mir_dataflow/src/framework/direction.rs @@ -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); } } diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index a370f8e40f9ae..517bc086ef683 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -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#"{diff}"#, + colspan = this.style.num_state_columns(), + fmt = fmt, + diff = diff_pretty( + this.results.get(), + &state_on_unwind, + this.results.analysis() + ), + ) + })?; + } + _ => {} }; diff --git a/compiler/rustc_mir_dataflow/src/framework/mod.rs b/compiler/rustc_mir_dataflow/src/framework/mod.rs index f0c9ac4c504a3..500fba8b1142c 100644 --- a/compiler/rustc_mir_dataflow/src/framework/mod.rs +++ b/compiler/rustc_mir_dataflow/src/framework/mod.rs @@ -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, 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 { 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; diff --git a/compiler/rustc_mir_dataflow/src/framework/tests.rs b/compiler/rustc_mir_dataflow/src/framework/tests.rs index 6efa8daec489a..01ca8ca9258fb 100644 --- a/compiler/rustc_mir_dataflow/src/framework/tests.rs +++ b/compiler/rustc_mir_dataflow/src/framework/tests.rs @@ -220,9 +220,7 @@ impl 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>, ) { } } diff --git a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs index d38b567a95849..6df2c8df3cea0 100644 --- a/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs @@ -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, _block: mir::BasicBlock, - _func: &mir::Operand<'tcx>, - _args: &[mir::Operand<'tcx>], - _dest_place: mir::Place<'tcx>, + _return_places: CallReturnPlaces<'_, 'tcx>, ) { } } diff --git a/compiler/rustc_mir_dataflow/src/impls/init_locals.rs b/compiler/rustc_mir_dataflow/src/impls/init_locals.rs index 07570e764f5e0..df13b5c33940a 100644 --- a/compiler/rustc_mir_dataflow/src/impls/init_locals.rs +++ b/compiler/rustc_mir_dataflow/src/impls/init_locals.rs @@ -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, _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), diff --git a/compiler/rustc_mir_dataflow/src/impls/liveness.rs b/compiler/rustc_mir_dataflow/src/impls/liveness.rs index 3e2548845e20f..5be9df6c452a2 100644 --- a/compiler/rustc_mir_dataflow/src/impls/liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/liveness.rs @@ -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, _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, diff --git a/compiler/rustc_mir_dataflow/src/impls/mod.rs b/compiler/rustc_mir_dataflow/src/impls/mod.rs index 2585701f60c66..5659fd2dc7075 100644 --- a/compiler/rustc_mir_dataflow/src/impls/mod.rs +++ b/compiler/rustc_mir_dataflow/src/impls/mod.rs @@ -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, _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>( @@ -472,21 +472,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> { &self, trans: &mut impl GenKill, _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>( @@ -591,21 +591,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> { &self, trans: &mut impl GenKill, _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, 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; diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index b468e50b391ca..108357abc0de0 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -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, _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, _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( diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 77a72ce63ce5e..10d2cf6eba03d 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -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; diff --git a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs index e404b49ecb931..feb85d4ffdfa8 100644 --- a/compiler/rustc_mir_dataflow/src/move_paths/builder.rs +++ b/compiler/rustc_mir_dataflow/src/move_paths/builder.rs @@ -419,6 +419,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> { options: _, line_spans: _, destination: _, + cleanup: _, } => { for op in operands { match *op { diff --git a/compiler/rustc_mir_transform/src/check_unsafety.rs b/compiler/rustc_mir_transform/src/check_unsafety.rs index 1ff9bd1572108..cdfeb957df9b1 100644 --- a/compiler/rustc_mir_transform/src/check_unsafety.rs +++ b/compiler/rustc_mir_transform/src/check_unsafety.rs @@ -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. diff --git a/compiler/rustc_mir_transform/src/const_prop.rs b/compiler/rustc_mir_transform/src/const_prop.rs index 63c637af5c21a..4bfa1de7a3b3f 100644 --- a/compiler/rustc_mir_transform/src/const_prop.rs +++ b/compiler/rustc_mir_transform/src/const_prop.rs @@ -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) diff --git a/compiler/rustc_mir_transform/src/dest_prop.rs b/compiler/rustc_mir_transform/src/dest_prop.rs index c45946a9e2a98..f3217f0b9b68b 100644 --- a/compiler/rustc_mir_transform/src/dest_prop.rs +++ b/compiler/rustc_mir_transform/src/dest_prop.rs @@ -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. diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 84a1e3fb600fd..4dacd4c288a27 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -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; } } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 06e4cee30ed81..309c305293fd6 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -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, diff --git a/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index fac55b978886c..fa238a8b3bc65 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -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(" *["," ] [","] ")" -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: diff --git a/src/test/codegen/asm-may_unwind.rs b/src/test/codegen/asm-may_unwind.rs new file mode 100644 index 0000000000000..85cae8b2b1c67 --- /dev/null +++ b/src/test/codegen/asm-may_unwind.rs @@ -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)); +} diff --git a/src/test/ui/asm/aarch64/bad-options.stderr b/src/test/ui/asm/aarch64/bad-options.stderr index 21bcc4a9c7bad..867e0433eae1b 100644 --- a/src/test/ui/asm/aarch64/bad-options.stderr +++ b/src/test/ui/asm/aarch64/bad-options.stderr @@ -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 diff --git a/src/test/ui/asm/aarch64/may_unwind.rs b/src/test/ui/asm/aarch64/may_unwind.rs new file mode 100644 index 0000000000000..94cc7d750491a --- /dev/null +++ b/src/test/ui/asm/aarch64/may_unwind.rs @@ -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); +} diff --git a/src/test/ui/asm/aarch64/parse-error.stderr b/src/test/ui/asm/aarch64/parse-error.stderr index 3d88cef5c7d71..a143c3b2b2818 100644 --- a/src/test/ui/asm/aarch64/parse-error.stderr +++ b/src/test/ui/asm/aarch64/parse-error.stderr @@ -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 diff --git a/src/test/ui/asm/may_unwind.rs b/src/test/ui/asm/may_unwind.rs new file mode 100644 index 0000000000000..436e8b9d95a89 --- /dev/null +++ b/src/test/ui/asm/may_unwind.rs @@ -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)) }; +} diff --git a/src/test/ui/asm/x86_64/bad-options.stderr b/src/test/ui/asm/x86_64/bad-options.stderr index e2351840eef21..a63c42aac2737 100644 --- a/src/test/ui/asm/x86_64/bad-options.stderr +++ b/src/test/ui/asm/x86_64/bad-options.stderr @@ -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 diff --git a/src/test/ui/asm/x86_64/may_unwind.rs b/src/test/ui/asm/x86_64/may_unwind.rs new file mode 100644 index 0000000000000..5ac4dd9b956d7 --- /dev/null +++ b/src/test/ui/asm/x86_64/may_unwind.rs @@ -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); +} diff --git a/src/test/ui/asm/x86_64/parse-error.stderr b/src/test/ui/asm/x86_64/parse-error.stderr index 018df9826c6ee..4f16c15af381c 100644 --- a/src/test/ui/asm/x86_64/parse-error.stderr +++ b/src/test/ui/asm/x86_64/parse-error.stderr @@ -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 diff --git a/src/test/ui/feature-gates/feature-gate-asm_unwind.rs b/src/test/ui/feature-gates/feature-gate-asm_unwind.rs new file mode 100644 index 0000000000000..c9957ff91d583 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-asm_unwind.rs @@ -0,0 +1,10 @@ +// only-x86_64 + +#![feature(asm)] + +fn main() { + unsafe { + asm!("", options(may_unwind)); + //~^ ERROR the `may_unwind` option is unstable + } +} diff --git a/src/test/ui/feature-gates/feature-gate-asm_unwind.stderr b/src/test/ui/feature-gates/feature-gate-asm_unwind.stderr new file mode 100644 index 0000000000000..6b5bf286e7bca --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-asm_unwind.stderr @@ -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 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`. diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index f7711b6fe9476..0eba6633ee19b 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -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, + _trans: &mut impl GenKill, _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 }