blob: 92a2d3787bfd6b60bc5184233f24893e10e91ab3 [file] [log] [blame]
// 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
}
}