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> { impl<I: Idx, T> Extend<T> for IndexVec<I, T> {
#[inline] #[inline]
fn extend<J: IntoIterator<Item = T>>(&mut self, iter: J) { 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 { pub fn val_ty(v: ValueRef) -> Type {
unsafe { unsafe {
Type::from_ref(llvm::LLVMTypeOf(v)) Type::from_ref(llvm::LLVMTypeOf(v))

View File

@ -197,6 +197,16 @@ pub enum CleanupKind {
Internal { funclet: mir::BasicBlock } 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> { pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec<mir::BasicBlock, CleanupKind> {
fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>, fn discover_masters<'tcx>(result: &mut IndexVec<mir::BasicBlock, CleanupKind>,
mir: &mir::Mir<'tcx>) { mir: &mir::Mir<'tcx>) {

View File

@ -19,8 +19,7 @@ use adt;
use base::{self, Lifetime}; use base::{self, Lifetime};
use callee; use callee;
use builder::Builder; use builder::Builder;
use common::{self, Funclet}; use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_undef};
use common::{C_bool, C_str_slice, C_struct, C_u32, C_undef};
use consts; use consts;
use machine::llalign_of_min; use machine::llalign_of_min;
use meth; use meth;
@ -28,95 +27,88 @@ use monomorphize;
use type_of; use type_of;
use type_::Type; use type_::Type;
use rustc_data_structures::indexed_vec::IndexVec;
use syntax::symbol::Symbol; use syntax::symbol::Symbol;
use std::cmp; use std::cmp;
use super::{MirContext, LocalRef}; use super::{MirContext, LocalRef};
use super::analyze::CleanupKind;
use super::constant::Const; use super::constant::Const;
use super::lvalue::{Alignment, LvalueRef}; use super::lvalue::{Alignment, LvalueRef};
use super::operand::OperandRef; use super::operand::OperandRef;
use super::operand::OperandValue::{Pair, Ref, Immediate}; use super::operand::OperandValue::{Pair, Ref, Immediate};
impl<'a, 'tcx> MirContext<'a, 'tcx> { impl<'a, 'tcx> MirContext<'a, 'tcx> {
pub fn trans_block(&mut self, bb: mir::BasicBlock, pub fn trans_block(&mut self, bb: mir::BasicBlock) {
funclets: &IndexVec<mir::BasicBlock, Option<Funclet>>) {
let mut bcx = self.get_builder(bb); let mut bcx = self.get_builder(bb);
let data = &self.mir[bb]; let data = &self.mir[bb];
debug!("trans_block({:?}={:?})", bb, data); 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 { for statement in &data.statements {
bcx = self.trans_statement(bcx, statement); 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, fn trans_terminator(&mut self,
mut bcx: Builder<'a, 'tcx>, mut bcx: Builder<'a, 'tcx>,
bb: mir::BasicBlock, bb: mir::BasicBlock,
terminator: &mir::Terminator<'tcx>, terminator: &mir::Terminator<'tcx>)
funclet: Option<&Funclet>)
{ {
debug!("trans_terminator: {:?}", terminator); debug!("trans_terminator: {:?}", terminator);
// Create the cleanup bundle, if needed. // Create the cleanup bundle, if needed.
let tcx = bcx.tcx(); 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_pad = funclet.map(|lp| lp.cleanuppad());
let cleanup_bundle = funclet.map(|l| l.bundle()); let cleanup_bundle = funclet.map(|l| l.bundle());
let funclet_br = |this: &Self, bcx: Builder, bb: mir::BasicBlock| { let lltarget = |this: &mut Self, target: mir::BasicBlock| {
let lltarget = this.blocks[bb]; let lltarget = this.blocks[target];
if let Some(cp) = cleanup_pad { let target_funclet = this.cleanup_kinds[target].funclet_bb(target);
match this.cleanup_kinds[bb] { match (funclet_bb, target_funclet) {
CleanupKind::Funclet => { (None, None) => (lltarget, false),
// micro-optimization: generate a `ret` rather than a jump (Some(f), Some(t_f))
// to a return block if f == t_f || !base::wants_msvc_seh(tcx.sess)
bcx.cleanup_ret(cp, Some(lltarget)); => (lltarget, false),
} (None, Some(_)) => {
CleanupKind::Internal { .. } => bcx.br(lltarget), // jump *into* cleanup - need a landing pad if GNU
CleanupKind::NotCleanup => bug!("jump from cleanup bb to bb {:?}", bb) (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 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 { debug!("llblock: creating cleanup trampoline for {:?}", target);
match this.cleanup_kinds[target] { let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target);
CleanupKind::Funclet => { let trampoline = this.new_block(name);
// MSVC cross-funclet jump - need a trampoline trampoline.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget));
trampoline.llbb()
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)
}
} else { } else {
if let (CleanupKind::NotCleanup, CleanupKind::Funclet) = lltarget
(this.cleanup_kinds[bb], this.cleanup_kinds[target]) }
{ };
// jump *into* cleanup - need a landing pad if GNU
this.landing_pad_to(target) let funclet_br = |this: &mut Self, bcx: Builder, target: mir::BasicBlock| {
} else { let (lltarget, is_cleanupret) = lltarget(this, target);
lltarget 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); self.set_debug_loc(&bcx, terminator.source_info);
match terminator.kind { match terminator.kind {
mir::TerminatorKind::Resume => { mir::TerminatorKind::Resume => {
@ -752,7 +743,7 @@ impl<'a, 'tcx> MirContext<'a, 'tcx> {
fn landing_pad_uncached(&mut self, target_bb: BasicBlockRef) -> BasicBlockRef { fn landing_pad_uncached(&mut self, target_bb: BasicBlockRef) -> BasicBlockRef {
if base::wants_msvc_seh(self.ccx.sess()) { 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"); 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 /// The funclet status of each basic block
cleanup_kinds: IndexVec<mir::BasicBlock, analyze::CleanupKind>, 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 /// This stores the landing-pad block for a given BB, computed lazily on GNU
/// and eagerly on MSVC. /// and eagerly on MSVC.
landing_pads: IndexVec<mir::BasicBlock, Option<BasicBlockRef>>, 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); debuginfo::create_function_debug_context(ccx, instance, sig, llfn, mir);
let bcx = Builder::new_block(ccx, llfn, "start"); 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 // Allocate a `Block` for every basic block, except
// the start block, if nothing loops back to it. // the start block, if nothing loops back to it.
let reentrant_start_block = !mir.predecessors_for(mir::START_BLOCK).is_empty(); 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. // Compute debuginfo scopes from MIR scopes.
let scopes = debuginfo::create_mir_scopes(ccx, mir, &debug_context); 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 { let mut mircx = MirContext {
mir: mir, mir: mir,
@ -228,7 +236,8 @@ pub fn trans_mir<'a, 'tcx: 'a>(
blocks: block_bcxs, blocks: block_bcxs,
unreachable_block: None, unreachable_block: None,
cleanup_kinds: cleanup_kinds, cleanup_kinds: cleanup_kinds,
landing_pads: IndexVec::from_elem(None, mir.basic_blocks()), landing_pads: landing_pads,
funclets: &funclets,
scopes: scopes, scopes: scopes,
locals: IndexVec::new(), locals: IndexVec::new(),
debug_context: debug_context, debug_context: debug_context,
@ -306,28 +315,13 @@ pub fn trans_mir<'a, 'tcx: 'a>(
// emitting should be enabled. // emitting should be enabled.
debuginfo::start_emitting_source_locations(&mircx.debug_context); 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 rpo = traversal::reverse_postorder(&mir);
let mut visited = BitVector::new(mir.basic_blocks().len()); let mut visited = BitVector::new(mir.basic_blocks().len());
// Translate the body of each block using reverse postorder // Translate the body of each block using reverse postorder
for (bb, _) in rpo { for (bb, _) in rpo {
visited.insert(bb.index()); visited.insert(bb.index());
mircx.trans_block(bb, &funclets); mircx.trans_block(bb);
} }
// Remove blocks that haven't been visited, or have no // 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 /// Produce, for each argument, a `ValueRef` pointing at the
/// argument's value. As arguments are lvalues, these are always /// argument's value. As arguments are lvalues, these are always
/// indirect. /// indirect.