mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-24 07:44:10 +00:00
Remove diverge terminator
Unreachable terminator can be contained all within the trans.
This commit is contained in:
parent
5b34690842
commit
4e86dcdb72
@ -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<BasicBlock> {
|
||||
(0..self.basic_blocks.len())
|
||||
@ -194,7 +191,7 @@ impl Debug for BasicBlock {
|
||||
#[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)]
|
||||
@ -237,14 +234,6 @@ pub enum Terminator<'tcx> {
|
||||
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,
|
||||
@ -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<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> {
|
||||
@ -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<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()],
|
||||
|
@ -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 => {
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
// 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.
|
||||
@ -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.
|
||||
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) {
|
||||
|
@ -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 */
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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()];
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
// 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
|
||||
|
Loading…
Reference in New Issue
Block a user