Add initial AST and MIR support for unwinding from inline assembly
This commit is contained in:
parent
532d2b14c0
commit
940b2eabad
39 changed files with 355 additions and 212 deletions
|
@ -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>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue