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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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