// Copyright 2012 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 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. /*! * * A `Datum` contains all the information you need to describe the LLVM * translation of a Rust value. It describes where the value is stored, * what Rust type the value has, whether it is addressed by reference, * and so forth. * * The idea of a datum is that, to the extent possible, you should not * care about these details, but rather use the methods on the Datum * type to "do what you want to do". For example, you can simply call * `copy_to()` or `move_to()` to copy or move the value into a new * home. * * # Datum location * * The primary two fields of a datum are the `val` and the `mode`. * The `val` is an LLVM value ref. It may either *be the value* that * is being tracked, or it may be a *pointer to the value being * tracked*. This is specified in the `mode` field, which can either * be `ByValue` or `ByRef`, respectively. The (Rust) type of the * value stored in the datum is indicated in the field `ty`. * * Generally speaking, you probably do not want to access the `val` field * unless you know what mode the value is in. Instead you should use one * of the following accessors: * * - `to_value_llval()` converts to by-value * - `to_ref_llval()` converts to by-ref, allocating a stack slot if necessary * - `to_appropriate_llval()` converts to by-value if this is an * immediate type, by-ref otherwise. This is particularly * convenient for interfacing with the various code floating around * that predates datums. * * # Datum cleanup styles * * Each datum carries with it an idea of how its value will be cleaned * up. This is primarily determined by the mode: a `ByValue` datum * will always be cleaned up by revoking cleanup using * `revoke_clean()`, because there is no other option. By ref datums * can sometimes be cleaned up via `revoke_clean` (in particular, * by-ref datums that originated from rvalues), but sometimes they * must be zeroed. This is indicated by the `DatumCleanup` * parameter. Note that zeroing a by-ref datum *always works* to * cancel the cleanup, but using `revoke_clean` is preferable since * there is no runtime cost. Some older parts of the code (notably * `match_`, at least at the time of this writing) rely on this and * only use zeroing. * * # Copying, moving, and storing * * There are three methods for moving the value into a new * location: * * - `copy_to()` will copy the value into a new location, meaning that * the value is first mem-copied and then the new location is "taken" * via the take glue, in effect creating a deep clone. * * - `move_to()` will copy the value, meaning that the value is mem-copied * into its new home and then the cleanup on the this datum is revoked. * This is a "shallow" clone. After `move_to()`, the current datum * is invalid and should no longer be used. * * - `store_to()` either performs a copy or a move by consulting the * moves_map computed by `middle::moves`. * * # Scratch datum * * Sometimes you just need some temporary scratch space. The * `scratch_datum()` function will yield you up a by-ref datum that * points into the stack. It's your responsibility to ensure that * whatever you put in there gets cleaned up etc. * * # Other actions * * There are various other helper methods on Datum, such as `deref()`, * `get_base_and_len()` and so forth. These are documented on the * methods themselves. Most are only suitable for some types of * values. */ use core::prelude::*; use lib; use lib::llvm::ValueRef; use middle::trans::adt; use middle::trans::base::*; use middle::trans::build::*; use middle::trans::common::*; use middle::trans::common; use middle::trans::expr; use middle::trans::glue; use middle::trans::tvec; use middle::trans::type_of; use middle::trans::write_guard; use middle::ty; use util::common::indenter; use util::ppaux::ty_to_str; use core::uint; use syntax::ast; use syntax::codemap::span; use syntax::parse::token::special_idents; #[deriving(Eq)] pub enum CopyAction { INIT, DROP_EXISTING } pub struct Datum { /// The llvm value. This is either a pointer to the Rust value or /// the value itself, depending on `mode` below. val: ValueRef, /// The rust type of the value. ty: ty::t, /// Indicates whether this is by-ref or by-value. mode: DatumMode, } pub struct DatumBlock { bcx: block, datum: Datum, } #[deriving(Eq, IterBytes)] pub enum DatumMode { /// `val` is a pointer to the actual value (and thus has type *T). /// The argument indicates how to cancel cleanup of this datum if /// the value is moved elsewhere, which can either be by zeroing /// the memory or by canceling a registered cleanup. ByRef(DatumCleanup), /// `val` is the actual value (*only used for immediates* like ints, ptrs) ByValue, } impl DatumMode { pub fn is_by_ref(&self) -> bool { match *self { ByRef(_) => true, ByValue => false } } pub fn is_by_value(&self) -> bool { match *self { ByRef(_) => false, ByValue => true } } } /// See `Datum cleanup styles` section at the head of this module. #[deriving(Eq, IterBytes)] pub enum DatumCleanup { RevokeClean, ZeroMem } pub fn immediate_rvalue(val: ValueRef, ty: ty::t) -> Datum { return Datum {val: val, ty: ty, mode: ByValue}; } pub fn immediate_rvalue_bcx(bcx: block, val: ValueRef, ty: ty::t) -> DatumBlock { return DatumBlock {bcx: bcx, datum: immediate_rvalue(val, ty)}; } pub fn scratch_datum(bcx: block, ty: ty::t, zero: bool) -> Datum { /*! * * Allocates temporary space on the stack using alloca() and * returns a by-ref Datum pointing to it. If `zero` is true, the * space will be zeroed when it is allocated; this is normally not * necessary, but in the case of automatic rooting in match * statements it is possible to have temporaries that may not get * initialized if a certain arm is not taken, so we must zero * them. You must arrange any cleanups etc yourself! */ let llty = type_of::type_of(bcx.ccx(), ty); let scratch = alloca_maybe_zeroed(bcx, llty, zero); Datum { val: scratch, ty: ty, mode: ByRef(RevokeClean) } } pub fn appropriate_mode(ty: ty::t) -> DatumMode { /*! * * Indicates the "appropriate" mode for this value, * which is either by ref or by value, depending * on whether type is immediate or not. */ if ty::type_is_nil(ty) || ty::type_is_bot(ty) { ByValue } else if ty::type_is_immediate(ty) { ByValue } else { ByRef(RevokeClean) } } impl Datum { pub fn store_to(&self, bcx: block, id: ast::node_id, action: CopyAction, dst: ValueRef) -> block { /*! * * Stores this value into its final home. This moves if * `id` is located in the move table, but copies otherwise. */ if bcx.ccx().maps.moves_map.contains(&id) { self.move_to(bcx, action, dst) } else { self.copy_to(bcx, action, dst) } } pub fn store_to_dest(&self, bcx: block, id: ast::node_id, dest: expr::Dest) -> block { match dest { expr::Ignore => { return bcx; } expr::SaveIn(addr) => { return self.store_to(bcx, id, INIT, addr); } } } pub fn store_to_datum(&self, bcx: block, id: ast::node_id, action: CopyAction, datum: Datum) -> block { debug!("store_to_datum(self=%s, action=%?, datum=%s)", self.to_str(bcx.ccx()), action, datum.to_str(bcx.ccx())); assert!(datum.mode.is_by_ref()); self.store_to(bcx, id, action, datum.val) } pub fn move_to_datum(&self, bcx: block, action: CopyAction, datum: Datum) -> block { assert!(datum.mode.is_by_ref()); self.move_to(bcx, action, datum.val) } pub fn copy_to_datum(&self, bcx: block, action: CopyAction, datum: Datum) -> block { assert!(datum.mode.is_by_ref()); self.copy_to(bcx, action, datum.val) } pub fn copy_to(&self, bcx: block, action: CopyAction, dst: ValueRef) -> block { /*! * * Copies the value into `dst`, which should be a pointer to a * memory location suitable for `self.ty`. You PROBABLY want * `store_to()` instead, which will move if possible but copy if * neccessary. */ let _icx = push_ctxt("copy_to"); if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) { return bcx; } debug!("copy_to(self=%s, action=%?, dst=%s)", self.to_str(bcx.ccx()), action, bcx.val_to_str(dst)); // Watch out for the case where we are writing the copying the // value into the same location we read it out from. We want // to avoid the case where we drop the existing value, which // frees it, and then overwrite it with itself (which has been // freed). if action == DROP_EXISTING && ty::type_needs_drop(bcx.tcx(), self.ty) { match self.mode { ByRef(_) => { let cast = PointerCast(bcx, dst, val_ty(self.val)); let cmp = ICmp(bcx, lib::llvm::IntNE, cast, self.val); do with_cond(bcx, cmp) |bcx| { self.copy_to_no_check(bcx, action, dst) } } ByValue => { self.copy_to_no_check(bcx, action, dst) } } } else { self.copy_to_no_check(bcx, action, dst) } } pub fn copy_to_no_check(&self, bcx: block, action: CopyAction, dst: ValueRef) -> block { /*! * * A helper for `copy_to()` which does not check to see if we * are copying to/from the same value. */ let _icx = push_ctxt("copy_to_no_check"); let mut bcx = bcx; if action == DROP_EXISTING { bcx = glue::drop_ty(bcx, dst, self.ty); } match self.mode { ByValue => { Store(bcx, self.val, dst); } ByRef(_) => { memcpy_ty(bcx, dst, self.val, self.ty); } } return glue::take_ty(bcx, dst, self.ty); } // This works like copy_val, except that it deinitializes the source. // Since it needs to zero out the source, src also needs to be an lval. // pub fn move_to(&self, bcx: block, action: CopyAction, dst: ValueRef) -> block { let _icx = push_ctxt("move_to"); let mut bcx = bcx; debug!("move_to(self=%s, action=%?, dst=%s)", self.to_str(bcx.ccx()), action, bcx.val_to_str(dst)); if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) { return bcx; } if action == DROP_EXISTING { bcx = glue::drop_ty(bcx, dst, self.ty); } match self.mode { ByRef(_) => { memcpy_ty(bcx, dst, self.val, self.ty); } ByValue => { Store(bcx, self.val, dst); } } self.cancel_clean(bcx); return bcx; } pub fn add_clean(&self, bcx: block) { /*! * Schedules this datum for cleanup in `bcx`. The datum * must be an rvalue. */ match self.mode { ByValue => { add_clean_temp_immediate(bcx, self.val, self.ty); } ByRef(RevokeClean) => { add_clean_temp_mem(bcx, self.val, self.ty); } ByRef(ZeroMem) => { bcx.tcx().sess.bug( fmt!("Cannot add clean to a 'zero-mem' datum")); } } } pub fn cancel_clean(&self, bcx: block) { if ty::type_needs_drop(bcx.tcx(), self.ty) { match self.mode { ByValue | ByRef(RevokeClean) => { revoke_clean(bcx, self.val); } ByRef(ZeroMem) => { // Lvalues which potentially need to be dropped // must be passed by ref, so that we can zero them // out. assert!(self.mode.is_by_ref()); zero_mem(bcx, self.val, self.ty); } } } } pub fn to_str(&self, ccx: &CrateContext) -> ~str { fmt!("Datum { val=%s, ty=%s, mode=%? }", ccx.tn.val_to_str(self.val), ty_to_str(ccx.tcx, self.ty), self.mode) } pub fn to_value_datum(&self, bcx: block) -> Datum { /*! * * Yields a by-ref form of this datum. This may involve * creation of a temporary stack slot. The value returned by * this function is not separately rooted from this datum, so * it will not live longer than the current datum. */ match self.mode { ByValue => *self, ByRef(_) => { Datum {val: self.to_value_llval(bcx), mode: ByValue, ty: self.ty} } } } pub fn to_value_llval(&self, bcx: block) -> ValueRef { /*! * * Yields the value itself. */ if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) { C_nil() } else { match self.mode { ByValue => self.val, ByRef(_) => { if ty::type_is_bool(self.ty) { LoadRangeAssert(bcx, self.val, 0, 2, lib::llvm::True) } else { Load(bcx, self.val) } } } } } pub fn to_ref_datum(&self, bcx: block) -> Datum { /*! * Yields a by-ref form of this datum. This may involve * creation of a temporary stack slot. The value returned by * this function is not separately rooted from this datum, so * it will not live longer than the current datum. */ match self.mode { ByRef(_) => *self, ByValue => { Datum {val: self.to_ref_llval(bcx), mode: ByRef(RevokeClean), ty: self.ty} } } } pub fn to_ref_llval(&self, bcx: block) -> ValueRef { match self.mode { ByRef(_) => self.val, ByValue => { if ty::type_is_nil(self.ty) || ty::type_is_bot(self.ty) { C_null(type_of::type_of(bcx.ccx(), self.ty).ptr_to()) } else { let slot = alloc_ty(bcx, self.ty); Store(bcx, self.val, slot); slot } } } } pub fn to_zeroable_ref_llval(&self, bcx: block) -> ValueRef { /*! * Returns a by-ref llvalue that can be zeroed in order to * cancel cleanup. This is a kind of hokey bridge used * to adapt to the match code. Please don't use it for new code. */ match self.mode { // All by-ref datums are zeroable, even if we *could* just // cancel the cleanup. ByRef(_) => self.val, // By value datums can't be zeroed (where would you store // the zero?) so we have to spill them. Add a temp cleanup // for this spilled value and cancel the cleanup on this // current value. ByValue => { let slot = self.to_ref_llval(bcx); self.cancel_clean(bcx); add_clean_temp_mem(bcx, slot, self.ty); slot } } } pub fn appropriate_mode(&self) -> DatumMode { /*! See the `appropriate_mode()` function */ appropriate_mode(self.ty) } pub fn to_appropriate_llval(&self, bcx: block) -> ValueRef { /*! * * Yields an llvalue with the `appropriate_mode()`. */ match self.appropriate_mode() { ByValue => self.to_value_llval(bcx), ByRef(_) => self.to_ref_llval(bcx) } } pub fn to_appropriate_datum(&self, bcx: block) -> Datum { /*! * * Yields a datum with the `appropriate_mode()`. */ match self.appropriate_mode() { ByValue => self.to_value_datum(bcx), ByRef(_) => self.to_ref_datum(bcx) } } pub fn get_element(&self, bcx: block, ty: ty::t, source: DatumCleanup, gep: &fn(ValueRef) -> ValueRef) -> Datum { let base_val = self.to_ref_llval(bcx); Datum { val: gep(base_val), mode: ByRef(source), ty: ty, } } pub fn drop_val(&self, bcx: block) -> block { if !ty::type_needs_drop(bcx.tcx(), self.ty) { return bcx; } return match self.mode { ByRef(_) => glue::drop_ty(bcx, self.val, self.ty), ByValue => glue::drop_ty_immediate(bcx, self.val, self.ty) }; } pub fn box_body(&self, bcx: block) -> Datum { /*! * * This datum must represent an @T or ~T box. Returns a new * by-ref datum of type T, pointing at the contents. */ let content_ty = match ty::get(self.ty).sty { ty::ty_box(mt) | ty::ty_uniq(mt) => mt.ty, _ => { bcx.tcx().sess.bug(fmt!( "box_body() invoked on non-box type %s", ty_to_str(bcx.tcx(), self.ty))); } }; let ptr = self.to_value_llval(bcx); let body = opaque_box_body(bcx, content_ty, ptr); Datum {val: body, ty: content_ty, mode: ByRef(ZeroMem)} } pub fn to_rptr(&self, bcx: block) -> Datum { //! Returns a new datum of region-pointer type containing the //! the same ptr as this datum (after converting to by-ref //! using `to_ref_llval()`). // Convert to ref, yielding lltype *T. Then create a Rust // type &'static T (which translates to *T). Construct new // result (which will be by-value). Note that it is not // significant *which* region we pick here. let llval = self.to_ref_llval(bcx); let rptr_ty = ty::mk_imm_rptr(bcx.tcx(), ty::re_static, self.ty); Datum {val: llval, ty: rptr_ty, mode: ByValue} } /// bcx: Block wherein to generate insns. /// span: Location where deref occurs. /// expr_id: ID of deref expr. /// derefs: Number of times deref'd already. /// is_auto: If true, only deref if auto-derefable. pub fn try_deref(&self, bcx: block, span: span, expr_id: ast::node_id, derefs: uint, is_auto: bool) -> (Option, block) { let ccx = bcx.ccx(); debug!("try_deref(expr_id=%?, derefs=%?, is_auto=%b, self=%?)", expr_id, derefs, is_auto, self.to_str(bcx.ccx())); let bcx = write_guard::root_and_write_guard( self, bcx, span, expr_id, derefs); match ty::get(self.ty).sty { ty::ty_box(_) | ty::ty_uniq(_) => { return (Some(self.box_body(bcx)), bcx); } ty::ty_ptr(mt) => { if is_auto { // unsafe ptrs are not AUTO-derefable return (None, bcx); } else { return (Some(deref_ptr(bcx, self, mt.ty)), bcx); } } ty::ty_rptr(_, mt) => { return (Some(deref_ptr(bcx, self, mt.ty)), bcx); } ty::ty_enum(did, ref substs) => { // Check whether this enum is a newtype enum: let variants = ty::enum_variants(ccx.tcx, did); if (*variants).len() != 1 || variants[0].args.len() != 1 { return (None, bcx); } let repr = adt::represent_type(ccx, self.ty); let ty = ty::subst(ccx.tcx, substs, variants[0].args[0]); return match self.mode { ByRef(_) => { // Recast lv.val as a pointer to the newtype // rather than a ptr to the enum type. ( Some(Datum { val: adt::trans_field_ptr(bcx, repr, self.val, 0, 0), ty: ty, mode: ByRef(ZeroMem) }), bcx ) } ByValue => { // Actually, this case cannot happen right // now, because enums are never immediate. // But in principle newtype'd immediate // values should be immediate, and in that // case the * would be a no-op except for // changing the type, so I am putting this // code in place here to do the right // thing if this change ever goes through. assert!(ty::type_is_immediate(ty)); (Some(Datum {ty: ty, ..*self}), bcx) } }; } ty::ty_struct(did, ref substs) => { // Check whether this struct is a newtype struct. let fields = ty::struct_fields(ccx.tcx, did, substs); if fields.len() != 1 || fields[0].ident != special_idents::unnamed_field { return (None, bcx); } let repr = adt::represent_type(ccx, self.ty); let ty = fields[0].mt.ty; return match self.mode { ByRef(_) => { // Recast lv.val as a pointer to the newtype rather // than a pointer to the struct type. // FIXME #6572: This isn't correct for structs with // destructors. ( Some(Datum { val: adt::trans_field_ptr(bcx, repr, self.val, 0, 0), ty: ty, mode: ByRef(ZeroMem) }), bcx ) } ByValue => { // Actually, this case cannot happen right now, // because structs are never immediate. But in // principle, newtype'd immediate values should be // immediate, and in that case the * would be a no-op // except for changing the type, so I am putting this // code in place here to do the right thing if this // change ever goes through. assert!(ty::type_is_immediate(ty)); (Some(Datum {ty: ty, ..*self}), bcx) } } } _ => { // not derefable. return (None, bcx); } } fn deref_ptr(bcx: block, lv: &Datum, ty: ty::t) -> Datum { Datum { val: lv.to_value_llval(bcx), ty: ty, mode: ByRef(ZeroMem) } } } /// expr: The deref expression. pub fn deref(&self, bcx: block, expr: &ast::expr, derefs: uint) -> DatumBlock { match self.try_deref(bcx, expr.span, expr.id, derefs, false) { (Some(lvres), bcx) => DatumBlock { bcx: bcx, datum: lvres }, (None, _) => { bcx.ccx().sess.span_bug(expr.span, "Cannot deref this expression"); } } } pub fn autoderef(&self, bcx: block, span: span, expr_id: ast::node_id, max: uint) -> DatumBlock { let _icx = push_ctxt("autoderef"); debug!("autoderef(expr_id=%d, max=%?, self=%?)", expr_id, max, self.to_str(bcx.ccx())); let _indenter = indenter(); let mut datum = *self; let mut derefs = 0u; let mut bcx = bcx; while derefs < max { derefs += 1u; match datum.try_deref(bcx, span, expr_id, derefs, true) { (None, new_bcx) => { bcx = new_bcx; break } (Some(datum_deref), new_bcx) => { datum = datum_deref; bcx = new_bcx; } } } // either we were asked to deref a specific number of times, // in which case we should have, or we asked to deref as many // times as we can assert!(derefs == max || max == uint::max_value); DatumBlock { bcx: bcx, datum: datum } } pub fn get_vec_base_and_len(&self, mut bcx: block, span: span, expr_id: ast::node_id, derefs: uint) -> (block, ValueRef, ValueRef) { //! Converts a vector into the slice pair. Performs rooting //! and write guards checks. // only imp't for @[] and @str, but harmless bcx = write_guard::root_and_write_guard(self, bcx, span, expr_id, derefs); let (base, len) = self.get_vec_base_and_len_no_root(bcx); (bcx, base, len) } pub fn get_vec_base_and_len_no_root(&self, bcx: block) -> (ValueRef, ValueRef) { //! Converts a vector into the slice pair. Des not root //! nor perform write guard checks. let llval = self.to_appropriate_llval(bcx); tvec::get_base_and_len(bcx, llval, self.ty) } pub fn root_and_write_guard(&self, bcx: block, span: span, expr_id: ast::node_id, derefs: uint) -> block { write_guard::root_and_write_guard(self, bcx, span, expr_id, derefs) } pub fn to_result(&self, bcx: block) -> common::Result { rslt(bcx, self.to_appropriate_llval(bcx)) } } impl DatumBlock { pub fn unpack(&self, bcx: &mut block) -> Datum { *bcx = self.bcx; return self.datum; } pub fn assert_by_ref(&self) -> DatumBlock { assert!(self.datum.mode.is_by_ref()); *self } pub fn drop_val(&self) -> block { self.datum.drop_val(self.bcx) } pub fn store_to(&self, id: ast::node_id, action: CopyAction, dst: ValueRef) -> block { self.datum.store_to(self.bcx, id, action, dst) } pub fn copy_to(&self, action: CopyAction, dst: ValueRef) -> block { self.datum.copy_to(self.bcx, action, dst) } pub fn move_to(&self, action: CopyAction, dst: ValueRef) -> block { self.datum.move_to(self.bcx, action, dst) } pub fn to_value_llval(&self) -> ValueRef { self.datum.to_value_llval(self.bcx) } pub fn to_result(&self) -> common::Result { rslt(self.bcx, self.datum.to_appropriate_llval(self.bcx)) } pub fn ccx(&self) -> @mut CrateContext { self.bcx.ccx() } pub fn tcx(&self) -> ty::ctxt { self.bcx.tcx() } pub fn to_str(&self) -> ~str { self.datum.to_str(self.ccx()) } }