diff options
| author | Felix S. Klock II <pnkfelix@pnkfx.org> | 2015-06-07 09:25:14 +0200 |
|---|---|---|
| committer | Felix S. Klock II <pnkfelix@pnkfx.org> | 2015-07-28 16:14:58 +0200 |
| commit | dce1c61e977deed5e0a499f4247d5da870809692 (patch) | |
| tree | 7fff58a51ee7955ca8df32f0a340397c770d7cd7 | |
| parent | d3d552b71b89633fe6692bf2214a2d001683bfa9 (diff) | |
| download | rust-dce1c61e977deed5e0a499f4247d5da870809692.tar.gz rust-dce1c61e977deed5e0a499f4247d5da870809692.zip | |
Add dropflag hints (stack-local booleans) for unfragmented paths in trans.
Added code to maintain these hints at runtime, and to conditionalize drop-filling and calls to destructors. In this early stage, we are using hints, so we are always free to leave out a flag for a path -- then we just pass `None` as the dropflag hint in the corresponding schedule cleanup call. But, once a path has a hint, we must at least maintain it: i.e. if the hint exists, we must ensure it is never set to "moved" if the data in question might actually have been initialized. It remains sound to conservatively set the hint to "initialized" as long as the true drop-flag embedded in the value itself is up-to-date. ---- Here are some high-level details I want to point out: * We maintain the hint in Lvalue::post_store, marking the lvalue as moved. (But also continue drop-filling if necessary.) * We update the hint on ExprAssign. * We pass along the hint in once closures that capture-by-move. * You only call `drop_ty` for state that does not have an associated hint. If you have a hint, you must call `drop_ty_core` instead. (Originally I passed the hint into `drop_ty` as well, to make the connection to a hint more apparent, but the vast majority of current calls to `drop_ty` are in contexts where no hint is available, so it just seemed like noise in the resulting diff.)
| -rw-r--r-- | src/librustc_trans/trans/_match.rs | 72 | ||||
| -rw-r--r-- | src/librustc_trans/trans/adt.rs | 14 | ||||
| -rw-r--r-- | src/librustc_trans/trans/base.rs | 53 | ||||
| -rw-r--r-- | src/librustc_trans/trans/cleanup.rs | 48 | ||||
| -rw-r--r-- | src/librustc_trans/trans/closure.rs | 4 | ||||
| -rw-r--r-- | src/librustc_trans/trans/common.rs | 6 | ||||
| -rw-r--r-- | src/librustc_trans/trans/datum.rs | 70 | ||||
| -rw-r--r-- | src/librustc_trans/trans/expr.rs | 29 | ||||
| -rw-r--r-- | src/librustc_trans/trans/glue.rs | 29 | ||||
| -rw-r--r-- | src/librustc_trans/trans/tvec.rs | 4 |
10 files changed, 259 insertions, 70 deletions
diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 08be1fd2e4c..7a491a628e5 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -205,7 +205,7 @@ use trans::build::{AddCase, And, Br, CondBr, GEPi, InBoundsGEP, Load, PointerCas use trans::build::{Not, Store, Sub, add_comment}; use trans::build; use trans::callee; -use trans::cleanup::{self, CleanupMethods}; +use trans::cleanup::{self, CleanupMethods, DropHintMethods}; use trans::common::*; use trans::consts; use trans::datum::*; @@ -947,14 +947,14 @@ fn insert_lllocals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, TrByCopy(llbinding) | TrByMoveIntoCopy(llbinding) => { let llval = Load(bcx, binding_info.llmatch); - let lval = match binding_info.trmode { + let lvalue = match binding_info.trmode { TrByCopy(..) => Lvalue::new("_match::insert_lllocals"), TrByMoveIntoCopy(..) => Lvalue::match_input("_match::insert_lllocals", bcx, binding_info.id), _ => unreachable!(), }; - let datum = Datum::new(llval, binding_info.ty, lval); + let datum = Datum::new(llval, binding_info.ty, lvalue); call_lifetime_start(bcx, llbinding); bcx = datum.store_to(bcx, llbinding); if let Some(cs) = cs { @@ -971,14 +971,15 @@ fn insert_lllocals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, TrByRef => (binding_info.llmatch, true), }; - let lval = Lvalue::local("_match::insert_lllocals", - bcx, - binding_info.id, - aliases_other_state); - let datum = Datum::new(llval, binding_info.ty, lval); + let lvalue = Lvalue::local("_match::insert_lllocals", + bcx, + binding_info.id, + aliases_other_state); + let datum = Datum::new(llval, binding_info.ty, lvalue); if let Some(cs) = cs { + let opt_datum = lvalue.dropflag_hint(bcx); bcx.fcx.schedule_lifetime_end(cs, binding_info.llmatch); - bcx.fcx.schedule_drop_and_fill_mem(cs, llval, binding_info.ty); + bcx.fcx.schedule_drop_and_fill_mem(cs, llval, binding_info.ty, opt_datum); } debug!("binding {} to {}", binding_info.id, bcx.val_to_string(llval)); @@ -1505,13 +1506,13 @@ fn create_bindings_map<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, pat: &ast::Pat, // but during matching we need to store a *T as explained // above llmatch = alloca_no_lifetime(bcx, - llvariable_ty.ptr_to(), - &bcx.name(name)); + llvariable_ty.ptr_to(), + &bcx.name(name)); trmode = TrByMoveRef; } ast::BindByRef(_) => { llmatch = alloca_no_lifetime(bcx, - llvariable_ty, + llvariable_ty, &bcx.name(name)); trmode = TrByRef; } @@ -1631,7 +1632,25 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, bcx = mk_binding_alloca( bcx, p_id, path1.node.name, scope, (), "_match::store_local::create_dummy_locals", - |(), bcx, llval, ty| { drop_done_fill_mem(bcx, llval, ty); bcx }); + |(), bcx, Datum { val: llval, ty, kind }| { + // Dummy-locals start out uninitialized, so set their + // drop-flag hints (if any) to "moved." + if let Some(hint) = kind.dropflag_hint(bcx) { + let moved_hint = adt::DTOR_MOVED_HINT as usize; + debug!("store moved_hint={} for hint={:?}, uninitialized dummy", + moved_hint, hint); + Store(bcx, C_u8(bcx.fcx.ccx, moved_hint), hint.to_value().value()); + } + + if kind.drop_flag_info.must_zero() { + // if no drop-flag hint, or the hint requires + // we maintain the embedded drop-flag, then + // mark embedded drop-flag(s) as moved + // (i.e. "already dropped"). + drop_done_fill_mem(bcx, llval, ty); + } + bcx + }); }); bcx } @@ -1654,8 +1673,8 @@ pub fn store_local<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, return mk_binding_alloca( bcx, pat.id, ident.name, var_scope, (), "_match::store_local", - |(), bcx, v, _| expr::trans_into(bcx, &**init_expr, - expr::SaveIn(v))); + |(), bcx, Datum { val: v, .. }| expr::trans_into(bcx, &**init_expr, + expr::SaveIn(v))); } None => {} @@ -1684,23 +1703,23 @@ fn mk_binding_alloca<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>, caller_name: &'static str, populate: F) -> Block<'blk, 'tcx> where - F: FnOnce(A, Block<'blk, 'tcx>, ValueRef, Ty<'tcx>) -> Block<'blk, 'tcx>, + F: FnOnce(A, Block<'blk, 'tcx>, Datum<'tcx, Lvalue>) -> Block<'blk, 'tcx>, { let var_ty = node_id_type(bcx, p_id); // Allocate memory on stack for the binding. let llval = alloc_ty(bcx, var_ty, &bcx.name(name)); + let lvalue = Lvalue::binding(caller_name, bcx, p_id, name); + let datum = Datum::new(llval, var_ty, lvalue); // Subtle: be sure that we *populate* the memory *before* // we schedule the cleanup. - let bcx = populate(arg, bcx, llval, var_ty); + let bcx = populate(arg, bcx, datum); bcx.fcx.schedule_lifetime_end(cleanup_scope, llval); - bcx.fcx.schedule_drop_mem(cleanup_scope, llval, var_ty); + bcx.fcx.schedule_drop_mem(cleanup_scope, llval, var_ty, lvalue.dropflag_hint(bcx)); // Now that memory is initialized and has cleanup scheduled, - // create the datum and insert into the local variable map. - let lval = Lvalue::binding(caller_name, bcx, p_id, name); - let datum = Datum::new(llval, var_ty, lval); + // insert datum into the local variable map. bcx.fcx.lllocals.borrow_mut().insert(p_id, datum); bcx } @@ -1746,7 +1765,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, bcx = mk_binding_alloca( bcx, pat.id, path1.node.name, cleanup_scope, (), "_match::bind_irrefutable_pat", - |(), bcx, llval, ty| { + |(), bcx, Datum { val: llval, ty, kind: _ }| { match pat_binding_mode { ast::BindByValue(_) => { // By value binding: move the value that `val` @@ -1854,10 +1873,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ast::PatBox(ref inner) => { let llbox = Load(bcx, val.val); bcx = bind_irrefutable_pat( - bcx, - &**inner, - MatchInput::from_val(llbox), - cleanup_scope); + bcx, &**inner, MatchInput::from_val(llbox), cleanup_scope); } ast::PatRegion(ref inner, _) => { let loaded_val = Load(bcx, val.val); @@ -1884,13 +1900,13 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, .chain(slice.iter()) .chain(after.iter()) .zip(extracted.vals) - .fold(bcx, |bcx, (inner, elem)| + .fold(bcx, |bcx, (inner, elem)| { bind_irrefutable_pat( bcx, &**inner, MatchInput::from_val(elem), cleanup_scope) - ); + }); } ast::PatMac(..) => { bcx.sess().span_bug(pat.span, "unexpanded macro"); diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index d2753f5b78f..b47d2dd4112 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -163,6 +163,20 @@ macro_rules! repeat_u8_as_u64 { (repeat_u8_as_u32!($name) as u64)) } } +/// `DTOR_NEEDED_HINT` is a stack-local hint that just means +/// "we do not know whether the destructor has run or not; check the +/// drop-flag embedded in the value itself." +pub const DTOR_NEEDED_HINT: u8 = 0x3d; + +/// `DTOR_MOVED_HINT` is a stack-local hint that means "this value has +/// definitely been moved; you do not need to run its destructor." +/// +/// (However, for now, such values may still end up being explicitly +/// zeroed by the generated code; this is the distinction between +/// `datum::DropFlagInfo::ZeroAndMaintain` versus +/// `datum::DropFlagInfo::DontZeroJustUse`.) +pub const DTOR_MOVED_HINT: u8 = 0x2d; + pub const DTOR_NEEDED: u8 = 0xd4; pub const DTOR_NEEDED_U32: u32 = repeat_u8_as_u32!(DTOR_NEEDED); pub const DTOR_NEEDED_U64: u64 = repeat_u8_as_u64!(DTOR_NEEDED); diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index ec7ff4fa748..61e81d75607 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -51,8 +51,7 @@ use trans::attributes; use trans::build::*; use trans::builder::{Builder, noname}; use trans::callee; -use trans::cleanup::CleanupMethods; -use trans::cleanup; +use trans::cleanup::{self, CleanupMethods, DropHint}; use trans::closure; use trans::common::{Block, C_bool, C_bytes_in_context, C_i32, C_int, C_integral}; use trans::common::{C_null, C_struct_in_context, C_u64, C_u8, C_undef}; @@ -88,7 +87,7 @@ use arena::TypedArena; use libc::c_uint; use std::ffi::{CStr, CString}; use std::cell::{Cell, RefCell}; -use std::collections::HashSet; +use std::collections::{HashMap, HashSet}; use std::mem; use std::str; use std::{i8, i16, i32, i64}; @@ -1284,6 +1283,54 @@ pub fn init_function<'a, 'tcx>(fcx: &'a FunctionContext<'a, 'tcx>, } } + // Create the drop-flag hints for every unfragmented path in the function. + let tcx = fcx.ccx.tcx(); + let fn_did = ast::DefId { krate: ast::LOCAL_CRATE, node: fcx.id }; + let mut hints = fcx.lldropflag_hints.borrow_mut(); + let fragment_infos = tcx.fragment_infos.borrow(); + + // Intern table for drop-flag hint datums. + let mut seen = HashMap::new(); + + if let Some(fragment_infos) = fragment_infos.get(&fn_did) { + for &info in fragment_infos { + + let make_datum = |id| { + let init_val = C_u8(fcx.ccx, adt::DTOR_NEEDED_HINT as usize); + let llname = &format!("dropflag_hint_{}", id); + debug!("adding hint {}", llname); + let ptr = alloc_ty(entry_bcx, tcx.types.u8, llname); + Store(entry_bcx, init_val, ptr); + let ty = tcx.mk_ptr(ty::TypeAndMut { ty: tcx.types.u8, mutbl: ast::MutMutable }); + let flag = datum::Lvalue::new_dropflag_hint("base::init_function"); + let datum = datum::Datum::new(ptr, ty, flag); + datum + }; + + let (var, datum) = match info { + ty::FragmentInfo::Moved { var, .. } | + ty::FragmentInfo::Assigned { var, .. } => { + let datum = seen.get(&var).cloned().unwrap_or_else(|| { + let datum = make_datum(var); + seen.insert(var, datum.clone()); + datum + }); + (var, datum) + } + }; + match info { + ty::FragmentInfo::Moved { move_expr: expr_id, .. } => { + debug!("FragmentInfo::Moved insert drop hint for {}", expr_id); + hints.insert(expr_id, DropHint::new(var, datum)); + } + ty::FragmentInfo::Assigned { assignee_id: expr_id, .. } => { + debug!("FragmentInfo::Assigned insert drop hint for {}", expr_id); + hints.insert(expr_id, DropHint::new(var, datum)); + } + } + } + } + entry_bcx } diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index fe996020267..b4b0472512e 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -219,6 +219,23 @@ pub struct DropHint<K>(pub ast::NodeId, pub K); pub type DropHintDatum<'tcx> = DropHint<Datum<'tcx, Lvalue>>; pub type DropHintValue = DropHint<ValueRef>; +impl<K> DropHint<K> { + pub fn new(id: ast::NodeId, k: K) -> DropHint<K> { DropHint(id, k) } +} + +impl DropHint<ValueRef> { + pub fn value(&self) -> ValueRef { self.1 } +} + +pub trait DropHintMethods { + type ValueKind; + fn to_value(&self) -> Self::ValueKind; +} +impl<'tcx> DropHintMethods for DropHintDatum<'tcx> { + type ValueKind = DropHintValue; + fn to_value(&self) -> DropHintValue { DropHint(self.0, self.1.val) } +} + impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { /// Invoked when we start to trans the code contained within a new cleanup scope. fn push_ast_cleanup_scope(&self, debug_loc: NodeIdAndSpan) { @@ -389,14 +406,17 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { fn schedule_drop_mem(&self, cleanup_scope: ScopeId, val: ValueRef, - ty: Ty<'tcx>) { + ty: Ty<'tcx>, + drop_hint: Option<DropHintDatum<'tcx>>) { if !self.type_needs_drop(ty) { return; } + let drop_hint = drop_hint.map(|hint|hint.to_value()); let drop = box DropValue { is_immediate: false, val: val, ty: ty, fill_on_drop: false, skip_dtor: false, + drop_hint: drop_hint, }; debug!("schedule_drop_mem({:?}, val={}, ty={:?}) fill_on_drop={} skip_dtor={}", @@ -413,23 +433,28 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { fn schedule_drop_and_fill_mem(&self, cleanup_scope: ScopeId, val: ValueRef, - ty: Ty<'tcx>) { + ty: Ty<'tcx>, + drop_hint: Option<DropHintDatum<'tcx>>) { if !self.type_needs_drop(ty) { return; } + let drop_hint = drop_hint.map(|datum|datum.to_value()); let drop = box DropValue { is_immediate: false, val: val, ty: ty, fill_on_drop: true, skip_dtor: false, + drop_hint: drop_hint, }; - debug!("schedule_drop_and_fill_mem({:?}, val={}, ty={:?}, fill_on_drop={}, skip_dtor={})", + debug!("schedule_drop_and_fill_mem({:?}, val={}, ty={:?}, + fill_on_drop={}, skip_dtor={}, has_drop_hint={})", cleanup_scope, self.ccx.tn().val_to_string(val), ty, drop.fill_on_drop, - drop.skip_dtor); + drop.skip_dtor, + drop_hint.is_some()); self.schedule_clean(cleanup_scope, drop as CleanupObj); } @@ -453,6 +478,7 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { ty: ty, fill_on_drop: false, skip_dtor: true, + drop_hint: None, }; debug!("schedule_drop_adt_contents({:?}, val={}, ty={:?}) fill_on_drop={} skip_dtor={}", @@ -472,13 +498,14 @@ impl<'blk, 'tcx> CleanupMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx> { ty: Ty<'tcx>) { if !self.type_needs_drop(ty) { return; } - let drop = box DropValue { + let drop = Box::new(DropValue { is_immediate: true, val: val, ty: ty, fill_on_drop: false, skip_dtor: false, - }; + drop_hint: None, + }); debug!("schedule_drop_immediate({:?}, val={}, ty={:?}) fill_on_drop={} skip_dtor={}", cleanup_scope, @@ -983,6 +1010,7 @@ pub struct DropValue<'tcx> { ty: Ty<'tcx>, fill_on_drop: bool, skip_dtor: bool, + drop_hint: Option<DropHintValue>, } impl<'tcx> Cleanup<'tcx> for DropValue<'tcx> { @@ -1007,7 +1035,7 @@ impl<'tcx> Cleanup<'tcx> for DropValue<'tcx> { let bcx = if self.is_immediate { glue::drop_ty_immediate(bcx, self.val, self.ty, debug_loc, self.skip_dtor) } else { - glue::drop_ty_core(bcx, self.val, self.ty, debug_loc, self.skip_dtor) + glue::drop_ty_core(bcx, self.val, self.ty, debug_loc, self.skip_dtor, self.drop_hint) }; if self.fill_on_drop { base::drop_done_fill_mem(bcx, self.val, self.ty); @@ -1135,11 +1163,13 @@ pub trait CleanupMethods<'blk, 'tcx> { fn schedule_drop_mem(&self, cleanup_scope: ScopeId, val: ValueRef, - ty: Ty<'tcx>); + ty: Ty<'tcx>, + drop_hint: Option<DropHintDatum<'tcx>>); fn schedule_drop_and_fill_mem(&self, cleanup_scope: ScopeId, val: ValueRef, - ty: Ty<'tcx>); + ty: Ty<'tcx>, + drop_hint: Option<DropHintDatum<'tcx>>); fn schedule_drop_adt_contents(&self, cleanup_scope: ScopeId, val: ValueRef, diff --git a/src/librustc_trans/trans/closure.rs b/src/librustc_trans/trans/closure.rs index ef5da3e40df..d9787231096 100644 --- a/src/librustc_trans/trans/closure.rs +++ b/src/librustc_trans/trans/closure.rs @@ -82,9 +82,11 @@ fn load_closure_environment<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, bcx.fcx.llupvars.borrow_mut().insert(def_id.node, upvar_ptr); if kind == ty::FnOnceClosureKind && !captured_by_ref { + let hint = bcx.fcx.lldropflag_hints.borrow().hint_datum(upvar_id.var_id); bcx.fcx.schedule_drop_mem(arg_scope_id, upvar_ptr, - node_id_type(bcx, def_id.node)) + node_id_type(bcx, def_id.node), + hint) } if let Some(env_pointer_alloca) = env_pointer_alloca { diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 82c835e8c34..9478c197a44 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -318,6 +318,12 @@ pub struct DropFlagHintsMap<'tcx> { impl<'tcx> DropFlagHintsMap<'tcx> { pub fn new() -> DropFlagHintsMap<'tcx> { DropFlagHintsMap { node_map: NodeMap() } } pub fn has_hint(&self, id: ast::NodeId) -> bool { self.node_map.contains_key(&id) } + pub fn insert(&mut self, id: ast::NodeId, datum: cleanup::DropHintDatum<'tcx>) { + self.node_map.insert(id, HintEntry { datum: datum }); + } + pub fn hint_datum(&self, id: ast::NodeId) -> Option<cleanup::DropHintDatum<'tcx>> { + self.node_map.get(&id).map(|t|t.datum) + } } // Function context. Every LLVM function we create will have one of diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs index f14b6f368f4..28fbc9fc8d5 100644 --- a/src/librustc_trans/trans/datum.rs +++ b/src/librustc_trans/trans/datum.rs @@ -93,11 +93,12 @@ pub use self::Expr::*; pub use self::RvalueMode::*; use llvm::ValueRef; +use trans::adt; use trans::base::*; -use trans::build::Load; +use trans::build::{Load, Store}; use trans::common::*; use trans::cleanup; -use trans::cleanup::CleanupMethods; +use trans::cleanup::{CleanupMethods, DropHintDatum, DropHintMethods}; use trans::expr; use trans::tvec; use trans::type_of; @@ -111,7 +112,7 @@ use syntax::codemap::DUMMY_SP; /// describes where the value is stored, what Rust type the value has, /// whether it is addressed by reference, and so forth. Please refer /// the section on datums in `README.md` for more details. -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Debug)] pub struct Datum<'tcx, K> { /// The llvm value. This is either a pointer to the Rust value or /// the value itself, depending on `kind` below. @@ -141,7 +142,7 @@ pub enum Expr { LvalueExpr(Lvalue), } -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub enum DropFlagInfo { DontZeroJustUse(ast::NodeId), ZeroAndMaintain(ast::NodeId), @@ -157,12 +158,18 @@ impl DropFlagInfo { } } - pub fn hint_to_maintain(&self) -> Option<ast::NodeId> { - match *self { - DropFlagInfo::DontZeroJustUse(id) => Some(id), - DropFlagInfo::ZeroAndMaintain(id) => Some(id), - DropFlagInfo::None => None, - } + pub fn hint_datum<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>) + -> Option<DropHintDatum<'tcx>> { + let id = match *self { + DropFlagInfo::None => return None, + DropFlagInfo::DontZeroJustUse(id) | + DropFlagInfo::ZeroAndMaintain(id) => id, + }; + + let hints = bcx.fcx.lldropflag_hints.borrow(); + let retval = hints.hint_datum(id); + assert!(retval.is_some(), "An id (={}) means must have a hint", id); + retval } } @@ -182,7 +189,8 @@ pub struct Rvalue { pub mode: RvalueMode } -impl Lvalue { +// XXX: reduce this to a smaller kernel of constructors. +impl Lvalue { // These are all constructors for various Lvalues. pub fn new(source: &'static str) -> Lvalue { Lvalue { source: source, drop_flag_info: DropFlagInfo::None } } @@ -267,9 +275,21 @@ impl Lvalue { Lvalue { source: source, drop_flag_info: info } } + pub fn new_dropflag_hint(source: &'static str) -> Lvalue { + debug!("dropflag hint Lvalue at {}", source); + Lvalue { source: source, drop_flag_info: DropFlagInfo::None } + } +} // end Lvalue constructor methods. + +impl Lvalue { fn has_dropflag_hint<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, id: ast::NodeId) -> bool { - bcx.fcx.lldropflag_hints.borrow().has_hint(id) + let hints = bcx.fcx.lldropflag_hints.borrow(); + hints.has_hint(id) + } + pub fn dropflag_hint<'blk, 'tcx>(&self, bcx: Block<'blk, 'tcx>) + -> Option<DropHintDatum<'tcx>> { + self.drop_flag_info.hint_datum(bcx) } } @@ -323,7 +343,7 @@ pub fn lvalue_scratch_datum<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>, // Subtle. Populate the scratch memory *before* scheduling cleanup. let bcx = populate(arg, bcx, scratch); bcx.fcx.schedule_lifetime_end(scope, scratch); - bcx.fcx.schedule_drop_mem(scope, scratch, ty); + bcx.fcx.schedule_drop_mem(scope, scratch, ty, None); DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue::new("datum::lvalue_scratch_datum"))) } @@ -362,7 +382,7 @@ fn add_rvalue_clean<'a, 'tcx>(mode: RvalueMode, ByValue => { fcx.schedule_drop_immediate(scope, val, ty); } ByRef => { fcx.schedule_lifetime_end(scope, val); - fcx.schedule_drop_mem(scope, val, ty); + fcx.schedule_drop_mem(scope, val, ty, None); } } } @@ -419,10 +439,28 @@ impl KindOps for Lvalue { -> Block<'blk, 'tcx> { let _icx = push_ctxt("<Lvalue as KindOps>::post_store"); if bcx.fcx.type_needs_drop(ty) { - // cancel cleanup of affine values by drop-filling the memory - let () = drop_done_fill_mem(bcx, val, ty); + // cancel cleanup of affine values: + // 1. if it has drop-hint, mark as moved; then code + // aware of drop-hint won't bother calling the + // drop-glue itself. + if let Some(hint_datum) = self.drop_flag_info.hint_datum(bcx) { + let moved_hint_byte = adt::DTOR_MOVED_HINT as usize; + let hint_llval = hint_datum.to_value().value(); + Store(bcx, C_u8(bcx.fcx.ccx, moved_hint_byte), hint_llval); + } + // 2. if the drop info says its necessary, drop-fill the memory. + if self.drop_flag_info.must_zero() { + let () = drop_done_fill_mem(bcx, val, ty); + } bcx } else { + // XXX would be nice to assert this, but we currently are + // adding e.g. DontZeroJustUse flags. (The dropflag hint + // construction should be taking !type_needs_drop into + // account; earlier analysis phases may not have all the + // info they need to do it properly, I think...) + // + // assert_eq!(self.drop_flag_info, DropFlagInfo::None); bcx } } diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 1582a43d94d..ba0ae27a005 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -61,7 +61,7 @@ use middle::traits; use trans::{_match, adt, asm, base, callee, closure, consts, controlflow}; use trans::base::*; use trans::build::*; -use trans::cleanup::{self, CleanupMethods}; +use trans::cleanup::{self, CleanupMethods, DropHintMethods}; use trans::common::*; use trans::datum::*; use trans::debuginfo::{self, DebugLoc, ToDebugLoc}; @@ -1004,10 +1004,26 @@ fn trans_rvalue_stmt_unadjusted<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, debuginfo::set_source_location(bcx.fcx, expr.id, expr.span); let src_datum = unpack_datum!( bcx, src_datum.to_rvalue_datum(bcx, "ExprAssign")); - bcx = glue::drop_ty(bcx, - dst_datum.val, - dst_datum.ty, - expr.debug_loc()); + if let Some(hint_datum) = dst_datum.kind.drop_flag_info.hint_datum(bcx) { + let hint_val = hint_datum.to_value(); + // XXX the checkpointed branch only does the + // drop_ty call within this branch (and I claim + // that seems like a bug). At this point I have + // moved it into the branch solely to see if it + // makes my plague of bugs go away. + bcx = glue::drop_ty_core(bcx, + dst_datum.val, + dst_datum.ty, + expr.debug_loc(), + false, + Some(hint_val)); + // We are initializing or overwriting the + // destination, so we need to write "drop needed" + // into the hint. + let hint_llval = hint_val.value(); + let drop_needed = C_u8(bcx.fcx.ccx, adt::DTOR_NEEDED_HINT as usize); + Store(bcx, drop_needed, hint_llval); + } src_datum.store_to(bcx, dst_datum.val) } else { src_datum.store_to(bcx, dst_datum.val) @@ -1578,7 +1594,8 @@ pub fn trans_adt<'a, 'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, bcx = trans_into(bcx, &**e, SaveIn(dest)); let scope = cleanup::CustomScope(custom_cleanup_scope); fcx.schedule_lifetime_end(scope, dest); - fcx.schedule_drop_mem(scope, dest, e_ty); + // FIXME: nonzeroing move should generalize to fields + fcx.schedule_drop_mem(scope, dest, e_ty, None); } } diff --git a/src/librustc_trans/trans/glue.rs b/src/librustc_trans/trans/glue.rs index e530eb0de59..6019099c058 100644 --- a/src/librustc_trans/trans/glue.rs +++ b/src/librustc_trans/trans/glue.rs @@ -130,17 +130,20 @@ pub fn drop_ty<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v: ValueRef, t: Ty<'tcx>, debug_loc: DebugLoc) -> Block<'blk, 'tcx> { - drop_ty_core(bcx, v, t, debug_loc, false) + drop_ty_core(bcx, v, t, debug_loc, false, None) } pub fn drop_ty_core<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v: ValueRef, t: Ty<'tcx>, debug_loc: DebugLoc, - skip_dtor: bool) -> Block<'blk, 'tcx> { + skip_dtor: bool, + drop_hint: Option<cleanup::DropHintValue>) + -> Block<'blk, 'tcx> { // NB: v is an *alias* of type t here, not a direct value. - debug!("drop_ty_core(t={:?}, skip_dtor={})", t, skip_dtor); + debug!("drop_ty_core(t={:?}, skip_dtor={} drop_hint={:?})", t, skip_dtor, drop_hint); let _icx = push_ctxt("drop_ty"); + let mut bcx = bcx; if bcx.fcx.type_needs_drop(t) { let ccx = bcx.ccx(); let g = if skip_dtor { @@ -156,7 +159,23 @@ pub fn drop_ty_core<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, v }; - Call(bcx, glue, &[ptr], None, debug_loc); + match drop_hint { + Some(drop_hint) => { + let hint_val = load_ty(bcx, drop_hint.value(), bcx.tcx().types.u8); + let moved_val = + C_integral(Type::i8(bcx.ccx()), adt::DTOR_MOVED_HINT as u64, false); + let may_need_drop = + ICmp(bcx, llvm::IntNE, hint_val, moved_val, DebugLoc::None); + bcx = with_cond(bcx, may_need_drop, |cx| { + Call(cx, glue, &[ptr], None, debug_loc); + cx + }) + } + None => { + // No drop-hint ==> call standard drop glue + Call(bcx, glue, &[ptr], None, debug_loc); + } + } } bcx } @@ -170,7 +189,7 @@ pub fn drop_ty_immediate<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let _icx = push_ctxt("drop_ty_immediate"); let vp = alloca(bcx, type_of(bcx.ccx(), t), ""); store_ty(bcx, v, vp, t); - drop_ty_core(bcx, vp, t, debug_loc, skip_dtor) + drop_ty_core(bcx, vp, t, debug_loc, skip_dtor, None) } pub fn get_drop_glue<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, t: Ty<'tcx>) -> ValueRef { diff --git a/src/librustc_trans/trans/tvec.rs b/src/librustc_trans/trans/tvec.rs index 0e05ca52959..a9abc61c274 100644 --- a/src/librustc_trans/trans/tvec.rs +++ b/src/librustc_trans/trans/tvec.rs @@ -117,7 +117,7 @@ pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Arrange for the backing array to be cleaned up. let cleanup_scope = cleanup::temporary_scope(bcx.tcx(), content_expr.id); fcx.schedule_lifetime_end(cleanup_scope, llfixed); - fcx.schedule_drop_mem(cleanup_scope, llfixed, fixed_ty); + fcx.schedule_drop_mem(cleanup_scope, llfixed, fixed_ty, None); // Generate the content into the backing array. // llfixed has type *[T x N], but we want the type *T, @@ -212,7 +212,7 @@ fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, SaveIn(lleltptr)); let scope = cleanup::CustomScope(temp_scope); fcx.schedule_lifetime_end(scope, lleltptr); - fcx.schedule_drop_mem(scope, lleltptr, vt.unit_ty); + fcx.schedule_drop_mem(scope, lleltptr, vt.unit_ty, None); } fcx.pop_custom_cleanup_scope(temp_scope); } |
