fix translation of MSVC funclets that loop to their own start

This commit is contained in:
Ariel Ben-Yehuda 2017-05-23 23:47:15 +03:00
parent 6adfbaf2d3
commit ee982d4355
5 changed files with 93 additions and 80 deletions

View File

@ -212,6 +212,13 @@ impl<I: Idx, T> IndexMut<I> for IndexVec<I, T> {
}
}
impl<I: Idx, T> Default for IndexVec<I, T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<I: Idx, T> Extend<T> for IndexVec<I, T> {
#[inline]
fn extend<J: IntoIterator<Item = T>>(&mut self, iter: J) {

View File

@ -191,15 +191,6 @@ impl Funclet {
}
}
impl Clone for Funclet {
fn clone(&self) -> Funclet {
Funclet {
cleanuppad: self.cleanuppad,
operand: OperandBundleDef::new("funclet", &[self.cleanuppad]),
}
}
}
pub fn val_ty(v: ValueRef) -> Type {
unsafe {
Type::from_ref(llvm::LLVMTypeOf(v))

View File

@ -197,6 +197,16 @@ pub enum CleanupKind {
Internal { funclet: mir::BasicBlock }
}
impl CleanupKind {
pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option<mir::BasicBlock> {
match self {
CleanupKind::NotCleanup => None,
CleanupKind::Funclet => Some(for_bb),
CleanupKind::Internal { funclet } => Some(funclet),
}
}
}
pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> {
fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
mir: &mir::Mir<'tcx>) {

View File

@ -19,8 +19,7 @@ use adt;
use base::{self, Lifetime};
use callee;
use builder::Builder;
use common::{self, Funclet};
use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef};
use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_undef};
use consts;
use machine::llalign_of_min;
use meth;
@ -28,95 +27,88 @@ use monomorphize;
use type_of;
use type_::Type;
use rustc_data_structures::indexed_vec::IndexVec;
use syntax::symbol::Symbol;
use std::cmp;
use super::{MirContext, LocalRef};
use super::analyze::CleanupKind;
use super::constant::Const;
use super::lvalue::{Alignment, LvalueRef};
use super::operand::OperandRef;
use super::operand::OperandValue::{Pair, Ref, Immediate};
impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_block(&mut self, bb: mir::BasicBlock,
funclets: &IndexVec<mir::BasicBlock, Option<Funclet>>) {
pub fn trans_block(&mut self, bb: mir::BasicBlock) {
let mut bcx = self.get_builder(bb);
let data = &self.mir[bb];
debug!("trans_block({:?}={:?})", bb, data);
let funclet = match self.cleanup_kinds[bb] {
CleanupKind::Internal { funclet } => funclets[funclet].as_ref(),
_ => funclets[bb].as_ref(),
};
for statement in &data.statements {
bcx = self.trans_statement(bcx, statement);
}
self.trans_terminator(bcx, bb, data.terminator(), funclet);
self.trans_terminator(bcx, bb, data.terminator());
}
fn trans_terminator(&mut self,
mut bcx: Builder<'a, 'tcx>,
bb: mir::BasicBlock,
terminator: &mir::Terminator<'tcx>,
funclet: Option<&Funclet>)
terminator: &mir::Terminator<'tcx>)
{
debug!("trans_terminator: {:?}", terminator);
// Create the cleanup bundle, if needed.
let tcx = bcx.tcx();
let span = terminator.source_info.span;
let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb);
let funclet = funclet_bb.and_then(|funclet_bb| self.funclets[funclet_bb].as_ref());
let cleanup_pad = funclet.map(|lp| lp.cleanuppad());
let cleanup_bundle = funclet.map(|l| l.bundle());
let funclet_br = |this: &Self, bcx: Builder, bb: mir::BasicBlock| {
let lltarget = this.blocks[bb];
if let Some(cp) = cleanup_pad {
match this.cleanup_kinds[bb] {
CleanupKind::Funclet => {
// micro-optimization: generate a `ret` rather than a jump
// to a return block
bcx.cleanup_ret(cp, Some(lltarget));
}
CleanupKind::Internal { .. } => bcx.br(lltarget),
CleanupKind::NotCleanup => bug!("jump from cleanup bb to bb {:?}", bb)
let lltarget = |this: &mut Self, target: mir::BasicBlock| {
let lltarget = this.blocks[target];
let target_funclet = this.cleanup_kinds[target].funclet_bb(target);
match (funclet_bb, target_funclet) {
(None, None) => (lltarget, false),
(Some(f), Some(t_f))
if f == t_f || !base::wants_msvc_seh(tcx.sess)
=> (lltarget, false),
(None, Some(_)) => {
// jump *into* cleanup - need a landing pad if GNU
(this.landing_pad_to(target), false)
}
(Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", terminator),
(Some(_), Some(_)) => {
(this.landing_pad_to(target), true)
}
} else {
bcx.br(lltarget);
}
};
let llblock = |this: &mut Self, target: mir::BasicBlock| {
let lltarget = this.blocks[target];
let (lltarget, is_cleanupret) = lltarget(this, target);
if is_cleanupret {
// MSVC cross-funclet jump - need a trampoline
if let Some(cp) = cleanup_pad {
match this.cleanup_kinds[target] {
CleanupKind::Funclet => {
// MSVC cross-funclet jump - need a trampoline
debug!("llblock: creating cleanup trampoline for {:?}", target);
let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target);
let trampoline = this.new_block(name);
trampoline.cleanup_ret(cp, Some(lltarget));
trampoline.llbb()
}
CleanupKind::Internal { .. } => lltarget,
CleanupKind::NotCleanup =>
bug!("jump from cleanup bb {:?} to bb {:?}", bb, target)
}
debug!("llblock: creating cleanup trampoline for {:?}", target);
let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target);
let trampoline = this.new_block(name);
trampoline.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget));
trampoline.llbb()
} else {
if let (CleanupKind::NotCleanup, CleanupKind::Funclet) =
(this.cleanup_kinds[bb], this.cleanup_kinds[target])
{
// jump *into* cleanup - need a landing pad if GNU
this.landing_pad_to(target)
} else {
lltarget
}
lltarget
}
};
let funclet_br = |this: &mut Self, bcx: Builder, target: mir::BasicBlock| {
let (lltarget, is_cleanupret) = lltarget(this, target);
if is_cleanupret {
// micro-optimization: generate a `ret` rather than a jump
// to a trampoline.
bcx.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget));
} else {
bcx.br(lltarget);
}
};
@ -168,7 +160,6 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
}
};
let span = terminator.source_info.span;
self.set_debug_loc(&bcx, terminator.source_info);
match terminator.kind {
mir::TerminatorKind::Resume => {
@ -752,7 +743,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
fn landing_pad_uncached(&mut self, target_bb: BasicBlockRef) -> BasicBlockRef {
if base::wants_msvc_seh(self.ccx.sess()) {
return target_bb;
span_bug!(self.mir.span, "landing pad was not inserted?")
}
let bcx = self.new_block("cleanup");

View File

@ -69,6 +69,10 @@ pub struct MirContext<'a, 'tcx:'a> {
/// The funclet status of each basic block
cleanup_kinds: IndexVec<mir::BasicBlock, analyze::CleanupKind>,
/// When targeting MSVC, this stores the cleanup info for each funclet
/// BB. This is initialized as we compute the funclets' head block in RPO.
funclets: &'a IndexVec<mir::BasicBlock, Option<Funclet>>,
/// This stores the landing-pad block for a given BB, computed lazily on GNU
/// and eagerly on MSVC.
landing_pads: IndexVec<mir::BasicBlock, Option<BasicBlockRef>>,
@ -202,8 +206,11 @@ pub fn trans_mir<'a, 'tcx: 'a>(
debuginfo::create_function_debug_context(ccx, instance, sig, llfn, mir);
let bcx = Builder::new_block(ccx, llfn, "start");
let cleanup_kinds = analyze::cleanup_kinds(&mir);
if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) {
bcx.set_personality_fn(ccx.eh_personality());
}
let cleanup_kinds = analyze::cleanup_kinds(&mir);
// Allocate a `Block` for every basic block, except
// the start block, if nothing loops back to it.
let reentrant_start_block = !mir.predecessors_for(mir::START_BLOCK).is_empty();
@ -218,6 +225,7 @@ pub fn trans_mir<'a, 'tcx: 'a>(
// Compute debuginfo scopes from MIR scopes.
let scopes = debuginfo::create_mir_scopes(ccx, mir, &debug_context);
let (landing_pads, funclets) = create_funclets(&bcx, &cleanup_kinds, &block_bcxs);
let mut mircx = MirContext {
mir: mir,
@ -228,7 +236,8 @@ pub fn trans_mir<'a, 'tcx: 'a>(
blocks: block_bcxs,
unreachable_block: None,
cleanup_kinds: cleanup_kinds,
landing_pads: IndexVec::from_elem(None, mir.basic_blocks()),
landing_pads: landing_pads,
funclets: &funclets,
scopes: scopes,
locals: IndexVec::new(),
debug_context: debug_context,
@ -306,28 +315,13 @@ pub fn trans_mir<'a, 'tcx: 'a>(
// emitting should be enabled.
debuginfo::start_emitting_source_locations(&mircx.debug_context);
let funclets: IndexVec<mir::BasicBlock, Option<Funclet>> =
mircx.cleanup_kinds.iter_enumerated().map(|(bb, cleanup_kind)| {
if let CleanupKind::Funclet = *cleanup_kind {
let bcx = mircx.get_builder(bb);
unsafe {
llvm::LLVMSetPersonalityFn(mircx.llfn, mircx.ccx.eh_personality());
}
if base::wants_msvc_seh(ccx.sess()) {
return Some(Funclet::new(bcx.cleanup_pad(None, &[])));
}
}
None
}).collect();
let rpo = traversal::reverse_postorder(&mir);
let mut visited = BitVector::new(mir.basic_blocks().len());
// Translate the body of each block using reverse postorder
for (bb, _) in rpo {
visited.insert(bb.index());
mircx.trans_block(bb, &funclets);
mircx.trans_block(bb);
}
// Remove blocks that haven't been visited, or have no
@ -343,6 +337,26 @@ pub fn trans_mir<'a, 'tcx: 'a>(
}
}
fn create_funclets<'a, 'tcx>(
bcx: &Builder<'a, 'tcx>,
cleanup_kinds: &IndexVec<mir::BasicBlock, CleanupKind>,
block_bcxs: &IndexVec<mir::BasicBlock, BasicBlockRef>)
-> (IndexVec<mir::BasicBlock, Option<BasicBlockRef>>,
IndexVec<mir::BasicBlock, Option<Funclet>>)
{
block_bcxs.iter_enumerated().zip(cleanup_kinds).map(|((bb, &llbb), cleanup_kind)| {
match *cleanup_kind {
CleanupKind::Funclet if base::wants_msvc_seh(bcx.sess()) => {
let cleanup_bcx = bcx.build_sibling_block(&format!("funclet_{:?}", bb));
let cleanup = cleanup_bcx.cleanup_pad(None, &[]);
cleanup_bcx.br(llbb);
(Some(cleanup_bcx.llbb()), Some(Funclet::new(cleanup)))
}
_ => (None, None)
}
}).unzip()
}
/// Produce, for each argument, a `ValueRef` pointing at the
/// argument's value. As arguments are lvalues, these are always
/// indirect.