Skip to content

Commit

Permalink
fix translation of terminators in MSVC cleanup blocks
Browse files Browse the repository at this point in the history
MSVC requires unwinding code to be split to a tree of *funclets*, where each funclet
can only branch to itself or to to its parent.

Luckily, the code we generates matches this pattern. Recover that structure in
an analyze pass and translate according to that.
  • Loading branch information
arielb1 committed Jun 4, 2016
1 parent 506086e commit 1ae7ae0
Show file tree
Hide file tree
Showing 7 changed files with 347 additions and 105 deletions.
6 changes: 4 additions & 2 deletions src/librustc_borrowck/borrowck/mir/elaborate_drops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -998,9 +998,11 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
continue
}
TerminatorKind::DropAndReplace { .. } => {
// this contains the consume of the source and
// this contains the move of the source and
// the initialization of the destination. We
// only want the latter
// only want the former - the latter is handled
// by the elaboration code and must be done
// *after* the destination is dropped.
assert!(self.patch.is_patched(bb));
allow_initializations = false;
}
Expand Down
24 changes: 23 additions & 1 deletion src/librustc_trans/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,15 @@ impl<'blk, 'tcx> BlockS<'blk, 'tcx> {
self.lpad.get()
}

pub fn set_lpad_ref(&self, lpad: Option<&'blk LandingPad>) {
// FIXME: use an IVar?
self.lpad.set(lpad);
}

pub fn set_lpad(&self, lpad: Option<LandingPad>) {
self.set_lpad_ref(lpad.map(|p| &*self.fcx().lpad_arena.alloc(p)))
}

pub fn mir(&self) -> CachedMir<'blk, 'tcx> {
self.fcx.mir()
}
Expand Down Expand Up @@ -716,7 +725,16 @@ impl<'blk, 'tcx> BlockAndBuilder<'blk, 'tcx> {
}

pub fn set_lpad(&self, lpad: Option<LandingPad>) {
self.bcx.lpad.set(lpad.map(|p| &*self.fcx().lpad_arena.alloc(p)))
self.bcx.set_lpad(lpad)
}

pub fn set_lpad_ref(&self, lpad: Option<&'blk LandingPad>) {
// FIXME: use an IVar?
self.bcx.set_lpad_ref(lpad);
}

pub fn lpad(&self) -> Option<&'blk LandingPad> {
self.bcx.lpad()
}
}

Expand Down Expand Up @@ -761,6 +779,10 @@ impl LandingPad {
pub fn bundle(&self) -> Option<&OperandBundleDef> {
self.operand.as_ref()
}

pub fn cleanuppad(&self) -> Option<ValueRef> {
self.cleanuppad
}
}

impl Clone for LandingPad {
Expand Down
103 changes: 103 additions & 0 deletions src/librustc_trans/mir/analyze.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@

use rustc_data_structures::bitvec::BitVector;
use rustc::mir::repr as mir;
use rustc::mir::repr::TerminatorKind;
use rustc::mir::visit::{Visitor, LvalueContext};
use rustc_mir::traversal;
use common::{self, Block, BlockAndBuilder};
use super::rvalue;

Expand Down Expand Up @@ -134,3 +136,104 @@ impl<'mir, 'bcx, 'tcx> Visitor<'tcx> for TempAnalyzer<'mir, 'bcx, 'tcx> {
self.super_lvalue(lvalue, context);
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum CleanupKind {
NotCleanup,
Funclet,
Internal { funclet: mir::BasicBlock }
}

pub fn cleanup_kinds<'bcx,'tcx>(_bcx: Block<'bcx,'tcx>,
mir: &mir::Mir<'tcx>)
-> Vec<CleanupKind>
{
fn discover_masters<'tcx>(result: &mut [CleanupKind], mir: &mir::Mir<'tcx>) {
for bb in mir.all_basic_blocks() {
let data = mir.basic_block_data(bb);
match data.terminator().kind {
TerminatorKind::Goto { .. } |
TerminatorKind::Resume |
TerminatorKind::Return |
TerminatorKind::If { .. } |
TerminatorKind::Switch { .. } |
TerminatorKind::SwitchInt { .. } => {
/* nothing to do */
}
TerminatorKind::Call { cleanup: unwind, .. } |
TerminatorKind::DropAndReplace { unwind, .. } |
TerminatorKind::Drop { unwind, .. } => {
if let Some(unwind) = unwind {
debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet",
bb, data, unwind);
result[unwind.index()] = CleanupKind::Funclet;
}
}
}
}
}

fn propagate<'tcx>(result: &mut [CleanupKind], mir: &mir::Mir<'tcx>) {
let mut funclet_succs : Vec<_> =
mir.all_basic_blocks().iter().map(|_| None).collect();

let mut set_successor = |funclet: mir::BasicBlock, succ| {
match funclet_succs[funclet.index()] {
ref mut s @ None => {
debug!("set_successor: updating successor of {:?} to {:?}",
funclet, succ);
*s = Some(succ);
},
Some(s) => if s != succ {
span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}",
funclet, s, succ);
}
}
};

for (bb, data) in traversal::reverse_postorder(mir) {
let funclet = match result[bb.index()] {
CleanupKind::NotCleanup => continue,
CleanupKind::Funclet => bb,
CleanupKind::Internal { funclet } => funclet,
};

debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}",
bb, data, result[bb.index()], funclet);

for &succ in data.terminator().successors().iter() {
let kind = result[succ.index()];
debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}",
funclet, succ, kind);
match kind {
CleanupKind::NotCleanup => {
result[succ.index()] = CleanupKind::Internal { funclet: funclet };
}
CleanupKind::Funclet => {
set_successor(funclet, succ);
}
CleanupKind::Internal { funclet: succ_funclet } => {
if funclet != succ_funclet {
// `succ` has 2 different funclet going into it, so it must
// be a funclet by itself.

debug!("promoting {:?} to a funclet and updating {:?}", succ,
succ_funclet);
result[succ.index()] = CleanupKind::Funclet;
set_successor(succ_funclet, succ);
set_successor(funclet, succ);
}
}
}
}
}
}

let mut result : Vec<_> =
mir.all_basic_blocks().iter().map(|_| CleanupKind::NotCleanup).collect();

discover_masters(&mut result, mir);
propagate(&mut result, mir);
debug!("cleanup_kinds: result={:?}", result);
result
}
Loading

0 comments on commit 1ae7ae0

Please sign in to comment.