From: Felix S. Klock II Date: Fri, 5 Jun 2015 19:34:03 +0000 (+0200) Subject: Extend `trans::datum::Lvalue` so that it carrys an optional dropflag hint. X-Git-Url: https://git.lizzy.rs/?a=commitdiff_plain;h=a0f3f2ac53690ee049d9c5387dafd46411320a4f;p=rust.git Extend `trans::datum::Lvalue` so that it carrys an optional dropflag hint. Instrumented calls sites that construct Lvalues to ease tracking down cases that we might need to change whether or not they carry a hint. Note that this commit does not do anything to actually *construct* the `lldropflag_hints` map, nor does it change anything about codegen itself. Those parts are in follow-on commits. --- diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index c1fb6f2437f..08be1fd2e4c 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -437,7 +437,7 @@ impl MatchInput { fn from_val(val: ValueRef) -> MatchInput { MatchInput { val: val, - lval: Lvalue, + lval: Lvalue::new("MatchInput::from_val"), } } @@ -941,30 +941,41 @@ fn insert_lllocals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, cs: Option) -> Block<'blk, 'tcx> { for (&ident, &binding_info) in bindings_map { - let llval = match binding_info.trmode { + let (llval, aliases_other_state) = match binding_info.trmode { // By value mut binding for a copy type: load from the ptr // into the matched value and copy to our alloca TrByCopy(llbinding) | TrByMoveIntoCopy(llbinding) => { let llval = Load(bcx, binding_info.llmatch); - let datum = Datum::new(llval, binding_info.ty, Lvalue); + let lval = 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); call_lifetime_start(bcx, llbinding); bcx = datum.store_to(bcx, llbinding); if let Some(cs) = cs { bcx.fcx.schedule_lifetime_end(cs, llbinding); } - llbinding + (llbinding, false) }, // By value move bindings: load from the ptr into the matched value - TrByMoveRef => Load(bcx, binding_info.llmatch), + TrByMoveRef => (Load(bcx, binding_info.llmatch), true), // By ref binding: use the ptr into the matched value - TrByRef => binding_info.llmatch + TrByRef => (binding_info.llmatch, true), }; - let datum = Datum::new(llval, binding_info.ty, Lvalue); + let lval = Lvalue::local("_match::insert_lllocals", + bcx, + binding_info.id, + aliases_other_state); + let datum = Datum::new(llval, binding_info.ty, lval); if let Some(cs) = cs { bcx.fcx.schedule_lifetime_end(cs, binding_info.llmatch); bcx.fcx.schedule_drop_and_fill_mem(cs, llval, binding_info.ty); @@ -1619,6 +1630,7 @@ fn create_dummy_locals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let scope = cleanup::var_scope(tcx, p_id); 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 @@ -1641,6 +1653,7 @@ fn create_dummy_locals<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, let var_scope = cleanup::var_scope(tcx, local.id); 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))); } @@ -1668,6 +1681,7 @@ fn mk_binding_alloca<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>, name: ast::Name, cleanup_scope: cleanup::ScopeId, arg: A, + caller_name: &'static str, populate: F) -> Block<'blk, 'tcx> where F: FnOnce(A, Block<'blk, 'tcx>, ValueRef, Ty<'tcx>) -> Block<'blk, 'tcx>, @@ -1685,7 +1699,8 @@ fn mk_binding_alloca<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>, // Now that memory is initialized and has cleanup scheduled, // create the datum and insert into the local variable map. - let datum = Datum::new(llval, var_ty, Lvalue); + let lval = Lvalue::binding(caller_name, bcx, p_id, name); + let datum = Datum::new(llval, var_ty, lval); bcx.fcx.lllocals.borrow_mut().insert(p_id, datum); bcx } @@ -1730,6 +1745,7 @@ pub fn bind_irrefutable_pat<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // map. bcx = mk_binding_alloca( bcx, pat.id, path1.node.name, cleanup_scope, (), + "_match::bind_irrefutable_pat", |(), bcx, llval, ty| { match pat_binding_mode { ast::BindByValue(_) => { diff --git a/src/librustc_trans/trans/adt.rs b/src/librustc_trans/trans/adt.rs index dc7e34a386f..d2753f5b78f 100644 --- a/src/librustc_trans/trans/adt.rs +++ b/src/librustc_trans/trans/adt.rs @@ -1083,7 +1083,7 @@ pub fn trans_drop_flag_ptr<'blk, 'tcx>(mut bcx: Block<'blk, 'tcx>, )); bcx = fold_variants(bcx, r, val, |variant_cx, st, value| { let ptr = struct_field_ptr(variant_cx, st, value, (st.fields.len() - 1), false); - datum::Datum::new(ptr, ptr_ty, datum::Lvalue) + datum::Datum::new(ptr, ptr_ty, datum::Lvalue::new("adt::trans_drop_flag_ptr")) .store_to(variant_cx, scratch.val) }); let expr_datum = scratch.to_expr_datum(); diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 1e016ea1fdc..ec7ff4fa748 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -56,7 +56,7 @@ 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}; -use trans::common::{CrateContext, FunctionContext}; +use trans::common::{CrateContext, DropFlagHintsMap, FunctionContext}; use trans::common::{Result, NodeIdAndSpan}; use trans::common::{node_id_type, return_type_is_void}; use trans::common::{type_is_immediate, type_is_zero_size, val_ty}; @@ -1235,6 +1235,7 @@ pub fn new_fn_ctxt<'a, 'tcx>(ccx: &'a CrateContext<'a, 'tcx>, caller_expects_out_pointer: uses_outptr, lllocals: RefCell::new(NodeMap()), llupvars: RefCell::new(NodeMap()), + lldropflag_hints: RefCell::new(DropFlagHintsMap::new()), id: id, param_substs: param_substs, span: sp, diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index 0d497120319..fe996020267 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -124,6 +124,7 @@ use trans::build; use trans::common; use trans::common::{Block, FunctionContext, NodeIdAndSpan}; +use trans::datum::{Datum, Lvalue}; use trans::debuginfo::{DebugLoc, ToDebugLoc}; use trans::glue; use middle::region; @@ -212,6 +213,12 @@ pub enum ScopeId { CustomScope(CustomScopeIndex) } +#[derive(Copy, Clone, Debug)] +pub struct DropHint(pub ast::NodeId, pub K); + +pub type DropHintDatum<'tcx> = DropHint>; +pub type DropHintValue = DropHint; + 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) { diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 8ec65639b52..82c835e8c34 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -299,6 +299,27 @@ pub fn validate_substs(substs: &Substs) { type RvalueDatum<'tcx> = datum::Datum<'tcx, datum::Rvalue>; pub type LvalueDatum<'tcx> = datum::Datum<'tcx, datum::Lvalue>; +#[derive(Clone, Debug)] +struct HintEntry<'tcx> { + // The datum for the dropflag-hint itself; note that many + // source-level Lvalues will be associated with the same + // dropflag-hint datum. + datum: cleanup::DropHintDatum<'tcx>, +} + +pub struct DropFlagHintsMap<'tcx> { + // Maps NodeId for expressions that read/write unfragmented state + // to that state's drop-flag "hint." (A stack-local hint + // indicates either that (1.) it is certain that no-drop is + // needed, or (2.) inline drop-flag must be consulted.) + node_map: NodeMap>, +} + +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) } +} + // Function context. Every LLVM function we create will have one of // these. pub struct FunctionContext<'a, 'tcx: 'a> { @@ -349,6 +370,10 @@ pub struct FunctionContext<'a, 'tcx: 'a> { // Same as above, but for closure upvars pub llupvars: RefCell>, + // Carries info about drop-flags for local bindings (longer term, + // paths) for the code being compiled. + pub lldropflag_hints: RefCell>, + // The NodeId of the function, or -1 if it doesn't correspond to // a user-defined function. pub id: ast::NodeId, diff --git a/src/librustc_trans/trans/datum.rs b/src/librustc_trans/trans/datum.rs index c0ebffb58af..f14b6f368f4 100644 --- a/src/librustc_trans/trans/datum.rs +++ b/src/librustc_trans/trans/datum.rs @@ -138,17 +138,141 @@ pub enum Expr { /// `val` is a pointer into memory for which a cleanup is scheduled /// (and thus has type *T). If you move out of an Lvalue, you must /// zero out the memory (FIXME #5016). - LvalueExpr, + LvalueExpr(Lvalue), } -#[derive(Clone, Copy, Debug)] -pub struct Lvalue; +#[derive(Copy, Clone, Debug)] +pub enum DropFlagInfo { + DontZeroJustUse(ast::NodeId), + ZeroAndMaintain(ast::NodeId), + None, +} + +impl DropFlagInfo { + pub fn must_zero(&self) -> bool { + match *self { + DropFlagInfo::DontZeroJustUse(..) => false, + DropFlagInfo::ZeroAndMaintain(..) => true, + DropFlagInfo::None => true, + } + } + + pub fn hint_to_maintain(&self) -> Option { + match *self { + DropFlagInfo::DontZeroJustUse(id) => Some(id), + DropFlagInfo::ZeroAndMaintain(id) => Some(id), + DropFlagInfo::None => None, + } + } +} + +// FIXME: having Lvalue be `Copy` is a bit of a footgun, since clients +// may not realize that subparts of an Lvalue can have a subset of +// drop-flags associated with them, while this as written will just +// memcpy the drop_flag_info. But, it is an easier way to get `_match` +// off the ground to just let this be `Copy` for now. +#[derive(Copy, Clone, Debug)] +pub struct Lvalue { + pub source: &'static str, + pub drop_flag_info: DropFlagInfo +} #[derive(Debug)] pub struct Rvalue { pub mode: RvalueMode } +impl Lvalue { + pub fn new(source: &'static str) -> Lvalue { + Lvalue { source: source, drop_flag_info: DropFlagInfo::None } + } + + pub fn upvar<'blk, 'tcx>(source: &'static str, + bcx: Block<'blk, 'tcx>, + id: ast::NodeId) -> Lvalue { + let info = if Lvalue::has_dropflag_hint(bcx, id) { + DropFlagInfo::ZeroAndMaintain(id) + } else { + DropFlagInfo::None + }; + let info = if bcx.tcx().sess.nonzeroing_move_hints() { info } else { DropFlagInfo::None }; + debug!("upvar Lvalue at {}, id: {} info: {:?}", source, id, info); + Lvalue { source: source, drop_flag_info: info } + } + + pub fn match_input<'blk, 'tcx>(source: &'static str, + bcx: Block<'blk, 'tcx>, + id: ast::NodeId) -> Lvalue + { + let info = if Lvalue::has_dropflag_hint(bcx, id) { + // match_input is used to move from the input into a + // separate stack slot; it must zero (at least until we + // improve things to track drop flags for the fragmented + // parent match input expression). + DropFlagInfo::ZeroAndMaintain(id) + } else { + DropFlagInfo::None + }; + let info = if bcx.tcx().sess.nonzeroing_move_hints() { info } else { DropFlagInfo::None }; + debug!("match_input Lvalue at {}, id: {} info: {:?}", source, id, info); + Lvalue { source: source, drop_flag_info: info } + } + + pub fn local<'blk, 'tcx>(source: &'static str, + bcx: Block<'blk, 'tcx>, + id: ast::NodeId, + aliases_other_state: bool) + -> Lvalue + { + let info = if Lvalue::has_dropflag_hint(bcx, id) { + if aliases_other_state { + DropFlagInfo::ZeroAndMaintain(id) + } else { + DropFlagInfo::DontZeroJustUse(id) + } + } else { + DropFlagInfo::None + }; + let info = if bcx.tcx().sess.nonzeroing_move_hints() { info } else { DropFlagInfo::None }; + debug!("local Lvalue at {}, id: {} info: {:?}", source, id, info); + Lvalue { source: source, drop_flag_info: info } + } + + pub fn store_arg<'blk, 'tcx>(source: &'static str, + bcx: Block<'blk, 'tcx>, + id: ast::NodeId) -> Lvalue + { + let info = if Lvalue::has_dropflag_hint(bcx, id) { + DropFlagInfo::ZeroAndMaintain(id) + } else { + DropFlagInfo::None + }; + let info = if bcx.tcx().sess.nonzeroing_move_hints() { info } else { DropFlagInfo::None }; + debug!("store_arg Lvalue at {}, id: {} info: {:?}", source, id, info); + Lvalue { source: source, drop_flag_info: info } + } + + pub fn binding<'blk, 'tcx>(source: &'static str, + bcx: Block<'blk, 'tcx>, + id: ast::NodeId, + name: ast::Name) -> Lvalue { + let info = if Lvalue::has_dropflag_hint(bcx, id) { + DropFlagInfo::DontZeroJustUse(id) + } else { + DropFlagInfo::None + }; + let info = if bcx.tcx().sess.nonzeroing_move_hints() { info } else { DropFlagInfo::None }; + debug!("binding Lvalue at {}, id: {} name: {} info: {:?}", + source, id, name, info); + Lvalue { source: source, drop_flag_info: info } + } + + fn has_dropflag_hint<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, + id: ast::NodeId) -> bool { + bcx.fcx.lldropflag_hints.borrow().has_hint(id) + } +} + impl Rvalue { pub fn new(m: RvalueMode) -> Rvalue { Rvalue { mode: m } @@ -201,7 +325,7 @@ pub fn lvalue_scratch_datum<'blk, 'tcx, A, F>(bcx: Block<'blk, 'tcx>, bcx.fcx.schedule_lifetime_end(scope, scratch); bcx.fcx.schedule_drop_mem(scope, scratch, ty); - DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue)) + DatumBlock::new(bcx, Datum::new(scratch, ty, Lvalue::new("datum::lvalue_scratch_datum"))) } /// Allocates temporary space on the stack using alloca() and returns a by-ref Datum pointing to @@ -308,7 +432,7 @@ fn is_by_ref(&self) -> bool { } fn to_expr_kind(self) -> Expr { - LvalueExpr + LvalueExpr(self) } } @@ -319,14 +443,14 @@ fn post_store<'blk, 'tcx>(&self, ty: Ty<'tcx>) -> Block<'blk, 'tcx> { match *self { - LvalueExpr => Lvalue.post_store(bcx, val, ty), + LvalueExpr(ref l) => l.post_store(bcx, val, ty), RvalueExpr(ref r) => r.post_store(bcx, val, ty), } } fn is_by_ref(&self) -> bool { match *self { - LvalueExpr => Lvalue.is_by_ref(), + LvalueExpr(ref l) => l.is_by_ref(), RvalueExpr(ref r) => r.is_by_ref() } } @@ -360,7 +484,10 @@ pub fn to_lvalue_datum_in_scope<'blk>(self, match self.kind.mode { ByRef => { add_rvalue_clean(ByRef, fcx, scope, self.val, self.ty); - DatumBlock::new(bcx, Datum::new(self.val, self.ty, Lvalue)) + DatumBlock::new(bcx, Datum::new( + self.val, + self.ty, + Lvalue::new("datum::to_lvalue_datum_in_scope"))) } ByValue => { @@ -417,7 +544,7 @@ fn match_kind(self, if_lvalue: F, if_rvalue: G) -> R where { let Datum { val, ty, kind } = self; match kind { - LvalueExpr => if_lvalue(Datum::new(val, ty, Lvalue)), + LvalueExpr(l) => if_lvalue(Datum::new(val, ty, l)), RvalueExpr(r) => if_rvalue(Datum::new(val, ty, r)), } } @@ -528,7 +655,7 @@ pub fn get_element<'blk, F>(&self, bcx: Block<'blk, 'tcx>, ty: Ty<'tcx>, }; Datum { val: val, - kind: Lvalue, + kind: Lvalue::new("Datum::get_element"), ty: ty, } } diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index 962803932b8..1582a43d94d 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -227,7 +227,7 @@ pub fn trans<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, let const_ty = expr_ty_adjusted(bcx, expr); let llty = type_of::type_of(bcx.ccx(), const_ty); let global = PointerCast(bcx, global, llty.ptr_to()); - let datum = Datum::new(global, const_ty, Lvalue); + let datum = Datum::new(global, const_ty, Lvalue::new("expr::trans")); return DatumBlock::new(bcx, datum.to_expr_datum()); } @@ -733,7 +733,7 @@ fn trans_field<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>, // Always generate an lvalue datum, because this pointer doesn't own // the data and cleanup is scheduled elsewhere. - DatumBlock::new(bcx, Datum::new(scratch.val, scratch.ty, LvalueExpr)) + DatumBlock::new(bcx, Datum::new(scratch.val, scratch.ty, LvalueExpr(d.kind))) } }) @@ -810,10 +810,11 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, Some(SaveIn(scratch.val)), false)); let datum = scratch.to_expr_datum(); + let lval = Lvalue::new("expr::trans_index overload"); if type_is_sized(bcx.tcx(), elt_ty) { - Datum::new(datum.to_llscalarish(bcx), elt_ty, LvalueExpr) + Datum::new(datum.to_llscalarish(bcx), elt_ty, LvalueExpr(lval)) } else { - Datum::new(datum.val, elt_ty, LvalueExpr) + Datum::new(datum.val, elt_ty, LvalueExpr(lval)) } } None => { @@ -867,7 +868,8 @@ fn trans_index<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, }); let elt = InBoundsGEP(bcx, base, &[ix_val]); let elt = PointerCast(bcx, elt, type_of::type_of(ccx, unit_ty).ptr_to()); - Datum::new(elt, unit_ty, LvalueExpr) + let lval = Lvalue::new("expr::trans_index fallback"); + Datum::new(elt, unit_ty, LvalueExpr(lval)) } }; @@ -912,7 +914,8 @@ fn trans_def<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // Case 2. base::get_extern_const(bcx.ccx(), did, const_ty) }; - DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr)) + let lval = Lvalue::new("expr::trans_def"); + DatumBlock::new(bcx, Datum::new(val, const_ty, LvalueExpr(lval))) } def::DefConst(_) => { bcx.sess().span_bug(ref_expr.span, @@ -1302,8 +1305,9 @@ pub fn trans_local_var<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, def::DefUpvar(nid, _) => { // Can't move upvars, so this is never a ZeroMemLastUse. let local_ty = node_id_type(bcx, nid); + let lval = Lvalue::upvar("expr::trans_local_var", bcx, nid); match bcx.fcx.llupvars.borrow().get(&nid) { - Some(&val) => Datum::new(val, local_ty, Lvalue), + Some(&val) => Datum::new(val, local_ty, lval), None => { bcx.sess().bug(&format!( "trans_local_var: no llval for upvar {} found", @@ -2267,7 +2271,7 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, if type_is_sized(bcx.tcx(), content_ty) { let ptr = load_ty(bcx, datum.val, datum.ty); - DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr)) + DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr(datum.kind))) } else { // A fat pointer and a DST lvalue have the same representation // just different types. Since there is no temporary for `*e` @@ -2275,13 +2279,15 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // object code path for running drop glue and free. Instead, // we schedule cleanup for `e`, turning it into an lvalue. - let datum = Datum::new(datum.val, content_ty, LvalueExpr); + let lval = Lvalue::new("expr::deref_once ty_uniq"); + let datum = Datum::new(datum.val, content_ty, LvalueExpr(lval)); DatumBlock::new(bcx, datum) } } ty::TyRawPtr(ty::TypeAndMut { ty: content_ty, .. }) | ty::TyRef(_, ty::TypeAndMut { ty: content_ty, .. }) => { + let lval = Lvalue::new("expr::deref_once ptr"); if type_is_sized(bcx.tcx(), content_ty) { let ptr = datum.to_llscalarish(bcx); @@ -2290,11 +2296,11 @@ fn deref_once<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // rvalue for non-owning pointers like &T or *T, in which // case cleanup *is* scheduled elsewhere, by the true // owner (or, in the case of *T, by the user). - DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr)) + DatumBlock::new(bcx, Datum::new(ptr, content_ty, LvalueExpr(lval))) } else { // A fat pointer and a DST lvalue have the same representation // just different types. - DatumBlock::new(bcx, Datum::new(datum.val, content_ty, LvalueExpr)) + DatumBlock::new(bcx, Datum::new(datum.val, content_ty, LvalueExpr(lval))) } }