Skip to content

Commit

Permalink
Add dropflag hints (stack-local booleans) for unfragmented paths in t…
Browse files Browse the repository at this point in the history
…rans.

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.)
  • Loading branch information
pnkfelix committed Jul 28, 2015
1 parent d3d552b commit dce1c61
Show file tree
Hide file tree
Showing 10 changed files with 259 additions and 70 deletions.
72 changes: 44 additions & 28 deletions src/librustc_trans/trans/_match.rs
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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 {
Expand All @@ -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));
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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
}
Expand All @@ -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 => {}
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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`
Expand Down Expand Up @@ -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);
Expand All @@ -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");
Expand Down
14 changes: 14 additions & 0 deletions src/librustc_trans/trans/adt.rs
Expand Up @@ -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);
Expand Down
53 changes: 50 additions & 3 deletions src/librustc_trans/trans/base.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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};
Expand Down Expand Up @@ -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
}

Expand Down
48 changes: 39 additions & 9 deletions src/librustc_trans/trans/cleanup.rs
Expand Up @@ -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) {
Expand Down Expand Up @@ -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={}",
Expand All @@ -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);
}
Expand All @@ -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={}",
Expand All @@ -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,
Expand Down Expand Up @@ -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> {
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit dce1c61

Please sign in to comment.