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.
This commit is contained in:
Felix S. Klock II 2015-06-05 21:34:03 +02:00
parent 20aa27b7bc
commit a0f3f2ac53
7 changed files with 213 additions and 31 deletions

View File

@ -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<cleanup::ScopeId>)
-> 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 @@ pub fn store_local<'blk, 'tcx>(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 @@ pub fn store_local<'blk, 'tcx>(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(_) => {

View File

@ -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();

View File

@ -56,7 +56,7 @@ use trans::cleanup;
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,

View File

@ -124,6 +124,7 @@ use trans::base;
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<K>(pub ast::NodeId, pub K);
pub type DropHintDatum<'tcx> = DropHint<Datum<'tcx, Lvalue>>;
pub type DropHintValue = DropHint<ValueRef>;
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) {

View File

@ -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<HintEntry<'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) }
}
// 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<NodeMap<ValueRef>>,
// Carries info about drop-flags for local bindings (longer term,
// paths) for the code being compiled.
pub lldropflag_hints: RefCell<DropFlagHintsMap<'tcx>>,
// The NodeId of the function, or -1 if it doesn't correspond to
// a user-defined function.
pub id: ast::NodeId,

View File

@ -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<ast::NodeId> {
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 @@ impl KindOps for Lvalue {
}
fn to_expr_kind(self) -> Expr {
LvalueExpr
LvalueExpr(self)
}
}
@ -319,14 +443,14 @@ impl KindOps for Expr {
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 @@ impl<'tcx> Datum<'tcx, Rvalue> {
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 @@ impl<'tcx> Datum<'tcx, Expr> {
{
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 @@ impl<'tcx> Datum<'tcx, Lvalue> {
};
Datum {
val: val,
kind: Lvalue,
kind: Lvalue::new("Datum::get_element"),
ty: ty,
}
}

View File

@ -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)))
}
}