Unreachable terminator can be contained all within the trans.
/// where execution ends, on normal return
pub const END_BLOCK: BasicBlock = BasicBlock(1);
-/// where execution ends, on panic
-pub const DIVERGE_BLOCK: BasicBlock = BasicBlock(2);
-
impl<'tcx> Mir<'tcx> {
pub fn all_basic_blocks(&self) -> Vec<BasicBlock> {
(0..self.basic_blocks.len())
#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BasicBlockData<'tcx> {
pub statements: Vec<Statement<'tcx>>,
- pub terminator: Terminator<'tcx>,
+ pub terminator: Option<Terminator<'tcx>>,
}
#[derive(RustcEncodable, RustcDecodable)]
targets: Vec<BasicBlock>,
},
- /// Indicates that the last statement in the block panics, aborts,
- /// etc. No successors. This terminator appears on exactly one
- /// basic block which we create in advance. However, during
- /// construction, we use this value as a sentinel for "terminator
- /// not yet assigned", and assert at the end that only the
- /// well-known diverging block actually diverges.
- Diverge,
-
/// Indicates that the landing pad is finished and unwinding should
/// continue. Emitted by build::scope::diverge_cleanup.
Resume,
If { targets: ref b, .. } => b.as_slice(),
Switch { targets: ref b, .. } => b,
SwitchInt { targets: ref b, .. } => b,
- Diverge => &[],
Resume => &[],
Return => &[],
Call { targets: ref b, .. } => b.as_slice(),
If { targets: ref mut b, .. } => b.as_mut_slice(),
Switch { targets: ref mut b, .. } => b,
SwitchInt { targets: ref mut b, .. } => b,
- Diverge => &mut [],
Resume => &mut [],
Return => &mut [],
Call { targets: ref mut b, .. } => b.as_mut_slice(),
}
impl<'tcx> BasicBlockData<'tcx> {
- pub fn new(terminator: Terminator<'tcx>) -> BasicBlockData<'tcx> {
+ pub fn new(terminator: Option<Terminator<'tcx>>) -> BasicBlockData<'tcx> {
BasicBlockData {
statements: vec![],
terminator: terminator,
}
}
+
+ /// Accessor for terminator.
+ ///
+ /// Terminator may not be None after construction of the basic block is complete. This accessor
+ /// provides a convenience way to reach the terminator.
+ pub fn terminator(&self) -> &Terminator<'tcx> {
+ self.terminator.as_ref().expect("invalid terminator state")
+ }
+
+ pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> {
+ self.terminator.as_mut().expect("invalid terminator state")
+ }
}
impl<'tcx> Debug for Terminator<'tcx> {
If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv),
Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv),
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
- Diverge => write!(fmt, "diverge"),
Return => write!(fmt, "return"),
Resume => write!(fmt, "resume"),
Call { .. } => {
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
use self::Terminator::*;
match *self {
- Diverge | Return | Resume => vec![],
+ Return | Resume => vec![],
Goto { .. } => vec!["".into_cow()],
If { .. } => vec!["true".into_cow(), "false".into_cow()],
Call { .. } => vec!["return".into_cow(), "unwind".into_cow()],
for statement in &data.statements {
self.visit_statement(block, statement);
}
- self.visit_terminator(block, &data.terminator);
+ data.terminator.as_ref().map(|r| self.visit_terminator(block, r));
}
fn super_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) {
}
}
- Terminator::Diverge |
Terminator::Resume |
Terminator::Return => {
}
for statement in &mut data.statements {
self.visit_statement(block, statement);
}
- self.visit_terminator(block, &mut data.terminator);
+ data.terminator.as_mut().map(|r| self.visit_terminator(block, r));
}
fn super_statement(&mut self,
}
}
- Terminator::Diverge |
Terminator::Resume |
Terminator::Return => {
}
pub fn start_new_block(&mut self) -> BasicBlock {
let node_index = self.basic_blocks.len();
- self.basic_blocks.push(BasicBlockData::new(Terminator::Diverge));
+ self.basic_blocks.push(BasicBlockData::new(None));
BasicBlock::new(node_index)
}
pub fn terminate(&mut self,
block: BasicBlock,
terminator: Terminator<'tcx>) {
- // Check whether this block has already been terminated. For
- // this, we rely on the fact that the initial state is to have
- // a Diverge terminator and an empty list of targets (which
- // is not a valid state).
- debug_assert!(match self.block_data(block).terminator { Terminator::Diverge => true,
- _ => false },
+ debug_assert!(self.block_data(block).terminator.is_none(),
"terminate: block {:?} already has a terminator set", block);
-
- self.block_data_mut(block).terminator = terminator;
+ self.block_data_mut(block).terminator = Some(terminator);
}
}
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
assert_eq!(builder.cfg.start_new_block(), END_BLOCK);
- assert_eq!(builder.cfg.start_new_block(), DIVERGE_BLOCK);
let mut block = START_BLOCK;
let arg_decls = unpack!(block = builder.args_and_body(block,
// Terminator head at the bottom, not including the list of successor blocks. Those will be
// displayed as labels on the edges between blocks.
let mut terminator_head = String::new();
- data.terminator.fmt_head(&mut terminator_head).unwrap();
+ data.terminator().fmt_head(&mut terminator_head).unwrap();
try!(write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head)));
// Close the table, node label, and the node itself.
/// Write graphviz DOT edges with labels between the given basic block and all of its successors.
fn write_edges<W: Write>(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> {
- let terminator = &mir.basic_block_data(source).terminator;
+ let terminator = &mir.basic_block_data(source).terminator();
let labels = terminator.fmt_successor_labels();
for (&target, label) in terminator.successors().iter().zip(labels) {
self.erase_regions_statement(statement);
}
- self.erase_regions_terminator(&mut basic_block.terminator);
+ self.erase_regions_terminator(basic_block.terminator_mut());
}
fn erase_regions_statement(&mut self,
terminator: &mut Terminator<'tcx>) {
match *terminator {
Terminator::Goto { .. } |
- Terminator::Diverge |
Terminator::Resume |
Terminator::Return => {
/* nothing to do */
use rustc::middle::const_eval::ConstVal;
use rustc::mir::repr::*;
-use std::mem;
use transform::util;
use transform::MirPass;
// These blocks are always required.
seen[START_BLOCK.index()] = true;
seen[END_BLOCK.index()] = true;
- seen[DIVERGE_BLOCK.index()] = true;
let mut worklist = vec![START_BLOCK];
while let Some(bb) = worklist.pop() {
- for succ in mir.basic_block_data(bb).terminator.successors() {
+ for succ in mir.basic_block_data(bb).terminator().successors() {
if !seen[succ.index()] {
seen[succ.index()] = true;
worklist.push(*succ);
while mir.basic_block_data(target).statements.is_empty() {
match mir.basic_block_data(target).terminator {
- Terminator::Goto { target: next } => {
+ Some(Terminator::Goto { target: next }) => {
if seen.contains(&next) {
return None;
}
let mut changed = false;
for bb in mir.all_basic_blocks() {
- // Temporarily swap out the terminator we're modifying to keep borrowck happy
- let mut terminator = Terminator::Diverge;
- mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator);
+ // Temporarily take ownership of the terminator we're modifying to keep borrowck happy
+ let mut terminator = mir.basic_block_data_mut(bb).terminator.take()
+ .expect("invalid terminator state");
for target in terminator.successors_mut() {
let new_target = match final_target(mir, *target) {
changed |= *target != new_target;
*target = new_target;
}
-
- mir.basic_block_data_mut(bb).terminator = terminator;
+ mir.basic_block_data_mut(bb).terminator = Some(terminator);
}
-
changed
}
let mut changed = false;
for bb in mir.all_basic_blocks() {
- // Temporarily swap out the terminator we're modifying to keep borrowck happy
- let mut terminator = Terminator::Diverge;
- mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator);
+ let basic_block = mir.basic_block_data_mut(bb);
+ let mut terminator = basic_block.terminator_mut();
- mir.basic_block_data_mut(bb).terminator = match terminator {
+ *terminator = match *terminator {
Terminator::If { ref targets, .. } if targets.0 == targets.1 => {
changed = true;
Terminator::Goto { target: targets.0 }
Terminator::SwitchInt { ref targets, .. } if targets.len() == 1 => {
Terminator::Goto { target: targets[0] }
}
- _ => terminator
+ _ => continue
}
}
changed |= self.remove_goto_chains(mir);
self.remove_dead_blocks(mir);
}
-
// FIXME: Should probably be moved into some kind of pass manager
mir.basic_blocks.shrink_to_fit();
}
/// in a single pass
pub fn update_basic_block_ids(mir: &mut Mir, replacements: &[BasicBlock]) {
for bb in mir.all_basic_blocks() {
- for target in mir.basic_block_data_mut(bb).terminator.successors_mut() {
+ for target in mir.basic_block_data_mut(bb).terminator_mut().successors_mut() {
*target = replacements[target.index()];
}
}
bcx = self.trans_statement(bcx, statement);
}
- debug!("trans_block: terminator: {:?}", data.terminator);
+ debug!("trans_block: terminator: {:?}", data.terminator());
- match data.terminator {
+ match *data.terminator() {
mir::Terminator::Goto { target } => {
build::Br(bcx, self.llblock(target), DebugLoc::None)
}
}
}
- mir::Terminator::Diverge => {
- build::Unreachable(bcx);
- }
-
mir::Terminator::Resume => {
if let Some(llpersonalityslot) = self.llpersonalityslot {
let lp = build::Load(bcx, llpersonalityslot);
// Translate the body of each block
for &bb in &mir_blocks {
- if bb != mir::DIVERGE_BLOCK {
- mircx.trans_block(bb);
- }
+ // NB that we do not handle the Resume terminator specially, because a block containing
+ // that terminator will have a higher block number than a function call which should take
+ // care of filling in that information.
+ mircx.trans_block(bb);
}
-
- // Total hack: translate DIVERGE_BLOCK last. This is so that any
- // panics which the fn may do can initialize the
- // `llpersonalityslot` cell. We don't do this up front because the
- // LLVM type of it is (frankly) annoying to compute.
- mircx.trans_block(mir::DIVERGE_BLOCK);
}
/// Produce, for each argument, a `ValueRef` pointing at the