diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 8a43b8d0aaf..2878b3d5e4e 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -51,9 +51,6 @@ pub const START_BLOCK: BasicBlock = BasicBlock(0); /// 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 { (0..self.basic_blocks.len()) @@ -194,7 +191,7 @@ impl Debug for BasicBlock { #[derive(Debug, RustcEncodable, RustcDecodable)] pub struct BasicBlockData<'tcx> { pub statements: Vec>, - pub terminator: Terminator<'tcx>, + pub terminator: Option>, } #[derive(RustcEncodable, RustcDecodable)] @@ -237,14 +234,6 @@ pub enum Terminator<'tcx> { targets: Vec, }, - /// 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, @@ -317,7 +306,6 @@ impl<'tcx> Terminator<'tcx> { 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(), @@ -336,7 +324,6 @@ impl<'tcx> Terminator<'tcx> { 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(), @@ -350,12 +337,24 @@ impl<'tcx> Terminator<'tcx> { } impl<'tcx> BasicBlockData<'tcx> { - pub fn new(terminator: Terminator<'tcx>) -> BasicBlockData<'tcx> { + pub fn new(terminator: Option>) -> 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> { @@ -396,7 +395,6 @@ impl<'tcx> 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 { .. } => { @@ -414,7 +412,7 @@ impl<'tcx> Terminator<'tcx> { pub fn fmt_successor_labels(&self) -> Vec> { 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()], diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 4d29a71c6e7..52bb9aa3d5c 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -84,7 +84,7 @@ pub trait Visitor<'tcx> { 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>) { @@ -132,7 +132,6 @@ pub trait Visitor<'tcx> { } } - Terminator::Diverge | Terminator::Resume | Terminator::Return => { } @@ -374,7 +373,7 @@ pub trait MutVisitor<'tcx> { 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, @@ -429,7 +428,6 @@ pub trait MutVisitor<'tcx> { } } - Terminator::Diverge | Terminator::Resume | Terminator::Return => { } diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index d28724c30aa..2e70e6bb5ae 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -28,7 +28,7 @@ impl<'tcx> CFG<'tcx> { 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) } @@ -67,15 +67,9 @@ impl<'tcx> CFG<'tcx> { 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); } } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 8347a03cda6..e6e5b8380b2 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -107,7 +107,6 @@ pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>, 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, diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs index d1d3e80e340..1b8fe650558 100644 --- a/src/librustc_mir/graphviz.rs +++ b/src/librustc_mir/graphviz.rs @@ -62,7 +62,7 @@ fn write_node(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<( // 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#"{}"#, dot::escape_html(&terminator_head))); // Close the table, node label, and the node itself. @@ -71,7 +71,7 @@ fn write_node(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<( /// Write graphviz DOT edges with labels between the given basic block and all of its successors. fn write_edges(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) { diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 90bb73dbe28..01d873abc6f 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -59,7 +59,7 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { 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, @@ -79,7 +79,6 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { terminator: &mut Terminator<'tcx>) { match *terminator { Terminator::Goto { .. } | - Terminator::Diverge | Terminator::Resume | Terminator::Return => { /* nothing to do */ diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index d0c0afc80a6..7a5a00a8d56 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -10,7 +10,6 @@ use rustc::middle::const_eval::ConstVal; use rustc::mir::repr::*; -use std::mem; use transform::util; use transform::MirPass; @@ -27,11 +26,10 @@ impl SimplifyCfg { // 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); @@ -51,7 +49,7 @@ impl SimplifyCfg { 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; } @@ -67,9 +65,9 @@ impl SimplifyCfg { 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) { @@ -80,10 +78,8 @@ impl SimplifyCfg { 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 } @@ -91,11 +87,10 @@ impl SimplifyCfg { 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 } @@ -115,7 +110,7 @@ impl SimplifyCfg { Terminator::SwitchInt { ref targets, .. } if targets.len() == 1 => { Terminator::Goto { target: targets[0] } } - _ => terminator + _ => continue } } @@ -131,7 +126,6 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { 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(); } diff --git a/src/librustc_mir/transform/util.rs b/src/librustc_mir/transform/util.rs index 95102694544..7e44beb18a2 100644 --- a/src/librustc_mir/transform/util.rs +++ b/src/librustc_mir/transform/util.rs @@ -15,7 +15,7 @@ use rustc::mir::repr::*; /// 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()]; } } diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 4c2bec14e5e..c2645d9882b 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -33,9 +33,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { 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) } @@ -82,10 +82,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Diverge => { - build::Unreachable(bcx); - } - mir::Terminator::Resume => { if let Some(llpersonalityslot) = self.llpersonalityslot { let lp = build::Load(bcx, llpersonalityslot); diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 0ed76ebeb43..5403b53596c 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -125,16 +125,11 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { // 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