rust/src/librustc_trans/cleanup.rs

163 lines
5.6 KiB
Rust
Raw Normal View History

2014-01-17 09:18:39 -06:00
// Copyright 2013-2014 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! ## The Cleanup module
//!
//! The cleanup module tracks what values need to be cleaned up as scopes
//! are exited, either via panic or just normal control flow.
//!
//! Cleanup items can be scheduled into any of the scopes on the stack.
//! Typically, when a scope is finished, we generate the cleanup code. This
//! corresponds to a normal exit from a block (for example, an expression
//! completing evaluation successfully without panic).
use llvm::BasicBlockRef;
use base;
use mir::lvalue::LvalueRef;
use rustc::mir::tcx::LvalueTy;
2016-12-31 17:00:24 -06:00
use builder::Builder;
2017-01-01 01:42:09 -06:00
use common::Funclet;
use glue;
use type_::Type;
pub struct CleanupScope<'tcx> {
2016-12-12 19:00:42 -06:00
// Cleanup to run upon scope exit.
cleanup: Option<DropValue<'tcx>>,
// Computed on creation if compiling with landing pads (!sess.no_landing_pads)
pub landing_pad: Option<BasicBlockRef>,
}
#[derive(Copy, Clone)]
pub struct DropValue<'tcx> {
val: LvalueRef<'tcx>,
skip_dtor: bool,
}
impl<'tcx> DropValue<'tcx> {
2016-12-31 17:00:24 -06:00
fn trans<'a>(&self, funclet: Option<&'a Funclet>, bcx: &Builder<'a, 'tcx>) {
glue::call_drop_glue(bcx, self.val, self.skip_dtor, funclet)
}
2016-12-19 19:19:19 -06:00
/// Creates a landing pad for the top scope. The landing pad will perform all cleanups necessary
/// for an unwind and then `resume` to continue error propagation:
///
/// landing_pad -> ... cleanups ... -> [resume]
///
/// This should only be called once per function, as it creates an alloca for the landingpad.
2016-12-31 17:00:24 -06:00
fn get_landing_pad<'a>(&self, bcx: &Builder<'a, 'tcx>) -> BasicBlockRef {
2016-12-19 19:19:19 -06:00
debug!("get_landing_pad");
let bcx = bcx.build_sibling_block("cleanup_unwind");
let llpersonality = bcx.ccx.eh_personality();
bcx.set_personality_fn(llpersonality);
2016-12-19 19:19:19 -06:00
if base::wants_msvc_seh(bcx.sess()) {
let pad = bcx.cleanup_pad(None, &[]);
let funclet = Some(Funclet::new(pad));
self.trans(funclet.as_ref(), &bcx);
2016-12-19 19:19:19 -06:00
bcx.cleanup_ret(pad, None);
2016-12-19 19:19:19 -06:00
} else {
// The landing pad return type (the type being propagated). Not sure
// what this represents but it's determined by the personality
// function and this is what the EH proposal example uses.
2016-12-31 17:00:24 -06:00
let llretty = Type::struct_(bcx.ccx, &[Type::i8p(bcx.ccx), Type::i32(bcx.ccx)], false);
2016-12-19 19:19:19 -06:00
// The only landing pad clause will be 'cleanup'
2016-12-31 17:00:24 -06:00
let llretval = bcx.landing_pad(llretty, llpersonality, 1, bcx.llfn());
2016-12-19 19:19:19 -06:00
// The landing pad block is a cleanup
bcx.set_cleanup(llretval);
2016-12-19 19:19:19 -06:00
// Insert cleanup instructions into the cleanup block
self.trans(None, &bcx);
trans: Reimplement unwinding on MSVC This commit transitions the compiler to using the new exception handling instructions in LLVM for implementing unwinding for MSVC. This affects both 32 and 64-bit MSVC as they're both now using SEH-based strategies. In terms of standard library support, lots more details about how SEH unwinding is implemented can be found in the commits. In terms of trans, this change necessitated a few modifications: * Branches were added to detect when the old landingpad instruction is used or the new cleanuppad instruction is used to `trans::cleanup`. * The return value from `cleanuppad` is not stored in an `alloca` (because it cannot be). * Each block in trans now has an `Option<LandingPad>` instead of `is_lpad: bool` for indicating whether it's in a landing pad or not. The new exception handling intrinsics require that on MSVC each `call` inside of a landing pad is annotated with which landing pad that it's in. This change to the basic block means that whenever a `call` or `invoke` instruction is generated we know whether to annotate it as part of a cleanuppad or not. * Lots of modifications were made to the instruction builders to construct the new instructions as well as pass the tagging information for the call/invoke instructions. * The translation of the `try` intrinsics for MSVC has been overhauled to use the new `catchpad` instruction. The filter function is now also a rustc-generated function instead of a purely libstd-defined function. The libstd definition still exists, it just has a stable ABI across architectures and leaves some of the really weird implementation details to the compiler (e.g. the `localescape` and `localrecover` intrinsics).
2015-10-23 20:18:44 -05:00
if !bcx.sess().target.target.options.custom_unwind_resume {
bcx.resume(llretval);
} else {
let exc_ptr = bcx.extract_value(llretval, 0);
bcx.call(bcx.ccx.eh_unwind_resume(), &[exc_ptr], None);
bcx.unreachable();
}
}
bcx.llbb()
}
}
2017-01-01 01:42:09 -06:00
impl<'a, 'tcx> CleanupScope<'tcx> {
/// Schedules a (deep) drop of `val`, which is a pointer to an instance of `ty`
2016-12-31 17:00:24 -06:00
pub fn schedule_drop_mem(
bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx>
2016-12-31 17:00:24 -06:00
) -> CleanupScope<'tcx> {
if let LvalueTy::Downcast { .. } = val.ty {
bug!("Cannot drop downcast ty yet");
}
if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) {
return CleanupScope::noop();
}
let drop = DropValue {
val: val,
skip_dtor: false,
};
2016-12-31 17:00:24 -06:00
CleanupScope::new(bcx, drop)
}
/// Issue #23611: Schedules a (deep) drop of the contents of
/// `val`, which is a pointer to an instance of struct/enum type
/// `ty`. The scheduled code handles extracting the discriminant
/// and dropping the contents associated with that variant
/// *without* executing any associated drop implementation.
2016-12-31 17:00:24 -06:00
pub fn schedule_drop_adt_contents(
bcx: &Builder<'a, 'tcx>, val: LvalueRef<'tcx>
2016-12-31 17:00:24 -06:00
) -> CleanupScope<'tcx> {
if let LvalueTy::Downcast { .. } = val.ty {
bug!("Cannot drop downcast ty yet");
}
// `if` below could be "!contents_needs_drop"; skipping drop
// is just an optimization, so sound to be conservative.
if !bcx.ccx.shared().type_needs_drop(val.ty.to_ty(bcx.tcx())) {
return CleanupScope::noop();
}
let drop = DropValue {
val: val,
skip_dtor: true,
};
2016-12-31 17:00:24 -06:00
CleanupScope::new(bcx, drop)
}
2017-01-01 01:42:09 -06:00
fn new(bcx: &Builder<'a, 'tcx>, drop_val: DropValue<'tcx>) -> CleanupScope<'tcx> {
CleanupScope {
cleanup: Some(drop_val),
landing_pad: if !bcx.sess().no_landing_pads() {
2016-12-31 17:00:24 -06:00
Some(drop_val.get_landing_pad(bcx))
} else {
None
},
}
}
pub fn noop() -> CleanupScope<'tcx> {
CleanupScope {
cleanup: None,
landing_pad: None,
}
}
2017-01-01 01:42:09 -06:00
pub fn trans(self, bcx: &'a Builder<'a, 'tcx>) {
if let Some(cleanup) = self.cleanup {
cleanup.trans(None, &bcx);
}
}
}