fix translation of MSVC funclets that loop to their own start
This commit is contained in:
parent
6adfbaf2d3
commit
ee982d4355
@ -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) {
|
||||||
|
@ -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))
|
||||||
|
@ -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>) {
|
||||||
|
@ -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");
|
||||||
|
@ -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.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user