| // Copyright 2012-2014 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 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| #![allow(non_camel_case_types)] |
| |
| 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_length; |
| |
| 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> { |
| 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, ""); |
| } 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))); |
| }; |
| |
| 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_length(bcx.tcx(), &count_expr, "repeat count").unwrap() { |
| 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_length(bcx.tcx(), &count_expr, "repeat count").unwrap() |
| } |
| _ => 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"), |
| } |
| } |
| |
| 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 |
| } else { |
| InBoundsGEP(bcx, data_ptr, &[loop_counter]) |
| }; |
| 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 cond_val = ICmp(bcx, llvm::IntULT, plusone, count, DebugLoc::None); |
| CondBr(bcx, cond_val, loop_bcx.llbb, next_bcx.llbb, DebugLoc::None); |
| |
| next_bcx |
| } |
| |
| 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 |
| } |
| } |