moves terminator types to sub module
This commit is contained in:
parent
7750c3d46b
commit
15e81beb47
2 changed files with 519 additions and 494 deletions
|
@ -21,7 +21,6 @@ use rustc_target::abi::VariantIdx;
|
||||||
|
|
||||||
use polonius_engine::Atom;
|
use polonius_engine::Atom;
|
||||||
pub use rustc_ast::ast::Mutability;
|
pub use rustc_ast::ast::Mutability;
|
||||||
use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_data_structures::graph::dominators::{dominators, Dominators};
|
use rustc_data_structures::graph::dominators::{dominators, Dominators};
|
||||||
use rustc_data_structures::graph::{self, GraphSuccessors};
|
use rustc_data_structures::graph::{self, GraphSuccessors};
|
||||||
|
@ -47,6 +46,8 @@ pub mod mono;
|
||||||
mod predecessors;
|
mod predecessors;
|
||||||
mod query;
|
mod query;
|
||||||
pub mod tcx;
|
pub mod tcx;
|
||||||
|
pub mod terminator;
|
||||||
|
pub use terminator::*;
|
||||||
pub mod traversal;
|
pub mod traversal;
|
||||||
mod type_foldable;
|
mod type_foldable;
|
||||||
pub mod visit;
|
pub mod visit;
|
||||||
|
@ -298,7 +299,11 @@ impl<'tcx> Body<'tcx> {
|
||||||
pub fn temps_iter<'a>(&'a self) -> impl Iterator<Item = Local> + 'a {
|
pub fn temps_iter<'a>(&'a self) -> impl Iterator<Item = Local> + 'a {
|
||||||
(self.arg_count + 1..self.local_decls.len()).filter_map(move |index| {
|
(self.arg_count + 1..self.local_decls.len()).filter_map(move |index| {
|
||||||
let local = Local::new(index);
|
let local = Local::new(index);
|
||||||
if self.local_decls[local].is_user_variable() { None } else { Some(local) }
|
if self.local_decls[local].is_user_variable() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(local)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1059,191 +1064,6 @@ pub struct BasicBlockData<'tcx> {
|
||||||
pub is_cleanup: bool,
|
pub is_cleanup: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
|
|
||||||
pub struct Terminator<'tcx> {
|
|
||||||
pub source_info: SourceInfo,
|
|
||||||
pub kind: TerminatorKind<'tcx>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)]
|
|
||||||
pub enum TerminatorKind<'tcx> {
|
|
||||||
/// Block should have one successor in the graph; we jump there.
|
|
||||||
Goto { target: BasicBlock },
|
|
||||||
|
|
||||||
/// Operand evaluates to an integer; jump depending on its value
|
|
||||||
/// to one of the targets, and otherwise fallback to `otherwise`.
|
|
||||||
SwitchInt {
|
|
||||||
/// The discriminant value being tested.
|
|
||||||
discr: Operand<'tcx>,
|
|
||||||
|
|
||||||
/// The type of value being tested.
|
|
||||||
/// This is always the same as the type of `discr`.
|
|
||||||
/// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing.
|
|
||||||
switch_ty: Ty<'tcx>,
|
|
||||||
|
|
||||||
/// Possible values. The locations to branch to in each case
|
|
||||||
/// are found in the corresponding indices from the `targets` vector.
|
|
||||||
values: Cow<'tcx, [u128]>,
|
|
||||||
|
|
||||||
/// Possible branch sites. The last element of this vector is used
|
|
||||||
/// for the otherwise branch, so targets.len() == values.len() + 1
|
|
||||||
/// should hold.
|
|
||||||
//
|
|
||||||
// This invariant is quite non-obvious and also could be improved.
|
|
||||||
// One way to make this invariant is to have something like this instead:
|
|
||||||
//
|
|
||||||
// branches: Vec<(ConstInt, BasicBlock)>,
|
|
||||||
// otherwise: Option<BasicBlock> // exhaustive if None
|
|
||||||
//
|
|
||||||
// However we’ve decided to keep this as-is until we figure a case
|
|
||||||
// where some other approach seems to be strictly better than other.
|
|
||||||
targets: Vec<BasicBlock>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Indicates that the landing pad is finished and unwinding should
|
|
||||||
/// continue. Emitted by `build::scope::diverge_cleanup`.
|
|
||||||
Resume,
|
|
||||||
|
|
||||||
/// Indicates that the landing pad is finished and that the process
|
|
||||||
/// should abort. Used to prevent unwinding for foreign items.
|
|
||||||
Abort,
|
|
||||||
|
|
||||||
/// Indicates a normal return. The return place should have
|
|
||||||
/// been filled in before this executes. This can occur multiple times
|
|
||||||
/// in different basic blocks.
|
|
||||||
Return,
|
|
||||||
|
|
||||||
/// Indicates a terminator that can never be reached.
|
|
||||||
Unreachable,
|
|
||||||
|
|
||||||
/// Drop the `Place`.
|
|
||||||
Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option<BasicBlock> },
|
|
||||||
|
|
||||||
/// Drop the `Place` and assign the new value over it. This ensures
|
|
||||||
/// that the assignment to `P` occurs *even if* the destructor for
|
|
||||||
/// place unwinds. Its semantics are best explained by the
|
|
||||||
/// elaboration:
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// BB0 {
|
|
||||||
/// DropAndReplace(P <- V, goto BB1, unwind BB2)
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// becomes
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// BB0 {
|
|
||||||
/// Drop(P, goto BB1, unwind BB2)
|
|
||||||
/// }
|
|
||||||
/// BB1 {
|
|
||||||
/// // P is now uninitialized
|
|
||||||
/// P <- V
|
|
||||||
/// }
|
|
||||||
/// BB2 {
|
|
||||||
/// // P is now uninitialized -- its dtor panicked
|
|
||||||
/// P <- V
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
DropAndReplace {
|
|
||||||
place: Place<'tcx>,
|
|
||||||
value: Operand<'tcx>,
|
|
||||||
target: BasicBlock,
|
|
||||||
unwind: Option<BasicBlock>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Block ends with a call of a converging function.
|
|
||||||
Call {
|
|
||||||
/// The function that’s being called.
|
|
||||||
func: Operand<'tcx>,
|
|
||||||
/// Arguments the function is called with.
|
|
||||||
/// These are owned by the callee, which is free to modify them.
|
|
||||||
/// This allows the memory occupied by "by-value" arguments to be
|
|
||||||
/// reused across function calls without duplicating the contents.
|
|
||||||
args: Vec<Operand<'tcx>>,
|
|
||||||
/// Destination for the return value. If some, the call is converging.
|
|
||||||
destination: Option<(Place<'tcx>, BasicBlock)>,
|
|
||||||
/// Cleanups to be done if the call unwinds.
|
|
||||||
cleanup: Option<BasicBlock>,
|
|
||||||
/// `true` if this is from a call in HIR rather than from an overloaded
|
|
||||||
/// operator. True for overloaded function call.
|
|
||||||
from_hir_call: bool,
|
|
||||||
/// This `Span` is the span of the function, without the dot and receiver
|
|
||||||
/// (e.g. `foo(a, b)` in `x.foo(a, b)`
|
|
||||||
fn_span: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Jump to the target if the condition has the expected value,
|
|
||||||
/// otherwise panic with a message and a cleanup target.
|
|
||||||
Assert {
|
|
||||||
cond: Operand<'tcx>,
|
|
||||||
expected: bool,
|
|
||||||
msg: AssertMessage<'tcx>,
|
|
||||||
target: BasicBlock,
|
|
||||||
cleanup: Option<BasicBlock>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// A suspend point.
|
|
||||||
Yield {
|
|
||||||
/// The value to return.
|
|
||||||
value: Operand<'tcx>,
|
|
||||||
/// Where to resume to.
|
|
||||||
resume: BasicBlock,
|
|
||||||
/// The place to store the resume argument in.
|
|
||||||
resume_arg: Place<'tcx>,
|
|
||||||
/// Cleanup to be done if the generator is dropped at this suspend point.
|
|
||||||
drop: Option<BasicBlock>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Indicates the end of the dropping of a generator.
|
|
||||||
GeneratorDrop,
|
|
||||||
|
|
||||||
/// A block where control flow only ever takes one real path, but borrowck
|
|
||||||
/// needs to be more conservative.
|
|
||||||
FalseEdge {
|
|
||||||
/// The target normal control flow will take.
|
|
||||||
real_target: BasicBlock,
|
|
||||||
/// A block control flow could conceptually jump to, but won't in
|
|
||||||
/// practice.
|
|
||||||
imaginary_target: BasicBlock,
|
|
||||||
},
|
|
||||||
/// A terminator for blocks that only take one path in reality, but where we
|
|
||||||
/// reserve the right to unwind in borrowck, even if it won't happen in practice.
|
|
||||||
/// This can arise in infinite loops with no function calls for example.
|
|
||||||
FalseUnwind {
|
|
||||||
/// The target normal control flow will take.
|
|
||||||
real_target: BasicBlock,
|
|
||||||
/// The imaginary cleanup block link. This particular path will never be taken
|
|
||||||
/// in practice, but in order to avoid fragility we want to always
|
|
||||||
/// consider it in borrowck. We don't want to accept programs which
|
|
||||||
/// pass borrowck only when `panic=abort` or some assertions are disabled
|
|
||||||
/// due to release vs. debug mode builds. This needs to be an `Option` because
|
|
||||||
/// of the `remove_noop_landing_pads` and `no_landing_pads` passes.
|
|
||||||
unwind: Option<BasicBlock>,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Block ends with an inline assembly block. This is a terminator since
|
|
||||||
/// inline assembly is allowed to diverge.
|
|
||||||
InlineAsm {
|
|
||||||
/// The template for the inline assembly, with placeholders.
|
|
||||||
template: &'tcx [InlineAsmTemplatePiece],
|
|
||||||
|
|
||||||
/// The operands for the inline assembly, as `Operand`s or `Place`s.
|
|
||||||
operands: Vec<InlineAsmOperand<'tcx>>,
|
|
||||||
|
|
||||||
/// Miscellaneous options for the inline assembly.
|
|
||||||
options: InlineAsmOptions,
|
|
||||||
|
|
||||||
/// Source spans for each line of the inline assembly code. These are
|
|
||||||
/// used to map assembler errors back to the line in the source code.
|
|
||||||
line_spans: &'tcx [Span],
|
|
||||||
|
|
||||||
/// Destination block after the inline assembly returns, unless it is
|
|
||||||
/// diverging (InlineAsmOptions::NORETURN).
|
|
||||||
destination: Option<BasicBlock>,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about an assertion failure.
|
/// Information about an assertion failure.
|
||||||
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)]
|
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)]
|
||||||
pub enum AssertKind<O> {
|
pub enum AssertKind<O> {
|
||||||
|
@ -1292,149 +1112,6 @@ pub type Successors<'a> =
|
||||||
pub type SuccessorsMut<'a> =
|
pub type SuccessorsMut<'a> =
|
||||||
iter::Chain<option::IntoIter<&'a mut BasicBlock>, slice::IterMut<'a, BasicBlock>>;
|
iter::Chain<option::IntoIter<&'a mut BasicBlock>, slice::IterMut<'a, BasicBlock>>;
|
||||||
|
|
||||||
impl<'tcx> Terminator<'tcx> {
|
|
||||||
pub fn successors(&self) -> Successors<'_> {
|
|
||||||
self.kind.successors()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
|
|
||||||
self.kind.successors_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwind(&self) -> Option<&Option<BasicBlock>> {
|
|
||||||
self.kind.unwind()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
|
|
||||||
self.kind.unwind_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> TerminatorKind<'tcx> {
|
|
||||||
pub fn if_(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
cond: Operand<'tcx>,
|
|
||||||
t: BasicBlock,
|
|
||||||
f: BasicBlock,
|
|
||||||
) -> TerminatorKind<'tcx> {
|
|
||||||
static BOOL_SWITCH_FALSE: &[u128] = &[0];
|
|
||||||
TerminatorKind::SwitchInt {
|
|
||||||
discr: cond,
|
|
||||||
switch_ty: tcx.types.bool,
|
|
||||||
values: From::from(BOOL_SWITCH_FALSE),
|
|
||||||
targets: vec![f, t],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn successors(&self) -> Successors<'_> {
|
|
||||||
use self::TerminatorKind::*;
|
|
||||||
match *self {
|
|
||||||
Resume
|
|
||||||
| Abort
|
|
||||||
| GeneratorDrop
|
|
||||||
| Return
|
|
||||||
| Unreachable
|
|
||||||
| Call { destination: None, cleanup: None, .. }
|
|
||||||
| InlineAsm { destination: None, .. } => None.into_iter().chain(&[]),
|
|
||||||
Goto { target: ref t }
|
|
||||||
| Call { destination: None, cleanup: Some(ref t), .. }
|
|
||||||
| Call { destination: Some((_, ref t)), cleanup: None, .. }
|
|
||||||
| Yield { resume: ref t, drop: None, .. }
|
|
||||||
| DropAndReplace { target: ref t, unwind: None, .. }
|
|
||||||
| 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(&[]),
|
|
||||||
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) } => {
|
|
||||||
Some(t).into_iter().chain(slice::from_ref(u))
|
|
||||||
}
|
|
||||||
SwitchInt { ref targets, .. } => None.into_iter().chain(&targets[..]),
|
|
||||||
FalseEdge { ref real_target, ref imaginary_target } => {
|
|
||||||
Some(real_target).into_iter().chain(slice::from_ref(imaginary_target))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
|
|
||||||
use self::TerminatorKind::*;
|
|
||||||
match *self {
|
|
||||||
Resume
|
|
||||||
| Abort
|
|
||||||
| GeneratorDrop
|
|
||||||
| Return
|
|
||||||
| Unreachable
|
|
||||||
| Call { destination: None, cleanup: None, .. }
|
|
||||||
| InlineAsm { destination: 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, .. }
|
|
||||||
| Yield { resume: ref mut t, drop: None, .. }
|
|
||||||
| DropAndReplace { target: ref mut t, unwind: None, .. }
|
|
||||||
| 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 []),
|
|
||||||
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) } => {
|
|
||||||
Some(t).into_iter().chain(slice::from_mut(u))
|
|
||||||
}
|
|
||||||
SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets[..]),
|
|
||||||
FalseEdge { ref mut real_target, ref mut imaginary_target } => {
|
|
||||||
Some(real_target).into_iter().chain(slice::from_mut(imaginary_target))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwind(&self) -> Option<&Option<BasicBlock>> {
|
|
||||||
match *self {
|
|
||||||
TerminatorKind::Goto { .. }
|
|
||||||
| TerminatorKind::Resume
|
|
||||||
| TerminatorKind::Abort
|
|
||||||
| TerminatorKind::Return
|
|
||||||
| TerminatorKind::Unreachable
|
|
||||||
| TerminatorKind::GeneratorDrop
|
|
||||||
| TerminatorKind::Yield { .. }
|
|
||||||
| TerminatorKind::SwitchInt { .. }
|
|
||||||
| TerminatorKind::FalseEdge { .. }
|
|
||||||
| TerminatorKind::InlineAsm { .. } => 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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
|
|
||||||
match *self {
|
|
||||||
TerminatorKind::Goto { .. }
|
|
||||||
| TerminatorKind::Resume
|
|
||||||
| TerminatorKind::Abort
|
|
||||||
| TerminatorKind::Return
|
|
||||||
| TerminatorKind::Unreachable
|
|
||||||
| TerminatorKind::GeneratorDrop
|
|
||||||
| TerminatorKind::Yield { .. }
|
|
||||||
| TerminatorKind::SwitchInt { .. }
|
|
||||||
| TerminatorKind::FalseEdge { .. }
|
|
||||||
| TerminatorKind::InlineAsm { .. } => 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),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> BasicBlockData<'tcx> {
|
impl<'tcx> BasicBlockData<'tcx> {
|
||||||
pub fn new(terminator: Option<Terminator<'tcx>>) -> BasicBlockData<'tcx> {
|
pub fn new(terminator: Option<Terminator<'tcx>>) -> BasicBlockData<'tcx> {
|
||||||
BasicBlockData { statements: vec![], terminator, is_cleanup: false }
|
BasicBlockData { statements: vec![], terminator, is_cleanup: false }
|
||||||
|
@ -1513,7 +1190,11 @@ impl<'tcx> BasicBlockData<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> {
|
pub fn visitable(&self, index: usize) -> &dyn MirVisitable<'tcx> {
|
||||||
if index < self.statements.len() { &self.statements[index] } else { &self.terminator }
|
if index < self.statements.len() {
|
||||||
|
&self.statements[index]
|
||||||
|
} else {
|
||||||
|
&self.terminator
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1641,169 +1322,6 @@ impl<O: fmt::Debug> fmt::Debug for AssertKind<O> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> Debug for TerminatorKind<'tcx> {
|
|
||||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
self.fmt_head(fmt)?;
|
|
||||||
let successor_count = self.successors().count();
|
|
||||||
let labels = self.fmt_successor_labels();
|
|
||||||
assert_eq!(successor_count, labels.len());
|
|
||||||
|
|
||||||
match successor_count {
|
|
||||||
0 => Ok(()),
|
|
||||||
|
|
||||||
1 => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
|
|
||||||
|
|
||||||
_ => {
|
|
||||||
write!(fmt, " -> [")?;
|
|
||||||
for (i, target) in self.successors().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
write!(fmt, ", ")?;
|
|
||||||
}
|
|
||||||
write!(fmt, "{}: {:?}", labels[i], target)?;
|
|
||||||
}
|
|
||||||
write!(fmt, "]")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> TerminatorKind<'tcx> {
|
|
||||||
/// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
|
|
||||||
/// successor basic block, if any. The only information not included is the list of possible
|
|
||||||
/// successors, which may be rendered differently between the text and the graphviz format.
|
|
||||||
pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
|
|
||||||
use self::TerminatorKind::*;
|
|
||||||
match self {
|
|
||||||
Goto { .. } => write!(fmt, "goto"),
|
|
||||||
SwitchInt { discr, .. } => write!(fmt, "switchInt({:?})", discr),
|
|
||||||
Return => write!(fmt, "return"),
|
|
||||||
GeneratorDrop => write!(fmt, "generator_drop"),
|
|
||||||
Resume => write!(fmt, "resume"),
|
|
||||||
Abort => write!(fmt, "abort"),
|
|
||||||
Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value),
|
|
||||||
Unreachable => write!(fmt, "unreachable"),
|
|
||||||
Drop { place, .. } => write!(fmt, "drop({:?})", place),
|
|
||||||
DropAndReplace { place, value, .. } => {
|
|
||||||
write!(fmt, "replace({:?} <- {:?})", place, value)
|
|
||||||
}
|
|
||||||
Call { func, args, destination, .. } => {
|
|
||||||
if let Some((destination, _)) = destination {
|
|
||||||
write!(fmt, "{:?} = ", destination)?;
|
|
||||||
}
|
|
||||||
write!(fmt, "{:?}(", func)?;
|
|
||||||
for (index, arg) in args.iter().enumerate() {
|
|
||||||
if index > 0 {
|
|
||||||
write!(fmt, ", ")?;
|
|
||||||
}
|
|
||||||
write!(fmt, "{:?}", arg)?;
|
|
||||||
}
|
|
||||||
write!(fmt, ")")
|
|
||||||
}
|
|
||||||
Assert { cond, expected, msg, .. } => {
|
|
||||||
write!(fmt, "assert(")?;
|
|
||||||
if !expected {
|
|
||||||
write!(fmt, "!")?;
|
|
||||||
}
|
|
||||||
write!(fmt, "{:?}, ", cond)?;
|
|
||||||
msg.fmt_assert_args(fmt)?;
|
|
||||||
write!(fmt, ")")
|
|
||||||
}
|
|
||||||
FalseEdge { .. } => write!(fmt, "falseEdge"),
|
|
||||||
FalseUnwind { .. } => write!(fmt, "falseUnwind"),
|
|
||||||
InlineAsm { template, ref operands, options, .. } => {
|
|
||||||
write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
|
|
||||||
for op in operands {
|
|
||||||
write!(fmt, ", ")?;
|
|
||||||
let print_late = |&late| if late { "late" } else { "" };
|
|
||||||
match op {
|
|
||||||
InlineAsmOperand::In { reg, value } => {
|
|
||||||
write!(fmt, "in({}) {:?}", reg, value)?;
|
|
||||||
}
|
|
||||||
InlineAsmOperand::Out { reg, late, place: Some(place) } => {
|
|
||||||
write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
|
|
||||||
}
|
|
||||||
InlineAsmOperand::Out { reg, late, place: None } => {
|
|
||||||
write!(fmt, "{}out({}) _", print_late(late), reg)?;
|
|
||||||
}
|
|
||||||
InlineAsmOperand::InOut {
|
|
||||||
reg,
|
|
||||||
late,
|
|
||||||
in_value,
|
|
||||||
out_place: Some(out_place),
|
|
||||||
} => {
|
|
||||||
write!(
|
|
||||||
fmt,
|
|
||||||
"in{}out({}) {:?} => {:?}",
|
|
||||||
print_late(late),
|
|
||||||
reg,
|
|
||||||
in_value,
|
|
||||||
out_place
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
|
|
||||||
write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
|
|
||||||
}
|
|
||||||
InlineAsmOperand::Const { value } => {
|
|
||||||
write!(fmt, "const {:?}", value)?;
|
|
||||||
}
|
|
||||||
InlineAsmOperand::SymFn { value } => {
|
|
||||||
write!(fmt, "sym_fn {:?}", value)?;
|
|
||||||
}
|
|
||||||
InlineAsmOperand::SymStatic { def_id } => {
|
|
||||||
write!(fmt, "sym_static {:?}", def_id)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write!(fmt, ", options({:?}))", options)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the list of labels for the edges to the successor basic blocks.
|
|
||||||
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
|
|
||||||
use self::TerminatorKind::*;
|
|
||||||
match *self {
|
|
||||||
Return | Resume | Abort | Unreachable | GeneratorDrop => vec![],
|
|
||||||
Goto { .. } => vec!["".into()],
|
|
||||||
SwitchInt { ref values, switch_ty, .. } => ty::tls::with(|tcx| {
|
|
||||||
let param_env = ty::ParamEnv::empty();
|
|
||||||
let switch_ty = tcx.lift(&switch_ty).unwrap();
|
|
||||||
let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
|
|
||||||
values
|
|
||||||
.iter()
|
|
||||||
.map(|&u| {
|
|
||||||
ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty)
|
|
||||||
.to_string()
|
|
||||||
.into()
|
|
||||||
})
|
|
||||||
.chain(iter::once("otherwise".into()))
|
|
||||||
.collect()
|
|
||||||
}),
|
|
||||||
Call { destination: Some(_), cleanup: Some(_), .. } => {
|
|
||||||
vec!["return".into(), "unwind".into()]
|
|
||||||
}
|
|
||||||
Call { destination: Some(_), cleanup: None, .. } => vec!["return".into()],
|
|
||||||
Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()],
|
|
||||||
Call { destination: None, cleanup: None, .. } => vec![],
|
|
||||||
Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
|
|
||||||
Yield { drop: None, .. } => vec!["resume".into()],
|
|
||||||
DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => {
|
|
||||||
vec!["return".into()]
|
|
||||||
}
|
|
||||||
DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => {
|
|
||||||
vec!["return".into(), "unwind".into()]
|
|
||||||
}
|
|
||||||
Assert { cleanup: None, .. } => vec!["".into()],
|
|
||||||
Assert { .. } => vec!["success".into(), "unwind".into()],
|
|
||||||
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![],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Statements
|
// Statements
|
||||||
|
|
||||||
|
|
507
src/librustc_middle/mir/terminator/mod.rs
Normal file
507
src/librustc_middle/mir/terminator/mod.rs
Normal file
|
@ -0,0 +1,507 @@
|
||||||
|
use crate::mir::interpret::Scalar;
|
||||||
|
use crate::ty::{self, Ty, TyCtxt};
|
||||||
|
use rustc_ast::ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
AssertMessage, BasicBlock, InlineAsmOperand, Operand, Place, SourceInfo, Successors,
|
||||||
|
SuccessorsMut,
|
||||||
|
};
|
||||||
|
pub use rustc_ast::ast::Mutability;
|
||||||
|
use rustc_macros::HashStable;
|
||||||
|
use rustc_span::Span;
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::fmt::{self, Debug, Formatter, Write};
|
||||||
|
use std::iter;
|
||||||
|
use std::slice;
|
||||||
|
|
||||||
|
pub use super::query::*;
|
||||||
|
|
||||||
|
#[derive(Clone, RustcEncodable, RustcDecodable, HashStable, PartialEq)]
|
||||||
|
pub enum TerminatorKind<'tcx> {
|
||||||
|
/// Block should have one successor in the graph; we jump there.
|
||||||
|
Goto { target: BasicBlock },
|
||||||
|
|
||||||
|
/// Operand evaluates to an integer; jump depending on its value
|
||||||
|
/// to one of the targets, and otherwise fallback to `otherwise`.
|
||||||
|
SwitchInt {
|
||||||
|
/// The discriminant value being tested.
|
||||||
|
discr: Operand<'tcx>,
|
||||||
|
|
||||||
|
/// The type of value being tested.
|
||||||
|
/// This is always the same as the type of `discr`.
|
||||||
|
/// FIXME: remove this redundant information. Currently, it is relied on by pretty-printing.
|
||||||
|
switch_ty: Ty<'tcx>,
|
||||||
|
|
||||||
|
/// Possible values. The locations to branch to in each case
|
||||||
|
/// are found in the corresponding indices from the `targets` vector.
|
||||||
|
values: Cow<'tcx, [u128]>,
|
||||||
|
|
||||||
|
/// Possible branch sites. The last element of this vector is used
|
||||||
|
/// for the otherwise branch, so targets.len() == values.len() + 1
|
||||||
|
/// should hold.
|
||||||
|
//
|
||||||
|
// This invariant is quite non-obvious and also could be improved.
|
||||||
|
// One way to make this invariant is to have something like this instead:
|
||||||
|
//
|
||||||
|
// branches: Vec<(ConstInt, BasicBlock)>,
|
||||||
|
// otherwise: Option<BasicBlock> // exhaustive if None
|
||||||
|
//
|
||||||
|
// However we’ve decided to keep this as-is until we figure a case
|
||||||
|
// where some other approach seems to be strictly better than other.
|
||||||
|
targets: Vec<BasicBlock>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Indicates that the landing pad is finished and unwinding should
|
||||||
|
/// continue. Emitted by `build::scope::diverge_cleanup`.
|
||||||
|
Resume,
|
||||||
|
|
||||||
|
/// Indicates that the landing pad is finished and that the process
|
||||||
|
/// should abort. Used to prevent unwinding for foreign items.
|
||||||
|
Abort,
|
||||||
|
|
||||||
|
/// Indicates a normal return. The return place should have
|
||||||
|
/// been filled in before this executes. This can occur multiple times
|
||||||
|
/// in different basic blocks.
|
||||||
|
Return,
|
||||||
|
|
||||||
|
/// Indicates a terminator that can never be reached.
|
||||||
|
Unreachable,
|
||||||
|
|
||||||
|
/// Drop the `Place`.
|
||||||
|
Drop { place: Place<'tcx>, target: BasicBlock, unwind: Option<BasicBlock> },
|
||||||
|
|
||||||
|
/// Drop the `Place` and assign the new value over it. This ensures
|
||||||
|
/// that the assignment to `P` occurs *even if* the destructor for
|
||||||
|
/// place unwinds. Its semantics are best explained by the
|
||||||
|
/// elaboration:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// BB0 {
|
||||||
|
/// DropAndReplace(P <- V, goto BB1, unwind BB2)
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// becomes
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// BB0 {
|
||||||
|
/// Drop(P, goto BB1, unwind BB2)
|
||||||
|
/// }
|
||||||
|
/// BB1 {
|
||||||
|
/// // P is now uninitialized
|
||||||
|
/// P <- V
|
||||||
|
/// }
|
||||||
|
/// BB2 {
|
||||||
|
/// // P is now uninitialized -- its dtor panicked
|
||||||
|
/// P <- V
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
DropAndReplace {
|
||||||
|
place: Place<'tcx>,
|
||||||
|
value: Operand<'tcx>,
|
||||||
|
target: BasicBlock,
|
||||||
|
unwind: Option<BasicBlock>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Block ends with a call of a converging function.
|
||||||
|
Call {
|
||||||
|
/// The function that’s being called.
|
||||||
|
func: Operand<'tcx>,
|
||||||
|
/// Arguments the function is called with.
|
||||||
|
/// These are owned by the callee, which is free to modify them.
|
||||||
|
/// This allows the memory occupied by "by-value" arguments to be
|
||||||
|
/// reused across function calls without duplicating the contents.
|
||||||
|
args: Vec<Operand<'tcx>>,
|
||||||
|
/// Destination for the return value. If some, the call is converging.
|
||||||
|
destination: Option<(Place<'tcx>, BasicBlock)>,
|
||||||
|
/// Cleanups to be done if the call unwinds.
|
||||||
|
cleanup: Option<BasicBlock>,
|
||||||
|
/// `true` if this is from a call in HIR rather than from an overloaded
|
||||||
|
/// operator. True for overloaded function call.
|
||||||
|
from_hir_call: bool,
|
||||||
|
/// This `Span` is the span of the function, without the dot and receiver
|
||||||
|
/// (e.g. `foo(a, b)` in `x.foo(a, b)`
|
||||||
|
fn_span: Span,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Jump to the target if the condition has the expected value,
|
||||||
|
/// otherwise panic with a message and a cleanup target.
|
||||||
|
Assert {
|
||||||
|
cond: Operand<'tcx>,
|
||||||
|
expected: bool,
|
||||||
|
msg: AssertMessage<'tcx>,
|
||||||
|
target: BasicBlock,
|
||||||
|
cleanup: Option<BasicBlock>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// A suspend point.
|
||||||
|
Yield {
|
||||||
|
/// The value to return.
|
||||||
|
value: Operand<'tcx>,
|
||||||
|
/// Where to resume to.
|
||||||
|
resume: BasicBlock,
|
||||||
|
/// The place to store the resume argument in.
|
||||||
|
resume_arg: Place<'tcx>,
|
||||||
|
/// Cleanup to be done if the generator is dropped at this suspend point.
|
||||||
|
drop: Option<BasicBlock>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Indicates the end of the dropping of a generator.
|
||||||
|
GeneratorDrop,
|
||||||
|
|
||||||
|
/// A block where control flow only ever takes one real path, but borrowck
|
||||||
|
/// needs to be more conservative.
|
||||||
|
FalseEdge {
|
||||||
|
/// The target normal control flow will take.
|
||||||
|
real_target: BasicBlock,
|
||||||
|
/// A block control flow could conceptually jump to, but won't in
|
||||||
|
/// practice.
|
||||||
|
imaginary_target: BasicBlock,
|
||||||
|
},
|
||||||
|
/// A terminator for blocks that only take one path in reality, but where we
|
||||||
|
/// reserve the right to unwind in borrowck, even if it won't happen in practice.
|
||||||
|
/// This can arise in infinite loops with no function calls for example.
|
||||||
|
FalseUnwind {
|
||||||
|
/// The target normal control flow will take.
|
||||||
|
real_target: BasicBlock,
|
||||||
|
/// The imaginary cleanup block link. This particular path will never be taken
|
||||||
|
/// in practice, but in order to avoid fragility we want to always
|
||||||
|
/// consider it in borrowck. We don't want to accept programs which
|
||||||
|
/// pass borrowck only when `panic=abort` or some assertions are disabled
|
||||||
|
/// due to release vs. debug mode builds. This needs to be an `Option` because
|
||||||
|
/// of the `remove_noop_landing_pads` and `no_landing_pads` passes.
|
||||||
|
unwind: Option<BasicBlock>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Block ends with an inline assembly block. This is a terminator since
|
||||||
|
/// inline assembly is allowed to diverge.
|
||||||
|
InlineAsm {
|
||||||
|
/// The template for the inline assembly, with placeholders.
|
||||||
|
template: &'tcx [InlineAsmTemplatePiece],
|
||||||
|
|
||||||
|
/// The operands for the inline assembly, as `Operand`s or `Place`s.
|
||||||
|
operands: Vec<InlineAsmOperand<'tcx>>,
|
||||||
|
|
||||||
|
/// Miscellaneous options for the inline assembly.
|
||||||
|
options: InlineAsmOptions,
|
||||||
|
|
||||||
|
/// Source spans for each line of the inline assembly code. These are
|
||||||
|
/// used to map assembler errors back to the line in the source code.
|
||||||
|
line_spans: &'tcx [Span],
|
||||||
|
|
||||||
|
/// Destination block after the inline assembly returns, unless it is
|
||||||
|
/// diverging (InlineAsmOptions::NORETURN).
|
||||||
|
destination: Option<BasicBlock>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)]
|
||||||
|
pub struct Terminator<'tcx> {
|
||||||
|
pub source_info: SourceInfo,
|
||||||
|
pub kind: TerminatorKind<'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Terminator<'tcx> {
|
||||||
|
pub fn successors(&self) -> Successors<'_> {
|
||||||
|
self.kind.successors()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
|
||||||
|
self.kind.successors_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwind(&self) -> Option<&Option<BasicBlock>> {
|
||||||
|
self.kind.unwind()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
|
||||||
|
self.kind.unwind_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> TerminatorKind<'tcx> {
|
||||||
|
pub fn if_(
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
cond: Operand<'tcx>,
|
||||||
|
t: BasicBlock,
|
||||||
|
f: BasicBlock,
|
||||||
|
) -> TerminatorKind<'tcx> {
|
||||||
|
static BOOL_SWITCH_FALSE: &[u128] = &[0];
|
||||||
|
TerminatorKind::SwitchInt {
|
||||||
|
discr: cond,
|
||||||
|
switch_ty: tcx.types.bool,
|
||||||
|
values: From::from(BOOL_SWITCH_FALSE),
|
||||||
|
targets: vec![f, t],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn successors(&self) -> Successors<'_> {
|
||||||
|
use self::TerminatorKind::*;
|
||||||
|
match *self {
|
||||||
|
Resume
|
||||||
|
| Abort
|
||||||
|
| GeneratorDrop
|
||||||
|
| Return
|
||||||
|
| Unreachable
|
||||||
|
| Call { destination: None, cleanup: None, .. }
|
||||||
|
| InlineAsm { destination: None, .. } => None.into_iter().chain(&[]),
|
||||||
|
Goto { target: ref t }
|
||||||
|
| Call { destination: None, cleanup: Some(ref t), .. }
|
||||||
|
| Call { destination: Some((_, ref t)), cleanup: None, .. }
|
||||||
|
| Yield { resume: ref t, drop: None, .. }
|
||||||
|
| DropAndReplace { target: ref t, unwind: None, .. }
|
||||||
|
| 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(&[]),
|
||||||
|
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) } => {
|
||||||
|
Some(t).into_iter().chain(slice::from_ref(u))
|
||||||
|
}
|
||||||
|
SwitchInt { ref targets, .. } => None.into_iter().chain(&targets[..]),
|
||||||
|
FalseEdge { ref real_target, ref imaginary_target } => {
|
||||||
|
Some(real_target).into_iter().chain(slice::from_ref(imaginary_target))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn successors_mut(&mut self) -> SuccessorsMut<'_> {
|
||||||
|
use self::TerminatorKind::*;
|
||||||
|
match *self {
|
||||||
|
Resume
|
||||||
|
| Abort
|
||||||
|
| GeneratorDrop
|
||||||
|
| Return
|
||||||
|
| Unreachable
|
||||||
|
| Call { destination: None, cleanup: None, .. }
|
||||||
|
| InlineAsm { destination: 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, .. }
|
||||||
|
| Yield { resume: ref mut t, drop: None, .. }
|
||||||
|
| DropAndReplace { target: ref mut t, unwind: None, .. }
|
||||||
|
| 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 []),
|
||||||
|
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) } => {
|
||||||
|
Some(t).into_iter().chain(slice::from_mut(u))
|
||||||
|
}
|
||||||
|
SwitchInt { ref mut targets, .. } => None.into_iter().chain(&mut targets[..]),
|
||||||
|
FalseEdge { ref mut real_target, ref mut imaginary_target } => {
|
||||||
|
Some(real_target).into_iter().chain(slice::from_mut(imaginary_target))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwind(&self) -> Option<&Option<BasicBlock>> {
|
||||||
|
match *self {
|
||||||
|
TerminatorKind::Goto { .. }
|
||||||
|
| TerminatorKind::Resume
|
||||||
|
| TerminatorKind::Abort
|
||||||
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::Unreachable
|
||||||
|
| TerminatorKind::GeneratorDrop
|
||||||
|
| TerminatorKind::Yield { .. }
|
||||||
|
| TerminatorKind::SwitchInt { .. }
|
||||||
|
| TerminatorKind::FalseEdge { .. }
|
||||||
|
| TerminatorKind::InlineAsm { .. } => 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unwind_mut(&mut self) -> Option<&mut Option<BasicBlock>> {
|
||||||
|
match *self {
|
||||||
|
TerminatorKind::Goto { .. }
|
||||||
|
| TerminatorKind::Resume
|
||||||
|
| TerminatorKind::Abort
|
||||||
|
| TerminatorKind::Return
|
||||||
|
| TerminatorKind::Unreachable
|
||||||
|
| TerminatorKind::GeneratorDrop
|
||||||
|
| TerminatorKind::Yield { .. }
|
||||||
|
| TerminatorKind::SwitchInt { .. }
|
||||||
|
| TerminatorKind::FalseEdge { .. }
|
||||||
|
| TerminatorKind::InlineAsm { .. } => 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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Debug for TerminatorKind<'tcx> {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
self.fmt_head(fmt)?;
|
||||||
|
let successor_count = self.successors().count();
|
||||||
|
let labels = self.fmt_successor_labels();
|
||||||
|
assert_eq!(successor_count, labels.len());
|
||||||
|
|
||||||
|
match successor_count {
|
||||||
|
0 => Ok(()),
|
||||||
|
|
||||||
|
1 => write!(fmt, " -> {:?}", self.successors().next().unwrap()),
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
write!(fmt, " -> [")?;
|
||||||
|
for (i, target) in self.successors().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
write!(fmt, ", ")?;
|
||||||
|
}
|
||||||
|
write!(fmt, "{}: {:?}", labels[i], target)?;
|
||||||
|
}
|
||||||
|
write!(fmt, "]")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> TerminatorKind<'tcx> {
|
||||||
|
/// Writes the "head" part of the terminator; that is, its name and the data it uses to pick the
|
||||||
|
/// successor basic block, if any. The only information not included is the list of possible
|
||||||
|
/// successors, which may be rendered differently between the text and the graphviz format.
|
||||||
|
pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
|
||||||
|
use self::TerminatorKind::*;
|
||||||
|
match self {
|
||||||
|
Goto { .. } => write!(fmt, "goto"),
|
||||||
|
SwitchInt { discr, .. } => write!(fmt, "switchInt({:?})", discr),
|
||||||
|
Return => write!(fmt, "return"),
|
||||||
|
GeneratorDrop => write!(fmt, "generator_drop"),
|
||||||
|
Resume => write!(fmt, "resume"),
|
||||||
|
Abort => write!(fmt, "abort"),
|
||||||
|
Yield { value, resume_arg, .. } => write!(fmt, "{:?} = yield({:?})", resume_arg, value),
|
||||||
|
Unreachable => write!(fmt, "unreachable"),
|
||||||
|
Drop { place, .. } => write!(fmt, "drop({:?})", place),
|
||||||
|
DropAndReplace { place, value, .. } => {
|
||||||
|
write!(fmt, "replace({:?} <- {:?})", place, value)
|
||||||
|
}
|
||||||
|
Call { func, args, destination, .. } => {
|
||||||
|
if let Some((destination, _)) = destination {
|
||||||
|
write!(fmt, "{:?} = ", destination)?;
|
||||||
|
}
|
||||||
|
write!(fmt, "{:?}(", func)?;
|
||||||
|
for (index, arg) in args.iter().enumerate() {
|
||||||
|
if index > 0 {
|
||||||
|
write!(fmt, ", ")?;
|
||||||
|
}
|
||||||
|
write!(fmt, "{:?}", arg)?;
|
||||||
|
}
|
||||||
|
write!(fmt, ")")
|
||||||
|
}
|
||||||
|
Assert { cond, expected, msg, .. } => {
|
||||||
|
write!(fmt, "assert(")?;
|
||||||
|
if !expected {
|
||||||
|
write!(fmt, "!")?;
|
||||||
|
}
|
||||||
|
write!(fmt, "{:?}, ", cond)?;
|
||||||
|
msg.fmt_assert_args(fmt)?;
|
||||||
|
write!(fmt, ")")
|
||||||
|
}
|
||||||
|
FalseEdge { .. } => write!(fmt, "falseEdge"),
|
||||||
|
FalseUnwind { .. } => write!(fmt, "falseUnwind"),
|
||||||
|
InlineAsm { template, ref operands, options, .. } => {
|
||||||
|
write!(fmt, "asm!(\"{}\"", InlineAsmTemplatePiece::to_string(template))?;
|
||||||
|
for op in operands {
|
||||||
|
write!(fmt, ", ")?;
|
||||||
|
let print_late = |&late| if late { "late" } else { "" };
|
||||||
|
match op {
|
||||||
|
InlineAsmOperand::In { reg, value } => {
|
||||||
|
write!(fmt, "in({}) {:?}", reg, value)?;
|
||||||
|
}
|
||||||
|
InlineAsmOperand::Out { reg, late, place: Some(place) } => {
|
||||||
|
write!(fmt, "{}out({}) {:?}", print_late(late), reg, place)?;
|
||||||
|
}
|
||||||
|
InlineAsmOperand::Out { reg, late, place: None } => {
|
||||||
|
write!(fmt, "{}out({}) _", print_late(late), reg)?;
|
||||||
|
}
|
||||||
|
InlineAsmOperand::InOut {
|
||||||
|
reg,
|
||||||
|
late,
|
||||||
|
in_value,
|
||||||
|
out_place: Some(out_place),
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
|
fmt,
|
||||||
|
"in{}out({}) {:?} => {:?}",
|
||||||
|
print_late(late),
|
||||||
|
reg,
|
||||||
|
in_value,
|
||||||
|
out_place
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
InlineAsmOperand::InOut { reg, late, in_value, out_place: None } => {
|
||||||
|
write!(fmt, "in{}out({}) {:?} => _", print_late(late), reg, in_value)?;
|
||||||
|
}
|
||||||
|
InlineAsmOperand::Const { value } => {
|
||||||
|
write!(fmt, "const {:?}", value)?;
|
||||||
|
}
|
||||||
|
InlineAsmOperand::SymFn { value } => {
|
||||||
|
write!(fmt, "sym_fn {:?}", value)?;
|
||||||
|
}
|
||||||
|
InlineAsmOperand::SymStatic { def_id } => {
|
||||||
|
write!(fmt, "sym_static {:?}", def_id)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(fmt, ", options({:?}))", options)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the list of labels for the edges to the successor basic blocks.
|
||||||
|
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
|
||||||
|
use self::TerminatorKind::*;
|
||||||
|
match *self {
|
||||||
|
Return | Resume | Abort | Unreachable | GeneratorDrop => vec![],
|
||||||
|
Goto { .. } => vec!["".into()],
|
||||||
|
SwitchInt { ref values, switch_ty, .. } => ty::tls::with(|tcx| {
|
||||||
|
let param_env = ty::ParamEnv::empty();
|
||||||
|
let switch_ty = tcx.lift(&switch_ty).unwrap();
|
||||||
|
let size = tcx.layout_of(param_env.and(switch_ty)).unwrap().size;
|
||||||
|
values
|
||||||
|
.iter()
|
||||||
|
.map(|&u| {
|
||||||
|
ty::Const::from_scalar(tcx, Scalar::from_uint(u, size), switch_ty)
|
||||||
|
.to_string()
|
||||||
|
.into()
|
||||||
|
})
|
||||||
|
.chain(iter::once("otherwise".into()))
|
||||||
|
.collect()
|
||||||
|
}),
|
||||||
|
Call { destination: Some(_), cleanup: Some(_), .. } => {
|
||||||
|
vec!["return".into(), "unwind".into()]
|
||||||
|
}
|
||||||
|
Call { destination: Some(_), cleanup: None, .. } => vec!["return".into()],
|
||||||
|
Call { destination: None, cleanup: Some(_), .. } => vec!["unwind".into()],
|
||||||
|
Call { destination: None, cleanup: None, .. } => vec![],
|
||||||
|
Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()],
|
||||||
|
Yield { drop: None, .. } => vec!["resume".into()],
|
||||||
|
DropAndReplace { unwind: None, .. } | Drop { unwind: None, .. } => {
|
||||||
|
vec!["return".into()]
|
||||||
|
}
|
||||||
|
DropAndReplace { unwind: Some(_), .. } | Drop { unwind: Some(_), .. } => {
|
||||||
|
vec!["return".into(), "unwind".into()]
|
||||||
|
}
|
||||||
|
Assert { cleanup: None, .. } => vec!["".into()],
|
||||||
|
Assert { .. } => vec!["success".into(), "unwind".into()],
|
||||||
|
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![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue