Add initial AST and MIR support for unwinding from inline assembly

This commit is contained in:
Amanieu d'Antras 2021-08-30 01:23:33 +01:00 committed by cynecx
parent 532d2b14c0
commit 940b2eabad
39 changed files with 355 additions and 212 deletions

View file

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

View file

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

View file

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

View file

@ -17,7 +17,7 @@ pub fn categorize(context: PlaceContext) -> Option<DefUse> {
PlaceContext::MutatingUse(MutatingUseContext::Store) | PlaceContext::MutatingUse(MutatingUseContext::Store) |
// This is potentially both a def and a use... // 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 // We let Call define the result in both the success and
// unwind cases. This is not really correct, however it // 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 // the def in call only to the input from the success
// path and not the unwind path. -nmatsakis // path and not the unwind path. -nmatsakis
PlaceContext::MutatingUse(MutatingUseContext::Call) | PlaceContext::MutatingUse(MutatingUseContext::Call) |
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
PlaceContext::MutatingUse(MutatingUseContext::Yield) | PlaceContext::MutatingUse(MutatingUseContext::Yield) |
// Storage live and storage dead aren't proper defines, but we can ignore // 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: _, options: _,
line_spans: _, line_spans: _,
destination: _, destination: _,
cleanup: _,
} => { } => {
for op in operands { for op in operands {
match *op { match *op {

View file

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

View file

@ -1828,10 +1828,16 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
self.assert_iscleanup(body, block_data, unwind, true); self.assert_iscleanup(body, block_data, unwind, true);
} }
} }
TerminatorKind::InlineAsm { destination, .. } => { TerminatorKind::InlineAsm { destination, cleanup, .. } => {
if let Some(target) = destination { if let Some(target) = destination {
self.assert_iscleanup(body, block_data, target, is_cleanup); 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); try_set_option(p, args, sym::att_syntax, ast::InlineAsmOptions::ATT_SYNTAX);
} else if p.eat_keyword(kw::Raw) { } else if p.eat_keyword(kw::Raw) {
try_set_option(p, args, kw::Raw, ast::InlineAsmOptions::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 { } else {
return p.unexpected(); return p.unexpected();
} }

View file

@ -211,6 +211,7 @@ impl<'mir, 'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> Visitor<'tcx>
PlaceContext::MutatingUse( PlaceContext::MutatingUse(
MutatingUseContext::Store MutatingUseContext::Store
| MutatingUseContext::LlvmAsmOutput
| MutatingUseContext::AsmOutput | MutatingUseContext::AsmOutput
| MutatingUseContext::Borrow | MutatingUseContext::Borrow
| MutatingUseContext::AddressOf | MutatingUseContext::AddressOf

View file

@ -1041,6 +1041,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
options, options,
line_spans, line_spans,
destination, destination,
cleanup: _, // TODO
} => { } => {
self.codegen_asm_terminator( self.codegen_asm_terminator(
helper, helper,

View file

@ -7,6 +7,7 @@ use rustc_middle::mir::visit::Visitor;
use rustc_middle::mir::{self, BasicBlock, Local, Location, Statement, StatementKind}; use rustc_middle::mir::{self, BasicBlock, Local, Location, Statement, StatementKind};
use rustc_mir_dataflow::fmt::DebugWithContext; use rustc_mir_dataflow::fmt::DebugWithContext;
use rustc_mir_dataflow::JoinSemiLattice; use rustc_mir_dataflow::JoinSemiLattice;
use rustc_mir_dataflow::{Analysis, AnalysisDomain, CallReturnPlaces};
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
use std::fmt; use std::fmt;
@ -80,18 +81,18 @@ where
fn apply_call_return_effect( fn apply_call_return_effect(
&mut self, &mut self,
_block: BasicBlock, _block: BasicBlock,
_func: &mir::Operand<'tcx>, return_places: CallReturnPlaces<'_, 'tcx>,
_args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
) { ) {
// We cannot reason about another function's internals, so use conservative type-based return_places.for_each(|place| {
// qualification for the result of a function call. // We cannot reason about another function's internals, so use conservative type-based
let return_ty = return_place.ty(self.ccx.body, self.ccx.tcx).ty; // qualification for the result of a function call.
let qualif = Q::in_any_value_of_ty(self.ccx, return_ty); 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() { if !place.is_indirect() {
self.assign_qualif_direct(&return_place, qualif); self.assign_qualif_direct(&place, qualif);
} }
});
} }
fn address_of_allows_mutation(&self, _mt: mir::Mutability, _place: mir::Place<'tcx>) -> bool { 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 where
Q: Qualif, 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 where
Q: Qualif, Q: Qualif,
{ {
@ -375,10 +376,8 @@ where
&self, &self,
state: &mut Self::Domain, state: &mut Self::Domain,
block: BasicBlock, block: BasicBlock,
func: &mir::Operand<'tcx>, return_places: CallReturnPlaces<'_, 'tcx>,
args: &[mir::Operand<'tcx>],
return_place: mir::Place<'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); self.check_edge(location, *unwind, EdgeKind::Unwind);
} }
} }
TerminatorKind::InlineAsm { destination, .. } => { TerminatorKind::InlineAsm { destination, cleanup, .. } => {
if let Some(destination) = destination { if let Some(destination) = destination {
self.check_edge(location, *destination, EdgeKind::Normal); self.check_edge(location, *destination, EdgeKind::Normal);
} }
if let Some(cleanup) = cleanup {
self.check_edge(location, *cleanup, EdgeKind::Unwind);
}
} }
// Nothing to validate for these. // Nothing to validate for these.
TerminatorKind::Resume TerminatorKind::Resume

View file

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

View file

@ -260,6 +260,10 @@ pub enum TerminatorKind<'tcx> {
/// Destination block after the inline assembly returns, unless it is /// Destination block after the inline assembly returns, unless it is
/// diverging (InlineAsmOptions::NORETURN). /// diverging (InlineAsmOptions::NORETURN).
destination: Option<BasicBlock>, 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)] #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]
@ -309,7 +313,7 @@ impl<'tcx> TerminatorKind<'tcx> {
| Return | Return
| Unreachable | Unreachable
| Call { destination: None, cleanup: None, .. } | 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 } Goto { target: ref t }
| Call { destination: None, cleanup: Some(ref t), .. } | Call { destination: None, cleanup: Some(ref t), .. }
| Call { destination: Some((_, ref t)), cleanup: None, .. } | Call { destination: Some((_, ref t)), cleanup: None, .. }
@ -318,13 +322,17 @@ impl<'tcx> TerminatorKind<'tcx> {
| Drop { target: ref t, unwind: None, .. } | Drop { target: ref t, unwind: None, .. }
| Assert { target: ref t, cleanup: None, .. } | Assert { target: ref t, cleanup: None, .. }
| FalseUnwind { real_target: ref t, unwind: 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), .. } Call { destination: Some((_, ref t)), cleanup: Some(ref u), .. }
| Yield { resume: ref t, drop: Some(ref u), .. } | Yield { resume: ref t, drop: Some(ref u), .. }
| DropAndReplace { target: ref t, unwind: Some(ref u), .. } | DropAndReplace { target: ref t, unwind: Some(ref u), .. }
| Drop { target: ref t, unwind: Some(ref u), .. } | Drop { target: ref t, unwind: Some(ref u), .. }
| Assert { target: ref t, cleanup: 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)) Some(t).into_iter().chain(slice::from_ref(u))
} }
SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets[..]), SwitchInt { ref targets, .. } => None.into_iter().chain(&targets.targets[..]),
@ -343,7 +351,7 @@ impl<'tcx> TerminatorKind<'tcx> {
| Return | Return
| Unreachable | Unreachable
| Call { destination: None, cleanup: None, .. } | 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 } Goto { target: ref mut t }
| Call { destination: None, cleanup: Some(ref mut t), .. } | Call { destination: None, cleanup: Some(ref mut t), .. }
| Call { destination: Some((_, ref mut t)), cleanup: None, .. } | Call { destination: Some((_, ref mut t)), cleanup: None, .. }
@ -352,13 +360,17 @@ impl<'tcx> TerminatorKind<'tcx> {
| Drop { target: ref mut t, unwind: None, .. } | Drop { target: ref mut t, unwind: None, .. }
| Assert { target: ref mut t, cleanup: None, .. } | Assert { target: ref mut t, cleanup: None, .. }
| FalseUnwind { real_target: ref mut t, unwind: 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), .. } Call { destination: Some((_, ref mut t)), cleanup: Some(ref mut u), .. }
| Yield { resume: ref mut t, drop: Some(ref mut u), .. } | Yield { resume: ref mut t, drop: Some(ref mut u), .. }
| DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. } | DropAndReplace { target: ref mut t, unwind: Some(ref mut u), .. }
| Drop { 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), .. } | 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)) Some(t).into_iter().chain(slice::from_mut(u))
} }
SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets[..]), SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets.targets[..]),
@ -378,13 +390,13 @@ impl<'tcx> TerminatorKind<'tcx> {
| TerminatorKind::GeneratorDrop | TerminatorKind::GeneratorDrop
| TerminatorKind::Yield { .. } | TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. } | TerminatorKind::SwitchInt { .. }
| TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseEdge { .. } => None,
| TerminatorKind::InlineAsm { .. } => None,
TerminatorKind::Call { cleanup: ref unwind, .. } TerminatorKind::Call { cleanup: ref unwind, .. }
| TerminatorKind::Assert { cleanup: ref unwind, .. } | TerminatorKind::Assert { cleanup: ref unwind, .. }
| TerminatorKind::DropAndReplace { ref unwind, .. } | TerminatorKind::DropAndReplace { ref unwind, .. }
| TerminatorKind::Drop { 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::GeneratorDrop
| TerminatorKind::Yield { .. } | TerminatorKind::Yield { .. }
| TerminatorKind::SwitchInt { .. } | TerminatorKind::SwitchInt { .. }
| TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseEdge { .. } => None,
| TerminatorKind::InlineAsm { .. } => None,
TerminatorKind::Call { cleanup: ref mut unwind, .. } TerminatorKind::Call { cleanup: ref mut unwind, .. }
| TerminatorKind::Assert { cleanup: ref mut unwind, .. } | TerminatorKind::Assert { cleanup: ref mut unwind, .. }
| TerminatorKind::DropAndReplace { ref mut unwind, .. } | TerminatorKind::DropAndReplace { ref mut unwind, .. }
| TerminatorKind::Drop { 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()], FalseEdge { .. } => vec!["real".into(), "imaginary".into()],
FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()], FalseUnwind { unwind: Some(_), .. } => vec!["real".into(), "cleanup".into()],
FalseUnwind { unwind: None, .. } => vec!["real".into()], FalseUnwind { unwind: None, .. } => vec!["real".into()],
InlineAsm { destination: Some(_), .. } => vec!["".into()], InlineAsm { destination: Some(_), cleanup: Some(_), .. } => {
InlineAsm { destination: None, .. } => vec![], 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 } FalseEdge { real_target, imaginary_target }
} }
FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind }, FalseUnwind { real_target, unwind } => FalseUnwind { real_target, unwind },
InlineAsm { template, operands, options, line_spans, destination } => InlineAsm { InlineAsm { template, operands, options, line_spans, destination, cleanup } => {
template, InlineAsm {
operands: operands.try_fold_with(folder)?, template,
options, operands: operands.try_fold_with(folder)?,
line_spans, options,
destination, line_spans,
}, destination,
cleanup,
}
}
}; };
Ok(Terminator { source_info: self.source_info, kind }) 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[..] { for output in & $($mutability)? asm.outputs[..] {
self.visit_place( self.visit_place(
output, output,
PlaceContext::MutatingUse(MutatingUseContext::AsmOutput), PlaceContext::MutatingUse(MutatingUseContext::LlvmAsmOutput),
location location
); );
} }
@ -581,6 +581,7 @@ macro_rules! make_mir_visitor {
options: _, options: _,
line_spans: _, line_spans: _,
destination: _, destination: _,
cleanup: _,
} => { } => {
for op in operands { for op in operands {
match op { match op {
@ -590,7 +591,7 @@ macro_rules! make_mir_visitor {
InlineAsmOperand::Out { place: Some(place), .. } => { InlineAsmOperand::Out { place: Some(place), .. } => {
self.visit_place( self.visit_place(
place, place,
PlaceContext::MutatingUse(MutatingUseContext::Store), PlaceContext::MutatingUse(MutatingUseContext::AsmOutput),
location, location,
); );
} }
@ -599,7 +600,7 @@ macro_rules! make_mir_visitor {
if let Some(out_place) = out_place { if let Some(out_place) = out_place {
self.visit_place( self.visit_place(
out_place, out_place,
PlaceContext::MutatingUse(MutatingUseContext::Store), PlaceContext::MutatingUse(MutatingUseContext::AsmOutput),
location, location,
); );
} }
@ -1178,8 +1179,10 @@ pub enum MutatingUseContext {
/// Appears as LHS of an assignment. /// Appears as LHS of an assignment.
Store, Store,
/// Can often be treated as a `Store`, but needs to be separate because /// 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. /// cannot be simplified the way a `Store`-`Store` can be.
LlvmAsmOutput,
/// Output operand of an inline assembly block.
AsmOutput, AsmOutput,
/// Destination of a call. /// Destination of a call.
Call, Call,
@ -1268,6 +1271,7 @@ impl PlaceContext {
PlaceContext::MutatingUse( PlaceContext::MutatingUse(
MutatingUseContext::Store MutatingUseContext::Store
| MutatingUseContext::Call | MutatingUseContext::Call
| MutatingUseContext::LlvmAsmOutput
| MutatingUseContext::AsmOutput, | MutatingUseContext::AsmOutput,
) )
) )

View file

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

View file

@ -1034,6 +1034,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| TerminatorKind::Call { .. } | TerminatorKind::Call { .. }
| TerminatorKind::DropAndReplace { .. } | TerminatorKind::DropAndReplace { .. }
| TerminatorKind::FalseUnwind { .. } | TerminatorKind::FalseUnwind { .. }
| TerminatorKind::InlineAsm { .. }
), ),
"diverge_from called on block with terminator that cannot unwind." "diverge_from called on block with terminator that cannot unwind."
); );
@ -1373,7 +1374,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
| TerminatorKind::DropAndReplace { unwind, .. } | TerminatorKind::DropAndReplace { unwind, .. }
| TerminatorKind::FalseUnwind { unwind, .. } | TerminatorKind::FalseUnwind { unwind, .. }
| TerminatorKind::Call { cleanup: unwind, .. } | TerminatorKind::Call { cleanup: unwind, .. }
| TerminatorKind::Assert { cleanup: unwind, .. } => { | TerminatorKind::Assert { cleanup: unwind, .. }
| TerminatorKind::InlineAsm { cleanup: unwind, .. } => {
*unwind = Some(to); *unwind = Some(to);
} }
TerminatorKind::Goto { .. } TerminatorKind::Goto { .. }
@ -1384,8 +1386,7 @@ impl<'tcx> DropTreeBuilder<'tcx> for Unwind {
| TerminatorKind::Unreachable | TerminatorKind::Unreachable
| TerminatorKind::Yield { .. } | TerminatorKind::Yield { .. }
| TerminatorKind::GeneratorDrop | TerminatorKind::GeneratorDrop
| TerminatorKind::FalseEdge { .. } | TerminatorKind::FalseEdge { .. } => {
| TerminatorKind::InlineAsm { .. } => {
span_bug!(term.source_info.span, "cannot unwind from {:?}", term.kind) 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 std::ops::RangeInclusive;
use super::visitor::{ResultsVisitable, ResultsVisitor}; 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 { pub trait Direction {
fn is_forward() -> bool; fn is_forward() -> bool;
@ -235,14 +237,26 @@ impl Direction for Backward {
// Apply terminator-specific edge effects. // Apply terminator-specific edge effects.
// //
// FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally. // FIXME(ecstaticmorse): Avoid cloning the exit state unconditionally.
mir::TerminatorKind::Call { mir::TerminatorKind::Call { destination: Some((return_place, dest)), .. }
destination: Some((return_place, dest)), if dest == bb =>
ref func, {
ref args, 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 => { } if dest == bb => {
let mut tmp = exit_state.clone(); 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); propagate(pred, &tmp);
} }
@ -258,6 +272,7 @@ impl Direction for Backward {
| mir::TerminatorKind::Drop { unwind: Some(unwind), .. } | mir::TerminatorKind::Drop { unwind: Some(unwind), .. }
| mir::TerminatorKind::DropAndReplace { unwind: Some(unwind), .. } | mir::TerminatorKind::DropAndReplace { unwind: Some(unwind), .. }
| mir::TerminatorKind::FalseUnwind { unwind: Some(unwind), .. } | mir::TerminatorKind::FalseUnwind { unwind: Some(unwind), .. }
| mir::TerminatorKind::InlineAsm { cleanup: Some(unwind), .. }
if unwind == bb => if unwind == bb =>
{ {
if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) { if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
@ -467,7 +482,7 @@ impl Direction for Forward {
propagate(target, exit_state); 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 let Some(unwind) = cleanup {
if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) { if dead_unwinds.map_or(true, |dead| !dead.contains(bb)) {
propagate(unwind, exit_state); propagate(unwind, exit_state);
@ -477,13 +492,37 @@ impl Direction for Forward {
if let Some((dest_place, target)) = destination { if let Some((dest_place, target)) = destination {
// N.B.: This must be done *last*, otherwise the unwind path will see the call // N.B.: This must be done *last*, otherwise the unwind path will see the call
// return effect. // 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); 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 { 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); 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 rustc_middle::mir::{self, BasicBlock, Body, Location};
use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; 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)] #[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum OutputStyle { pub enum OutputStyle {
@ -231,16 +231,15 @@ where
// for the basic block itself. That way, we could display terminator-specific effects for // for the basic block itself. That way, we could display terminator-specific effects for
// backward dataflow analyses as well as effects for `SwitchInt` terminators. // backward dataflow analyses as well as effects for `SwitchInt` terminators.
match terminator.kind { match terminator.kind {
mir::TerminatorKind::Call { mir::TerminatorKind::Call { destination: Some((return_place, _)), .. } => {
destination: Some((return_place, _)),
ref func,
ref args,
..
} => {
self.write_row(w, "", "(on successful return)", |this, w, fmt| { self.write_row(w, "", "(on successful return)", |this, w, fmt| {
let state_on_unwind = this.results.get().clone(); let state_on_unwind = this.results.get().clone();
this.results.apply_custom_effect(|analysis, state| { 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!( 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, &self,
state: &mut Self::Domain, state: &mut Self::Domain,
block: BasicBlock, block: BasicBlock,
func: &mir::Operand<'tcx>, return_places: CallReturnPlaces<'_, 'tcx>,
args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
); );
/// Updates the current dataflow state with the effect of resuming from a `Yield` terminator. /// 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, &self,
trans: &mut impl GenKill<Self::Idx>, trans: &mut impl GenKill<Self::Idx>,
block: BasicBlock, block: BasicBlock,
func: &mir::Operand<'tcx>, return_places: CallReturnPlaces<'_, 'tcx>,
args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
); );
/// See `Analysis::apply_yield_resume_effect`. /// See `Analysis::apply_yield_resume_effect`.
@ -347,11 +343,9 @@ where
&self, &self,
state: &mut A::Domain, state: &mut A::Domain,
block: BasicBlock, block: BasicBlock,
func: &mir::Operand<'tcx>, return_places: CallReturnPlaces<'_, 'tcx>,
args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
) { ) {
self.call_return_effect(state, block, func, args, return_place); self.call_return_effect(state, block, return_places);
} }
fn apply_yield_resume_effect( 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)); 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)] #[cfg(test)]
mod tests; mod tests;

View file

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

View file

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

View file

@ -2,7 +2,7 @@
//! //!
//! A local will be maybe initialized if *any* projections of that local might be initialized. //! 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_index::bit_set::BitSet;
use rustc_middle::mir::visit::{PlaceContext, Visitor}; use rustc_middle::mir::visit::{PlaceContext, Visitor};
@ -53,11 +53,9 @@ impl crate::GenKillAnalysis<'tcx> for MaybeInitializedLocals {
&self, &self,
trans: &mut impl GenKill<Self::Idx>, trans: &mut impl GenKill<Self::Idx>,
_block: BasicBlock, _block: BasicBlock,
_func: &mir::Operand<'tcx>, return_places: CallReturnPlaces<'_, 'tcx>,
_args: &[mir::Operand<'tcx>],
return_place: mir::Place<'tcx>,
) { ) {
trans.gen(return_place.local) return_places.for_each(|place| trans.gen(place.local));
} }
/// See `Analysis::apply_yield_resume_effect`. /// See `Analysis::apply_yield_resume_effect`.
@ -83,7 +81,11 @@ where
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, NonUseContext}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, NonUseContext};
match context { match context {
// These are handled specially in `call_return_effect` and `yield_resume_effect`. // 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. // Otherwise, when a place is mutated, we must consider it possibly initialized.
PlaceContext::MutatingUse(_) => self.trans.gen(local), 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::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{self, Local, Location}; 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]. /// A [live-variable dataflow analysis][liveness].
/// ///
@ -94,13 +94,13 @@ impl GenKillAnalysis<'tcx> for MaybeLiveLocals {
&self, &self,
trans: &mut impl GenKill<Self::Idx>, trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock, _block: mir::BasicBlock,
_func: &mir::Operand<'tcx>, return_places: CallReturnPlaces<'_, 'tcx>,
_args: &[mir::Operand<'tcx>],
dest_place: mir::Place<'tcx>,
) { ) {
if let Some(local) = dest_place.as_local() { return_places.for_each(|place| {
trans.kill(local); if let Some(local) = place.as_local() {
} trans.kill(local);
}
});
} }
fn yield_resume_effect( fn yield_resume_effect(
@ -167,12 +167,16 @@ impl DefUse {
// destination place for a `Call` return or `Yield` resume respectively. Since this is // 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 // only a `Def` when the function returns successfully, we handle this case separately
// in `call_return_effect` above. // 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... // All other contexts are uses...
PlaceContext::MutatingUse( PlaceContext::MutatingUse(
MutatingUseContext::AddressOf MutatingUseContext::AddressOf
| MutatingUseContext::AsmOutput | MutatingUseContext::LlvmAsmOutput
| MutatingUseContext::Borrow | MutatingUseContext::Borrow
| MutatingUseContext::Drop | MutatingUseContext::Drop
| MutatingUseContext::Retag, | 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_function_entry;
use crate::drop_flag_effects_for_location; use crate::drop_flag_effects_for_location;
use crate::elaborate_drops::DropFlagState; 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::move_paths::{HasMoveData, InitIndex, InitKind, LookupResult, MoveData, MovePathIndex};
use crate::on_lookup_result_bits; use crate::on_lookup_result_bits;
use crate::MoveDataParamEnv; use crate::MoveDataParamEnv;
@ -354,21 +354,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> {
&self, &self,
trans: &mut impl GenKill<Self::Idx>, trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock, _block: mir::BasicBlock,
_func: &mir::Operand<'tcx>, return_places: CallReturnPlaces<'_, 'tcx>,
_args: &[mir::Operand<'tcx>],
dest_place: mir::Place<'tcx>,
) { ) {
// when a call returns successfully, that means we need to set return_places.for_each(|place| {
// the bits for that dest_place to 1 (initialized). // when a call returns successfully, that means we need to set
on_lookup_result_bits( // the bits for that dest_place to 1 (initialized).
self.tcx, on_lookup_result_bits(
self.body, self.tcx,
self.move_data(), self.body,
self.move_data().rev_lookup.find(dest_place.as_ref()), self.move_data(),
|mpi| { self.move_data().rev_lookup.find(place.as_ref()),
trans.gen(mpi); |mpi| {
}, trans.gen(mpi);
); },
);
});
} }
fn switch_int_edge_effects<G: GenKill<Self::Idx>>( fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
@ -472,21 +472,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for MaybeUninitializedPlaces<'_, 'tcx> {
&self, &self,
trans: &mut impl GenKill<Self::Idx>, trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock, _block: mir::BasicBlock,
_func: &mir::Operand<'tcx>, return_places: CallReturnPlaces<'_, 'tcx>,
_args: &[mir::Operand<'tcx>],
dest_place: mir::Place<'tcx>,
) { ) {
// when a call returns successfully, that means we need to set return_places.for_each(|place| {
// the bits for that dest_place to 0 (initialized). // when a call returns successfully, that means we need to set
on_lookup_result_bits( // the bits for that dest_place to 0 (initialized).
self.tcx, on_lookup_result_bits(
self.body, self.tcx,
self.move_data(), self.body,
self.move_data().rev_lookup.find(dest_place.as_ref()), self.move_data(),
|mpi| { self.move_data().rev_lookup.find(place.as_ref()),
trans.kill(mpi); |mpi| {
}, trans.kill(mpi);
); },
);
});
} }
fn switch_int_edge_effects<G: GenKill<Self::Idx>>( fn switch_int_edge_effects<G: GenKill<Self::Idx>>(
@ -591,21 +591,21 @@ impl<'tcx> GenKillAnalysis<'tcx> for DefinitelyInitializedPlaces<'_, 'tcx> {
&self, &self,
trans: &mut impl GenKill<Self::Idx>, trans: &mut impl GenKill<Self::Idx>,
_block: mir::BasicBlock, _block: mir::BasicBlock,
_func: &mir::Operand<'tcx>, return_places: CallReturnPlaces<'_, 'tcx>,
_args: &[mir::Operand<'tcx>],
dest_place: mir::Place<'tcx>,
) { ) {
// when a call returns successfully, that means we need to set return_places.for_each(|place| {
// the bits for that dest_place to 1 (initialized). // when a call returns successfully, that means we need to set
on_lookup_result_bits( // the bits for that dest_place to 1 (initialized).
self.tcx, on_lookup_result_bits(
self.body, self.tcx,
self.move_data(), self.body,
self.move_data().rev_lookup.find(dest_place.as_ref()), self.move_data(),
|mpi| { self.move_data().rev_lookup.find(place.as_ref()),
trans.gen(mpi); |mpi| {
}, trans.gen(mpi);
); },
);
});
} }
} }
@ -679,9 +679,7 @@ impl<'tcx> GenKillAnalysis<'tcx> for EverInitializedPlaces<'_, 'tcx> {
&self, &self,
trans: &mut impl GenKill<Self::Idx>, trans: &mut impl GenKill<Self::Idx>,
block: mir::BasicBlock, block: mir::BasicBlock,
_func: &mir::Operand<'tcx>, _return_places: CallReturnPlaces<'_, 'tcx>,
_args: &[mir::Operand<'tcx>],
_dest_place: mir::Place<'tcx>,
) { ) {
let move_data = self.move_data(); let move_data = self.move_data();
let init_loc_map = &move_data.init_loc_map; let init_loc_map = &move_data.init_loc_map;

View file

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

View file

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

View file

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

View file

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

View file

@ -624,6 +624,7 @@ impl Conflicts<'a> {
options: _, options: _,
line_spans: _, line_spans: _,
destination: _, destination: _,
cleanup: _,
} => { } => {
// The intended semantics here aren't documented, we just assume that nothing that // 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. // 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::Resume => cost += RESUME_PENALTY,
TerminatorKind::InlineAsm { cleanup, .. } => {
cost += INSTR_COST;
if cleanup.is_some() {
cost += LANDINGPAD_PENALTY;
}
}
_ => cost += INSTR_COST, _ => 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") 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 { if let Some(ref mut tgt) = *destination {
*tgt = self.map_block(*tgt); *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

@ -817,6 +817,7 @@ symbols! {
maxnumf32, maxnumf32,
maxnumf64, maxnumf64,
may_dangle, may_dangle,
may_unwind,
maybe_uninit, maybe_uninit,
maybe_uninit_uninit, maybe_uninit_uninit,
maybe_uninit_zeroed, maybe_uninit_zeroed,

View file

@ -36,41 +36,41 @@ LL | asm!("{}", out(reg) foo, clobber_abi("C"));
| | | |
| generic outputs | 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 --> $DIR/bad-options.rs:28:25
| |
LL | global_asm!("", options(nomem)); 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 --> $DIR/bad-options.rs:30:25
| |
LL | global_asm!("", options(readonly)); 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 --> $DIR/bad-options.rs:32:25
| |
LL | global_asm!("", options(noreturn)); 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 --> $DIR/bad-options.rs:34:25
| |
LL | global_asm!("", options(pure)); 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 --> $DIR/bad-options.rs:36:25
| |
LL | global_asm!("", options(nostack)); 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 --> $DIR/bad-options.rs:38:25
| |
LL | global_asm!("", options(preserves_flags)); 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` error: invalid ABI for `clobber_abi`
--> $DIR/bad-options.rs:20:18 --> $DIR/bad-options.rs:20:18

View file

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

View file

@ -45,41 +45,41 @@ LL | asm!("{}", out(reg) foo, clobber_abi("C"), clobber_abi("C"));
| | clobber_abi | | clobber_abi
| generic outputs | 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 --> $DIR/bad-options.rs:31:25
| |
LL | global_asm!("", options(nomem)); 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 --> $DIR/bad-options.rs:33:25
| |
LL | global_asm!("", options(readonly)); 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 --> $DIR/bad-options.rs:35:25
| |
LL | global_asm!("", options(noreturn)); 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 --> $DIR/bad-options.rs:37:25
| |
LL | global_asm!("", options(pure)); 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 --> $DIR/bad-options.rs:39:25
| |
LL | global_asm!("", options(nostack)); 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 --> $DIR/bad-options.rs:41:25
| |
LL | global_asm!("", options(preserves_flags)); 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` error: invalid ABI for `clobber_abi`
--> $DIR/bad-options.rs:20:18 --> $DIR/bad-options.rs:20:18

View file

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

View file

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