Remove diverge terminator

Unreachable terminator can be contained all within the trans.
This commit is contained in:
Simonas Kazlauskas 2015-12-19 00:44:32 +02:00
parent 5b34690842
commit 4e86dcdb72
10 changed files with 40 additions and 67 deletions

View File

@ -51,9 +51,6 @@ pub const START_BLOCK: BasicBlock = BasicBlock(0);
/// where execution ends, on normal return /// where execution ends, on normal return
pub const END_BLOCK: BasicBlock = BasicBlock(1); pub const END_BLOCK: BasicBlock = BasicBlock(1);
/// where execution ends, on panic
pub const DIVERGE_BLOCK: BasicBlock = BasicBlock(2);
impl<'tcx> Mir<'tcx> { impl<'tcx> Mir<'tcx> {
pub fn all_basic_blocks(&self) -> Vec<BasicBlock> { pub fn all_basic_blocks(&self) -> Vec<BasicBlock> {
(0..self.basic_blocks.len()) (0..self.basic_blocks.len())
@ -194,7 +191,7 @@ impl Debug for BasicBlock {
#[derive(Debug, RustcEncodable, RustcDecodable)] #[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BasicBlockData<'tcx> { pub struct BasicBlockData<'tcx> {
pub statements: Vec<Statement<'tcx>>, pub statements: Vec<Statement<'tcx>>,
pub terminator: Terminator<'tcx>, pub terminator: Option<Terminator<'tcx>>,
} }
#[derive(RustcEncodable, RustcDecodable)] #[derive(RustcEncodable, RustcDecodable)]
@ -237,14 +234,6 @@ pub enum Terminator<'tcx> {
targets: Vec<BasicBlock>, 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 /// Indicates that the landing pad is finished and unwinding should
/// continue. Emitted by build::scope::diverge_cleanup. /// continue. Emitted by build::scope::diverge_cleanup.
Resume, Resume,
@ -317,7 +306,6 @@ impl<'tcx> Terminator<'tcx> {
If { targets: ref b, .. } => b.as_slice(), If { targets: ref b, .. } => b.as_slice(),
Switch { targets: ref b, .. } => b, Switch { targets: ref b, .. } => b,
SwitchInt { targets: ref b, .. } => b, SwitchInt { targets: ref b, .. } => b,
Diverge => &[],
Resume => &[], Resume => &[],
Return => &[], Return => &[],
Call { targets: ref b, .. } => b.as_slice(), Call { targets: ref b, .. } => b.as_slice(),
@ -336,7 +324,6 @@ impl<'tcx> Terminator<'tcx> {
If { targets: ref mut b, .. } => b.as_mut_slice(), If { targets: ref mut b, .. } => b.as_mut_slice(),
Switch { targets: ref mut b, .. } => b, Switch { targets: ref mut b, .. } => b,
SwitchInt { targets: ref mut b, .. } => b, SwitchInt { targets: ref mut b, .. } => b,
Diverge => &mut [],
Resume => &mut [], Resume => &mut [],
Return => &mut [], Return => &mut [],
Call { targets: ref mut b, .. } => b.as_mut_slice(), Call { targets: ref mut b, .. } => b.as_mut_slice(),
@ -350,12 +337,24 @@ impl<'tcx> Terminator<'tcx> {
} }
impl<'tcx> BasicBlockData<'tcx> { impl<'tcx> BasicBlockData<'tcx> {
pub fn new(terminator: Terminator<'tcx>) -> BasicBlockData<'tcx> { pub fn new(terminator: Option<Terminator<'tcx>>) -> BasicBlockData<'tcx> {
BasicBlockData { BasicBlockData {
statements: vec![], statements: vec![],
terminator: terminator, 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> { impl<'tcx> Debug for Terminator<'tcx> {
@ -396,7 +395,6 @@ impl<'tcx> Terminator<'tcx> {
If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv), If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv),
Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv), Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv),
SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
Diverge => write!(fmt, "diverge"),
Return => write!(fmt, "return"), Return => write!(fmt, "return"),
Resume => write!(fmt, "resume"), Resume => write!(fmt, "resume"),
Call { .. } => { Call { .. } => {
@ -414,7 +412,7 @@ impl<'tcx> Terminator<'tcx> {
pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> { pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
use self::Terminator::*; use self::Terminator::*;
match *self { match *self {
Diverge | Return | Resume => vec![], Return | Resume => vec![],
Goto { .. } => vec!["".into_cow()], Goto { .. } => vec!["".into_cow()],
If { .. } => vec!["true".into_cow(), "false".into_cow()], If { .. } => vec!["true".into_cow(), "false".into_cow()],
Call { .. } => vec!["return".into_cow(), "unwind".into_cow()], Call { .. } => vec!["return".into_cow(), "unwind".into_cow()],

View File

@ -84,7 +84,7 @@ pub trait Visitor<'tcx> {
for statement in &data.statements { for statement in &data.statements {
self.visit_statement(block, statement); 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>) { fn super_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) {
@ -132,7 +132,6 @@ pub trait Visitor<'tcx> {
} }
} }
Terminator::Diverge |
Terminator::Resume | Terminator::Resume |
Terminator::Return => { Terminator::Return => {
} }
@ -374,7 +373,7 @@ pub trait MutVisitor<'tcx> {
for statement in &mut data.statements { for statement in &mut data.statements {
self.visit_statement(block, statement); 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, fn super_statement(&mut self,
@ -429,7 +428,6 @@ pub trait MutVisitor<'tcx> {
} }
} }
Terminator::Diverge |
Terminator::Resume | Terminator::Resume |
Terminator::Return => { Terminator::Return => {
} }

View File

@ -28,7 +28,7 @@ impl<'tcx> CFG<'tcx> {
pub fn start_new_block(&mut self) -> BasicBlock { pub fn start_new_block(&mut self) -> BasicBlock {
let node_index = self.basic_blocks.len(); 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) BasicBlock::new(node_index)
} }
@ -67,15 +67,9 @@ impl<'tcx> CFG<'tcx> {
pub fn terminate(&mut self, pub fn terminate(&mut self,
block: BasicBlock, block: BasicBlock,
terminator: Terminator<'tcx>) { terminator: Terminator<'tcx>) {
// Check whether this block has already been terminated. For debug_assert!(self.block_data(block).terminator.is_none(),
// 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 },
"terminate: block {:?} already has a terminator set", block); "terminate: block {:?} already has a terminator set", block);
self.block_data_mut(block).terminator = Some(terminator);
self.block_data_mut(block).terminator = terminator;
} }
} }

View File

@ -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(), START_BLOCK);
assert_eq!(builder.cfg.start_new_block(), END_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 mut block = START_BLOCK;
let arg_decls = unpack!(block = builder.args_and_body(block, let arg_decls = unpack!(block = builder.args_and_body(block,

View File

@ -62,7 +62,7 @@ fn write_node<W: Write>(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<(
// Terminator head at the bottom, not including the list of successor blocks. Those will be // Terminator head at the bottom, not including the list of successor blocks. Those will be
// displayed as labels on the edges between blocks. // displayed as labels on the edges between blocks.
let mut terminator_head = String::new(); 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))); try!(write!(w, r#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head)));
// Close the table, node label, and the node itself. // Close the table, node label, and the node itself.
@ -71,7 +71,7 @@ fn write_node<W: Write>(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. /// 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<()> { 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(); let labels = terminator.fmt_successor_labels();
for (&target, label) in terminator.successors().iter().zip(labels) { for (&target, label) in terminator.successors().iter().zip(labels) {

View File

@ -59,7 +59,7 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> {
self.erase_regions_statement(statement); 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, fn erase_regions_statement(&mut self,
@ -79,7 +79,6 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> {
terminator: &mut Terminator<'tcx>) { terminator: &mut Terminator<'tcx>) {
match *terminator { match *terminator {
Terminator::Goto { .. } | Terminator::Goto { .. } |
Terminator::Diverge |
Terminator::Resume | Terminator::Resume |
Terminator::Return => { Terminator::Return => {
/* nothing to do */ /* nothing to do */

View File

@ -10,7 +10,6 @@
use rustc::middle::const_eval::ConstVal; use rustc::middle::const_eval::ConstVal;
use rustc::mir::repr::*; use rustc::mir::repr::*;
use std::mem;
use transform::util; use transform::util;
use transform::MirPass; use transform::MirPass;
@ -27,11 +26,10 @@ impl SimplifyCfg {
// These blocks are always required. // These blocks are always required.
seen[START_BLOCK.index()] = true; seen[START_BLOCK.index()] = true;
seen[END_BLOCK.index()] = true; seen[END_BLOCK.index()] = true;
seen[DIVERGE_BLOCK.index()] = true;
let mut worklist = vec![START_BLOCK]; let mut worklist = vec![START_BLOCK];
while let Some(bb) = worklist.pop() { 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()] { if !seen[succ.index()] {
seen[succ.index()] = true; seen[succ.index()] = true;
worklist.push(*succ); worklist.push(*succ);
@ -51,7 +49,7 @@ impl SimplifyCfg {
while mir.basic_block_data(target).statements.is_empty() { while mir.basic_block_data(target).statements.is_empty() {
match mir.basic_block_data(target).terminator { match mir.basic_block_data(target).terminator {
Terminator::Goto { target: next } => { Some(Terminator::Goto { target: next }) => {
if seen.contains(&next) { if seen.contains(&next) {
return None; return None;
} }
@ -67,9 +65,9 @@ impl SimplifyCfg {
let mut changed = false; let mut changed = false;
for bb in mir.all_basic_blocks() { for bb in mir.all_basic_blocks() {
// Temporarily swap out the terminator we're modifying to keep borrowck happy // Temporarily take ownership of the terminator we're modifying to keep borrowck happy
let mut terminator = Terminator::Diverge; let mut terminator = mir.basic_block_data_mut(bb).terminator.take()
mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator); .expect("invalid terminator state");
for target in terminator.successors_mut() { for target in terminator.successors_mut() {
let new_target = match final_target(mir, *target) { let new_target = match final_target(mir, *target) {
@ -80,10 +78,8 @@ impl SimplifyCfg {
changed |= *target != new_target; changed |= *target != new_target;
*target = new_target; *target = new_target;
} }
mir.basic_block_data_mut(bb).terminator = Some(terminator);
mir.basic_block_data_mut(bb).terminator = terminator;
} }
changed changed
} }
@ -91,11 +87,10 @@ impl SimplifyCfg {
let mut changed = false; let mut changed = false;
for bb in mir.all_basic_blocks() { for bb in mir.all_basic_blocks() {
// Temporarily swap out the terminator we're modifying to keep borrowck happy let basic_block = mir.basic_block_data_mut(bb);
let mut terminator = Terminator::Diverge; let mut terminator = basic_block.terminator_mut();
mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator);
mir.basic_block_data_mut(bb).terminator = match terminator { *terminator = match *terminator {
Terminator::If { ref targets, .. } if targets.0 == targets.1 => { Terminator::If { ref targets, .. } if targets.0 == targets.1 => {
changed = true; changed = true;
Terminator::Goto { target: targets.0 } Terminator::Goto { target: targets.0 }
@ -115,7 +110,7 @@ impl SimplifyCfg {
Terminator::SwitchInt { ref targets, .. } if targets.len() == 1 => { Terminator::SwitchInt { ref targets, .. } if targets.len() == 1 => {
Terminator::Goto { target: targets[0] } Terminator::Goto { target: targets[0] }
} }
_ => terminator _ => continue
} }
} }
@ -131,7 +126,6 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg {
changed |= self.remove_goto_chains(mir); changed |= self.remove_goto_chains(mir);
self.remove_dead_blocks(mir); self.remove_dead_blocks(mir);
} }
// FIXME: Should probably be moved into some kind of pass manager // FIXME: Should probably be moved into some kind of pass manager
mir.basic_blocks.shrink_to_fit(); mir.basic_blocks.shrink_to_fit();
} }

View File

@ -15,7 +15,7 @@ use rustc::mir::repr::*;
/// in a single pass /// in a single pass
pub fn update_basic_block_ids(mir: &mut Mir, replacements: &[BasicBlock]) { pub fn update_basic_block_ids(mir: &mut Mir, replacements: &[BasicBlock]) {
for bb in mir.all_basic_blocks() { 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()]; *target = replacements[target.index()];
} }
} }

View File

@ -33,9 +33,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
bcx = self.trans_statement(bcx, statement); 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 } => { mir::Terminator::Goto { target } => {
build::Br(bcx, self.llblock(target), DebugLoc::None) 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 => { mir::Terminator::Resume => {
if let Some(llpersonalityslot) = self.llpersonalityslot { if let Some(llpersonalityslot) = self.llpersonalityslot {
let lp = build::Load(bcx, llpersonalityslot); let lp = build::Load(bcx, llpersonalityslot);

View File

@ -125,16 +125,11 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) {
// Translate the body of each block // Translate the body of each block
for &bb in &mir_blocks { for &bb in &mir_blocks {
if bb != mir::DIVERGE_BLOCK { // NB that we do not handle the Resume terminator specially, because a block containing
mircx.trans_block(bb); // 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 /// Produce, for each argument, a `ValueRef` pointing at the