diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 0e641bf679d11..5b887db7ec08e 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -22,7 +22,7 @@ extern crate rustc_front; use build; use graphviz; use pretty; -use transform::simplify_cfg; +use transform::{simplify_cfg, no_landing_pads}; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use hair::cx::Cx; @@ -148,6 +148,7 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> { match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) { Ok(mut mir) => { + no_landing_pads::NoLandingPads.run_on_mir(&mut mir, self.tcx); simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx); let meta_item_list = self.attr diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 41ea4469734f7..017cf3607161a 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -10,4 +10,5 @@ pub mod simplify_cfg; pub mod erase_regions; +pub mod no_landing_pads; mod util; diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs new file mode 100644 index 0000000000000..d0ea9f10f2e29 --- /dev/null +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -0,0 +1,49 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! This pass removes the unwind branch of all the terminators when the no-landing-pads option is +//! specified. + +use rustc::middle::ty; +use rustc::mir::repr::*; +use rustc::mir::visit::MutVisitor; +use rustc::mir::transform::MirPass; + +pub struct NoLandingPads; + +impl<'tcx> MutVisitor<'tcx> for NoLandingPads { + fn visit_terminator(&mut self, bb: BasicBlock, terminator: &mut Terminator<'tcx>) { + match *terminator { + Terminator::Goto { .. } | + Terminator::Resume | + Terminator::Return | + Terminator::If { .. } | + Terminator::Switch { .. } | + Terminator::SwitchInt { .. } => { + /* nothing to do */ + }, + Terminator::Drop { ref mut unwind, .. } => { + unwind.take(); + }, + Terminator::Call { ref mut cleanup, .. } => { + cleanup.take(); + }, + } + self.super_terminator(bb, terminator); + } +} + +impl MirPass for NoLandingPads { + fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) { + if tcx.sess.no_landing_pads() { + self.visit_mir(mir); + } + } +} diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 1269c266c7c10..ca37052991824 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -767,6 +767,10 @@ impl<'blk, 'tcx> BlockAndBuilder<'blk, 'tcx> { { self.bcx.monomorphize(value) } + + pub fn set_lpad(&self, lpad: Option) { + self.bcx.lpad.set(lpad.map(|p| &*self.fcx().lpad_arena.alloc(p))) + } } impl<'blk, 'tcx> Deref for BlockAndBuilder<'blk, 'tcx> { diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index b3b8214a9a70d..b5e09cb9c802f 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::{BasicBlockRef, ValueRef}; +use llvm::{BasicBlockRef, ValueRef, OperandBundleDef}; use rustc::middle::ty; use rustc::mir::repr as mir; use syntax::abi::Abi; @@ -34,6 +34,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let mut bcx = self.bcx(bb); let data = self.mir.basic_block_data(bb); + // MSVC SEH bits + let (cleanup_pad, cleanup_bundle) = if let Some((cp, cb)) = self.make_cleanup_pad(bb) { + (Some(cp), Some(cb)) + } else { + (None, None) + }; + let funclet_br = |bcx: BlockAndBuilder, llbb: BasicBlockRef| if let Some(cp) = cleanup_pad { + bcx.cleanup_ret(cp, Some(llbb)); + } else { + bcx.br(llbb); + }; + for statement in &data.statements { bcx = self.trans_statement(bcx, statement); } @@ -41,8 +53,21 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debug!("trans_block: terminator: {:?}", data.terminator()); match *data.terminator() { + mir::Terminator::Resume => { + if let Some(cleanup_pad) = cleanup_pad { + bcx.cleanup_ret(cleanup_pad, None); + } else { + let ps = self.get_personality_slot(&bcx); + let lp = bcx.load(ps); + bcx.with_block(|bcx| { + base::call_lifetime_end(bcx, ps); + base::trans_unwind_resume(bcx, lp); + }); + } + } + mir::Terminator::Goto { target } => { - bcx.br(self.llblock(target)); + funclet_br(bcx, self.llblock(target)); } mir::Terminator::If { ref cond, targets: (true_bb, false_bb) } => { @@ -85,19 +110,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Resume => { - let ps = self.get_personality_slot(&bcx); - let lp = bcx.load(ps); - bcx.with_block(|bcx| { - base::call_lifetime_end(bcx, ps); - base::trans_unwind_resume(bcx, lp); - }); - } - mir::Terminator::Return => { let return_ty = bcx.monomorphize(&self.mir.return_ty); bcx.with_block(|bcx| { - base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None); + base::build_return_block(self.fcx, bcx, return_ty, DebugLoc::None); }) } @@ -106,7 +122,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let ty = lvalue.ty.to_ty(bcx.tcx()); // Double check for necessity to drop if !glue::type_needs_drop(bcx.tcx(), ty) { - bcx.br(self.llblock(target)); + funclet_br(bcx, self.llblock(target)); return; } let drop_fn = glue::get_drop_glue(bcx.ccx(), ty); @@ -123,11 +139,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { &[llvalue], self.llblock(target), unwind.llbb(), - None, + cleanup_bundle.as_ref(), None); } else { - bcx.call(drop_fn, &[llvalue], None, None); - bcx.br(self.llblock(target)); + bcx.call(drop_fn, &[llvalue], cleanup_bundle.as_ref(), None); + funclet_br(bcx, self.llblock(target)); } } @@ -180,11 +196,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - let avoid_invoke = bcx.with_block(|bcx| base::avoid_invoke(bcx)); // Many different ways to call a function handled here - match (is_foreign, avoid_invoke, cleanup, destination) { + match (is_foreign, cleanup, destination) { // The two cases below are the only ones to use LLVM’s `invoke`. - (false, false, &Some(cleanup), &None) => { + (false, &Some(cleanup), &None) => { let cleanup = self.bcx(cleanup); let landingpad = self.make_landing_pad(cleanup); let unreachable_blk = self.unreachable_block(); @@ -192,14 +207,14 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { &llargs[..], unreachable_blk.llbb, landingpad.llbb(), - None, + cleanup_bundle.as_ref(), Some(attrs)); }, - (false, false, &Some(cleanup), &Some((_, success))) => { + (false, &Some(cleanup), &Some((_, success))) => { let cleanup = self.bcx(cleanup); let landingpad = self.make_landing_pad(cleanup); let (target, postinvoke) = if must_copy_dest { - (bcx.fcx().new_block("", None).build(), Some(self.bcx(success))) + (self.fcx.new_block("", None).build(), Some(self.bcx(success))) } else { (self.bcx(success), None) }; @@ -207,7 +222,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { &llargs[..], target.llbb(), landingpad.llbb(), - None, + cleanup_bundle.as_ref(), Some(attrs)); if let Some(postinvoketarget) = postinvoke { // We translate the copy into a temporary block. The temporary block is @@ -242,14 +257,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { target.br(postinvoketarget.llbb()); } }, - (false, _, _, &None) => { - bcx.call(callee.immediate(), &llargs[..], None, Some(attrs)); + (false, _, &None) => { + bcx.call(callee.immediate(), + &llargs[..], + cleanup_bundle.as_ref(), + Some(attrs)); bcx.unreachable(); } - (false, _, _, &Some((_, target))) => { + (false, _, &Some((_, target))) => { let llret = bcx.call(callee.immediate(), &llargs[..], - None, + cleanup_bundle.as_ref(), Some(attrs)); if must_copy_dest { let (ret_dest, ret_ty) = ret_dest_ty @@ -258,10 +276,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { base::store_ty(bcx, llret, ret_dest.llval, ret_ty); }); } - bcx.br(self.llblock(target)); + funclet_br(bcx, self.llblock(target)); } // Foreign functions - (true, _, _, destination) => { + (true, _, destination) => { let (dest, _) = ret_dest_ty .expect("return destination is not set"); bcx = bcx.map_block(|bcx| { @@ -274,7 +292,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { debugloc) }); if let Some((_, target)) = *destination { - bcx.br(self.llblock(target)); + funclet_br(bcx, self.llblock(target)); } }, } @@ -297,11 +315,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } + /// Create a landingpad wrapper around the given Block. + /// + /// No-op in MSVC SEH scheme. fn make_landing_pad(&mut self, cleanup: BlockAndBuilder<'bcx, 'tcx>) -> BlockAndBuilder<'bcx, 'tcx> { - // FIXME(#30941) this doesn't handle msvc-style exceptions + if base::wants_msvc_seh(cleanup.sess()) { + return cleanup; + } let bcx = self.fcx.new_block("cleanup", None).build(); let ccx = bcx.ccx(); let llpersonality = self.fcx.eh_personality(); @@ -314,6 +337,31 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bcx } + /// Create prologue cleanuppad instruction under MSVC SEH handling scheme. + /// + /// Also handles setting some state for the original trans and creating an operand bundle for + /// function calls. + fn make_cleanup_pad(&mut self, bb: mir::BasicBlock) -> Option<(ValueRef, OperandBundleDef)> { + let bcx = self.bcx(bb); + let data = self.mir.basic_block_data(bb); + let use_funclets = base::wants_msvc_seh(bcx.sess()) && data.is_cleanup; + let cleanup_pad = if use_funclets { + bcx.set_personality_fn(self.fcx.eh_personality()); + Some(bcx.cleanup_pad(None, &[])) + } else { + None + }; + // Set the landingpad global-state for old translator, so it knows about the SEH used. + bcx.set_lpad(if let Some(cleanup_pad) = cleanup_pad { + Some(common::LandingPad::msvc(cleanup_pad)) + } else if data.is_cleanup { + Some(common::LandingPad::gnu()) + } else { + None + }); + cleanup_pad.map(|f| (f, OperandBundleDef::new("funclet", &[f]))) + } + fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> { self.unreachable_block.unwrap_or_else(|| { let bl = self.fcx.new_block("unreachable", None); diff --git a/src/test/run-fail/mir_drop_panics.rs b/src/test/run-fail/mir_drop_panics.rs index df20700e01628..1a4330523babe 100644 --- a/src/test/run-fail/mir_drop_panics.rs +++ b/src/test/run-fail/mir_drop_panics.rs @@ -9,7 +9,6 @@ // except according to those terms. #![feature(rustc_attrs)] -// ignore-msvc: FIXME(#30941) // error-pattern:panic 1 // error-pattern:drop 2 use std::io::{self, Write}; diff --git a/src/test/run-fail/mir_trans_calls_converging_drops.rs b/src/test/run-fail/mir_trans_calls_converging_drops.rs index eb399e07d859d..5927d802b4560 100644 --- a/src/test/run-fail/mir_trans_calls_converging_drops.rs +++ b/src/test/run-fail/mir_trans_calls_converging_drops.rs @@ -10,7 +10,6 @@ #![feature(rustc_attrs)] -// ignore-msvc: FIXME(#30941) // error-pattern:converging_fn called // error-pattern:0 dropped // error-pattern:exit diff --git a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs index df4ead387b91f..96a46f47eb565 100644 --- a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs +++ b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs @@ -10,7 +10,6 @@ #![feature(rustc_attrs)] -// ignore-msvc: FIXME(#30941) // error-pattern:complex called // error-pattern:dropped // error-pattern:exit diff --git a/src/test/run-fail/mir_trans_calls_diverging_drops.rs b/src/test/run-fail/mir_trans_calls_diverging_drops.rs index cbe8793cceb22..89b53b18f0619 100644 --- a/src/test/run-fail/mir_trans_calls_diverging_drops.rs +++ b/src/test/run-fail/mir_trans_calls_diverging_drops.rs @@ -10,7 +10,6 @@ #![feature(rustc_attrs)] -// ignore-msvc: FIXME(#30941) // error-pattern:diverging_fn called // error-pattern:0 dropped