use llvm;
use llvm::ValueRef;
use base::*;
-use base;
use build::*;
-use cleanup;
-use cleanup::CleanupMethods;
use common::*;
-use consts;
-use datum::*;
use debuginfo::DebugLoc;
-use expr::{Dest, Ignore, SaveIn};
-use expr;
-use machine::llsize_of_alloc;
-use type_::Type;
-use type_of;
-use value::Value;
-use rustc::ty::{self, Ty};
-
-use rustc::hir;
-use rustc_const_eval::eval_repeat_count;
-
-use syntax::ast;
-use syntax::parse::token::InternedString;
-
-#[derive(Copy, Clone, Debug)]
-struct VecTypes<'tcx> {
- unit_ty: Ty<'tcx>,
- llunit_ty: Type
-}
-
-pub fn trans_fixed_vstore<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
- expr: &hir::Expr,
- dest: expr::Dest)
- -> Block<'blk, 'tcx> {
- //!
- //
- // [...] allocates a fixed-size array and moves it around "by value".
- // In this case, it means that the caller has already given us a location
- // to store the array of the suitable size, so all we have to do is
- // generate the content.
-
- debug!("trans_fixed_vstore(expr={:?}, dest={:?})", expr, dest);
-
- let vt = vec_types_from_expr(bcx, expr);
-
- return match dest {
- Ignore => write_content(bcx, &vt, expr, expr, dest),
- SaveIn(lldest) => {
- // lldest will have type *[T x N], but we want the type *T,
- // so use GEP to convert:
- let lldest = StructGEP(bcx, lldest, 0);
- write_content(bcx, &vt, expr, expr, SaveIn(lldest))
- }
- };
-}
-
-/// &[...] allocates memory on the stack and writes the values into it, returning the vector (the
-/// caller must make the reference). "..." is similar except that the memory can be statically
-/// allocated and we return a reference (strings are always by-ref).
-pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
- slice_expr: &hir::Expr,
- content_expr: &hir::Expr)
- -> DatumBlock<'blk, 'tcx, Expr> {
+use rustc::ty::Ty;
+
+pub fn slice_for_each<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
+ data_ptr: ValueRef,
+ unit_ty: Ty<'tcx>,
+ len: ValueRef,
+ f: F)
+ -> Block<'blk, 'tcx> where
+ F: FnOnce(Block<'blk, 'tcx>, ValueRef) -> Block<'blk, 'tcx>,
+{
+ let _icx = push_ctxt("tvec::slice_for_each");
let fcx = bcx.fcx;
- let mut bcx = bcx;
-
- debug!("trans_slice_vec(slice_expr={:?})",
- slice_expr);
-
- let vec_ty = node_id_type(bcx, slice_expr.id);
-
- // Handle the "..." case (returns a slice since strings are always unsized):
- if let hir::ExprLit(ref lit) = content_expr.node {
- if let ast::LitKind::Str(ref s, _) = lit.node {
- let scratch = rvalue_scratch_datum(bcx, vec_ty, "");
- bcx = trans_lit_str(bcx,
- content_expr,
- s.clone(),
- SaveIn(scratch.val));
- return DatumBlock::new(bcx, scratch.to_expr_datum());
- }
- }
-
- // Handle the &[...] case:
- let vt = vec_types_from_expr(bcx, content_expr);
- let count = elements_required(bcx, content_expr);
- debug!(" vt={:?}, count={}", vt, count);
- let fixed_ty = bcx.tcx().mk_array(vt.unit_ty, count);
-
- // Always create an alloca even if zero-sized, to preserve
- // the non-null invariant of the inner slice ptr
- let llfixed;
- // Issue 30018: ensure state is initialized as dropped if necessary.
- if fcx.type_needs_drop(vt.unit_ty) {
- llfixed = base::alloc_ty_init(bcx, fixed_ty, InitAlloca::Dropped, "");
+ // Special-case vectors with elements of size 0 so they don't go out of bounds (#9890)
+ let zst = type_is_zero_size(bcx.ccx(), unit_ty);
+ let add = |bcx, a, b| if zst {
+ Add(bcx, a, b, DebugLoc::None)
} else {
- let uninit = InitAlloca::Uninit("fcx says vt.unit_ty is non-drop");
- llfixed = base::alloc_ty_init(bcx, fixed_ty, uninit, "");
- call_lifetime_start(bcx, llfixed);
- };
-
- if count > 0 {
- // 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, None);
-
- // Generate the content into the backing array.
- // llfixed has type *[T x N], but we want the type *T,
- // so use GEP to convert
- bcx = write_content(bcx, &vt, slice_expr, content_expr,
- SaveIn(StructGEP(bcx, llfixed, 0)));
+ InBoundsGEP(bcx, a, &[b])
};
- immediate_rvalue_bcx(bcx, llfixed, vec_ty).to_expr_datumblock()
-}
-
-/// Literal strings translate to slices into static memory. This is different from
-/// trans_slice_vstore() above because it doesn't need to copy the content anywhere.
-pub fn trans_lit_str<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
- lit_expr: &hir::Expr,
- str_lit: InternedString,
- dest: Dest)
- -> Block<'blk, 'tcx> {
- debug!("trans_lit_str(lit_expr={:?}, dest={:?})", lit_expr, dest);
-
- match dest {
- Ignore => bcx,
- SaveIn(lldest) => {
- let bytes = str_lit.len();
- let llbytes = C_uint(bcx.ccx(), bytes);
- let llcstr = C_cstr(bcx.ccx(), str_lit, false);
- let llcstr = consts::ptrcast(llcstr, Type::i8p(bcx.ccx()));
- Store(bcx, llcstr, expr::get_dataptr(bcx, lldest));
- Store(bcx, llbytes, expr::get_meta(bcx, lldest));
- bcx
- }
- }
-}
-
-fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
- vt: &VecTypes<'tcx>,
- vstore_expr: &hir::Expr,
- content_expr: &hir::Expr,
- dest: Dest)
- -> Block<'blk, 'tcx> {
- let _icx = push_ctxt("tvec::write_content");
- let fcx = bcx.fcx;
- let mut bcx = bcx;
-
- debug!("write_content(vt={:?}, dest={:?}, vstore_expr={:?})",
- vt, dest, vstore_expr);
-
- match content_expr.node {
- hir::ExprLit(ref lit) => {
- match lit.node {
- ast::LitKind::Str(ref s, _) => {
- match dest {
- Ignore => return bcx,
- SaveIn(lldest) => {
- let bytes = s.len();
- let llbytes = C_uint(bcx.ccx(), bytes);
- let llcstr = C_cstr(bcx.ccx(), (*s).clone(), false);
- if !bcx.unreachable.get() {
- base::call_memcpy(&B(bcx), lldest, llcstr, llbytes, 1);
- }
- return bcx;
- }
- }
- }
- _ => {
- span_bug!(content_expr.span, "unexpected evec content");
- }
- }
- }
- hir::ExprVec(ref elements) => {
- match dest {
- Ignore => {
- for element in elements {
- bcx = expr::trans_into(bcx, &element, Ignore);
- }
- }
-
- SaveIn(lldest) => {
- let temp_scope = fcx.push_custom_cleanup_scope();
- for (i, element) in elements.iter().enumerate() {
- let lleltptr = GEPi(bcx, lldest, &[i]);
- debug!("writing index {} with lleltptr={:?}",
- i, Value(lleltptr));
- bcx = expr::trans_into(bcx, &element,
- SaveIn(lleltptr));
- let scope = cleanup::CustomScope(temp_scope);
- // Issue #30822: mark memory as dropped after running destructor
- fcx.schedule_drop_and_fill_mem(scope, lleltptr, vt.unit_ty, None);
- }
- fcx.pop_custom_cleanup_scope(temp_scope);
- }
- }
- return bcx;
- }
- hir::ExprRepeat(ref element, ref count_expr) => {
- match dest {
- Ignore => {
- return expr::trans_into(bcx, &element, Ignore);
- }
- SaveIn(lldest) => {
- match eval_repeat_count(bcx.tcx(), &count_expr) {
- 0 => expr::trans_into(bcx, &element, Ignore),
- 1 => expr::trans_into(bcx, &element, SaveIn(lldest)),
- count => {
- let elem = unpack_datum!(bcx, expr::trans(bcx, &element));
- let bcx = iter_vec_loop(bcx, lldest, vt,
- C_uint(bcx.ccx(), count),
- |set_bcx, lleltptr, _| {
- elem.shallow_copy(set_bcx, lleltptr)
- });
- bcx
- }
- }
- }
- }
- }
- _ => {
- span_bug!(content_expr.span, "unexpected vec content");
- }
- }
-}
-
-fn vec_types_from_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, vec_expr: &hir::Expr)
- -> VecTypes<'tcx> {
- let vec_ty = node_id_type(bcx, vec_expr.id);
- vec_types(bcx, vec_ty.sequence_element_type(bcx.tcx()))
-}
-
-fn vec_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, unit_ty: Ty<'tcx>)
- -> VecTypes<'tcx> {
- VecTypes {
- unit_ty: unit_ty,
- llunit_ty: type_of::type_of(bcx.ccx(), unit_ty)
- }
-}
-
-fn elements_required(bcx: Block, content_expr: &hir::Expr) -> usize {
- //! Figure out the number of elements we need to store this content
-
- match content_expr.node {
- hir::ExprLit(ref lit) => {
- match lit.node {
- ast::LitKind::Str(ref s, _) => s.len(),
- _ => {
- span_bug!(content_expr.span, "unexpected evec content")
- }
- }
- },
- hir::ExprVec(ref es) => es.len(),
- hir::ExprRepeat(_, ref count_expr) => {
- eval_repeat_count(bcx.tcx(), &count_expr)
- }
- _ => span_bug!(content_expr.span, "unexpected vec content")
- }
-}
-
-/// Converts a fixed-length vector into the slice pair. The vector should be stored in `llval`
-/// which should be by ref.
-pub fn get_fixed_base_and_len(bcx: Block,
- llval: ValueRef,
- vec_length: usize)
- -> (ValueRef, ValueRef) {
- let ccx = bcx.ccx();
-
- let base = expr::get_dataptr(bcx, llval);
- let len = C_uint(ccx, vec_length);
- (base, len)
-}
-
-/// Converts a vector into the slice pair. The vector should be stored in `llval` which should be
-/// by-reference. If you have a datum, you would probably prefer to call
-/// `Datum::get_base_and_len()` which will handle any conversions for you.
-pub fn get_base_and_len<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
- llval: ValueRef,
- vec_ty: Ty<'tcx>)
- -> (ValueRef, ValueRef) {
- match vec_ty.sty {
- ty::TyArray(_, n) => get_fixed_base_and_len(bcx, llval, n),
- ty::TySlice(_) | ty::TyStr => {
- let base = Load(bcx, expr::get_dataptr(bcx, llval));
- let len = Load(bcx, expr::get_meta(bcx, llval));
- (base, len)
- }
-
- // Only used for pattern matching.
- ty::TyBox(ty) | ty::TyRef(_, ty::TypeAndMut{ty, ..}) => {
- let inner = if type_is_sized(bcx.tcx(), ty) {
- Load(bcx, llval)
- } else {
- llval
- };
- get_base_and_len(bcx, inner, ty)
- },
- _ => bug!("unexpected type in get_base_and_len"),
- }
-}
+ let header_bcx = fcx.new_block("slice_loop_header");
+ let body_bcx = fcx.new_block("slice_loop_body");
+ let next_bcx = fcx.new_block("slice_loop_next");
-fn iter_vec_loop<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
- data_ptr: ValueRef,
- vt: &VecTypes<'tcx>,
- count: ValueRef,
- f: F)
- -> Block<'blk, 'tcx> where
- F: FnOnce(Block<'blk, 'tcx>, ValueRef, Ty<'tcx>) -> Block<'blk, 'tcx>,
-{
- let _icx = push_ctxt("tvec::iter_vec_loop");
-
- if bcx.unreachable.get() {
- return bcx;
- }
-
- let fcx = bcx.fcx;
- let loop_bcx = fcx.new_temp_block("expr_repeat");
- let next_bcx = fcx.new_temp_block("expr_repeat: next");
-
- Br(bcx, loop_bcx.llbb, DebugLoc::None);
-
- let loop_counter = Phi(loop_bcx, bcx.ccx().int_type(),
- &[C_uint(bcx.ccx(), 0 as usize)], &[bcx.llbb]);
-
- let bcx = loop_bcx;
-
- let lleltptr = if llsize_of_alloc(bcx.ccx(), vt.llunit_ty) == 0 {
- data_ptr
+ let start = if zst {
+ C_uint(bcx.ccx(), 0 as usize)
} else {
- InBoundsGEP(bcx, data_ptr, &[loop_counter])
+ data_ptr
};
- let bcx = f(bcx, lleltptr, vt.unit_ty);
- let plusone = Add(bcx, loop_counter, C_uint(bcx.ccx(), 1usize), DebugLoc::None);
- AddIncomingToPhi(loop_counter, plusone, bcx.llbb);
+ let end = add(bcx, start, len);
- let cond_val = ICmp(bcx, llvm::IntULT, plusone, count, DebugLoc::None);
- CondBr(bcx, cond_val, loop_bcx.llbb, next_bcx.llbb, DebugLoc::None);
+ Br(bcx, header_bcx.llbb, DebugLoc::None);
+ let current = Phi(header_bcx, val_ty(start), &[start], &[bcx.llbb]);
- next_bcx
-}
+ let keep_going =
+ ICmp(header_bcx, llvm::IntULT, current, end, DebugLoc::None);
+ CondBr(header_bcx, keep_going, body_bcx.llbb, next_bcx.llbb, DebugLoc::None);
-pub fn iter_vec_raw<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
- data_ptr: ValueRef,
- unit_ty: Ty<'tcx>,
- len: ValueRef,
- f: F)
- -> Block<'blk, 'tcx> where
- F: FnOnce(Block<'blk, 'tcx>, ValueRef, Ty<'tcx>) -> Block<'blk, 'tcx>,
-{
- let _icx = push_ctxt("tvec::iter_vec_raw");
- let fcx = bcx.fcx;
-
- let vt = vec_types(bcx, unit_ty);
-
- if llsize_of_alloc(bcx.ccx(), vt.llunit_ty) == 0 {
- // Special-case vectors with elements of size 0 so they don't go out of bounds (#9890)
- iter_vec_loop(bcx, data_ptr, &vt, len, f)
- } else {
- // Calculate the last pointer address we want to handle.
- let data_end_ptr = InBoundsGEP(bcx, data_ptr, &[len]);
-
- // Now perform the iteration.
- let header_bcx = fcx.new_temp_block("iter_vec_loop_header");
- Br(bcx, header_bcx.llbb, DebugLoc::None);
- let data_ptr =
- Phi(header_bcx, val_ty(data_ptr), &[data_ptr], &[bcx.llbb]);
- let not_yet_at_end =
- ICmp(header_bcx, llvm::IntULT, data_ptr, data_end_ptr, DebugLoc::None);
- let body_bcx = fcx.new_temp_block("iter_vec_loop_body");
- let next_bcx = fcx.new_temp_block("iter_vec_next");
- CondBr(header_bcx, not_yet_at_end, body_bcx.llbb, next_bcx.llbb, DebugLoc::None);
- let body_bcx = f(body_bcx, data_ptr, unit_ty);
- AddIncomingToPhi(data_ptr, InBoundsGEP(body_bcx, data_ptr,
- &[C_int(bcx.ccx(), 1)]),
- body_bcx.llbb);
- Br(body_bcx, header_bcx.llbb, DebugLoc::None);
- next_bcx
- }
+ let body_bcx = f(body_bcx, if zst { data_ptr } else { current });
+ let next = add(body_bcx, current, C_uint(bcx.ccx(), 1usize));
+ AddIncomingToPhi(current, next, body_bcx.llbb);
+ Br(body_bcx, header_bcx.llbb, DebugLoc::None);
+ next_bcx
}