mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Add initial AST and MIR support for unwinding from inline assembly
This commit is contained in:
parent
532d2b14c0
commit
940b2eabad
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -199,6 +199,7 @@ impl<'cx, 'tcx> Visitor<'tcx> for InvalidationGenerator<'cx, 'tcx> {
|
||||
options: _,
|
||||
line_spans: _,
|
||||
destination: _,
|
||||
cleanup: _,
|
||||
} => {
|
||||
for op in operands {
|
||||
match *op {
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
});
|
||||
|
@ -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![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 })
|
||||
}
|
||||
|
@ -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,
|
||||
)
|
||||
)
|
||||
|
@ -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()
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
),
|
||||
)
|
||||
})?;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -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>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -419,6 +419,7 @@ impl<'b, 'a, 'tcx> Gatherer<'b, 'a, 'tcx> {
|
||||
options: _,
|
||||
line_spans: _,
|
||||
destination: _,
|
||||
cleanup: _,
|
||||
} => {
|
||||
for op in operands {
|
||||
match *op {
|
||||
|
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -817,6 +817,7 @@ symbols! {
|
||||
maxnumf32,
|
||||
maxnumf64,
|
||||
may_dangle,
|
||||
may_unwind,
|
||||
maybe_uninit,
|
||||
maybe_uninit_uninit,
|
||||
maybe_uninit_zeroed,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user