]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/tvec.rs
Auto merge of #35004 - asomers:master, r=alexcrichton
[rust.git] / src / librustc_trans / tvec.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 #![allow(non_camel_case_types)]
12
13 use llvm;
14 use llvm::ValueRef;
15 use base::*;
16 use base;
17 use build::*;
18 use cleanup;
19 use cleanup::CleanupMethods;
20 use common::*;
21 use consts;
22 use datum::*;
23 use debuginfo::DebugLoc;
24 use expr::{Dest, Ignore, SaveIn};
25 use expr;
26 use machine::llsize_of_alloc;
27 use type_::Type;
28 use type_of;
29 use value::Value;
30 use rustc::ty::{self, Ty};
31
32 use rustc::hir;
33 use rustc_const_eval::eval_length;
34
35 use syntax::ast;
36 use syntax::parse::token::InternedString;
37
38 #[derive(Copy, Clone, Debug)]
39 struct VecTypes<'tcx> {
40     unit_ty: Ty<'tcx>,
41     llunit_ty: Type
42 }
43
44 pub fn trans_fixed_vstore<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
45                                       expr: &hir::Expr,
46                                       dest: expr::Dest)
47                                       -> Block<'blk, 'tcx> {
48     //!
49     //
50     // [...] allocates a fixed-size array and moves it around "by value".
51     // In this case, it means that the caller has already given us a location
52     // to store the array of the suitable size, so all we have to do is
53     // generate the content.
54
55     debug!("trans_fixed_vstore(expr={:?}, dest={:?})", expr, dest);
56
57     let vt = vec_types_from_expr(bcx, expr);
58
59     return match dest {
60         Ignore => write_content(bcx, &vt, expr, expr, dest),
61         SaveIn(lldest) => {
62             // lldest will have type *[T x N], but we want the type *T,
63             // so use GEP to convert:
64             let lldest = StructGEP(bcx, lldest, 0);
65             write_content(bcx, &vt, expr, expr, SaveIn(lldest))
66         }
67     };
68 }
69
70 /// &[...] allocates memory on the stack and writes the values into it, returning the vector (the
71 /// caller must make the reference).  "..." is similar except that the memory can be statically
72 /// allocated and we return a reference (strings are always by-ref).
73 pub fn trans_slice_vec<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
74                                    slice_expr: &hir::Expr,
75                                    content_expr: &hir::Expr)
76                                    -> DatumBlock<'blk, 'tcx, Expr> {
77     let fcx = bcx.fcx;
78     let mut bcx = bcx;
79
80     debug!("trans_slice_vec(slice_expr={:?})",
81            slice_expr);
82
83     let vec_ty = node_id_type(bcx, slice_expr.id);
84
85     // Handle the "..." case (returns a slice since strings are always unsized):
86     if let hir::ExprLit(ref lit) = content_expr.node {
87         if let ast::LitKind::Str(ref s, _) = lit.node {
88             let scratch = rvalue_scratch_datum(bcx, vec_ty, "");
89             bcx = trans_lit_str(bcx,
90                                 content_expr,
91                                 s.clone(),
92                                 SaveIn(scratch.val));
93             return DatumBlock::new(bcx, scratch.to_expr_datum());
94         }
95     }
96
97     // Handle the &[...] case:
98     let vt = vec_types_from_expr(bcx, content_expr);
99     let count = elements_required(bcx, content_expr);
100     debug!("    vt={:?}, count={}", vt, count);
101
102     let fixed_ty = bcx.tcx().mk_array(vt.unit_ty, count);
103
104     // Always create an alloca even if zero-sized, to preserve
105     // the non-null invariant of the inner slice ptr
106     let llfixed;
107     // Issue 30018: ensure state is initialized as dropped if necessary.
108     if fcx.type_needs_drop(vt.unit_ty) {
109         llfixed = base::alloc_ty_init(bcx, fixed_ty, InitAlloca::Dropped, "");
110     } else {
111         let uninit = InitAlloca::Uninit("fcx says vt.unit_ty is non-drop");
112         llfixed = base::alloc_ty_init(bcx, fixed_ty, uninit, "");
113         call_lifetime_start(bcx, llfixed);
114     };
115
116     if count > 0 {
117         // Arrange for the backing array to be cleaned up.
118         let cleanup_scope = cleanup::temporary_scope(bcx.tcx(), content_expr.id);
119         fcx.schedule_lifetime_end(cleanup_scope, llfixed);
120         fcx.schedule_drop_mem(cleanup_scope, llfixed, fixed_ty, None);
121
122         // Generate the content into the backing array.
123         // llfixed has type *[T x N], but we want the type *T,
124         // so use GEP to convert
125         bcx = write_content(bcx, &vt, slice_expr, content_expr,
126                             SaveIn(StructGEP(bcx, llfixed, 0)));
127     };
128
129     immediate_rvalue_bcx(bcx, llfixed, vec_ty).to_expr_datumblock()
130 }
131
132 /// Literal strings translate to slices into static memory.  This is different from
133 /// trans_slice_vstore() above because it doesn't need to copy the content anywhere.
134 pub fn trans_lit_str<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
135                                  lit_expr: &hir::Expr,
136                                  str_lit: InternedString,
137                                  dest: Dest)
138                                  -> Block<'blk, 'tcx> {
139     debug!("trans_lit_str(lit_expr={:?}, dest={:?})", lit_expr, dest);
140
141     match dest {
142         Ignore => bcx,
143         SaveIn(lldest) => {
144             let bytes = str_lit.len();
145             let llbytes = C_uint(bcx.ccx(), bytes);
146             let llcstr = C_cstr(bcx.ccx(), str_lit, false);
147             let llcstr = consts::ptrcast(llcstr, Type::i8p(bcx.ccx()));
148             Store(bcx, llcstr, expr::get_dataptr(bcx, lldest));
149             Store(bcx, llbytes, expr::get_meta(bcx, lldest));
150             bcx
151         }
152     }
153 }
154
155 fn write_content<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
156                              vt: &VecTypes<'tcx>,
157                              vstore_expr: &hir::Expr,
158                              content_expr: &hir::Expr,
159                              dest: Dest)
160                              -> Block<'blk, 'tcx> {
161     let _icx = push_ctxt("tvec::write_content");
162     let fcx = bcx.fcx;
163     let mut bcx = bcx;
164
165     debug!("write_content(vt={:?}, dest={:?}, vstore_expr={:?})",
166            vt, dest, vstore_expr);
167
168     match content_expr.node {
169         hir::ExprLit(ref lit) => {
170             match lit.node {
171                 ast::LitKind::Str(ref s, _) => {
172                     match dest {
173                         Ignore => return bcx,
174                         SaveIn(lldest) => {
175                             let bytes = s.len();
176                             let llbytes = C_uint(bcx.ccx(), bytes);
177                             let llcstr = C_cstr(bcx.ccx(), (*s).clone(), false);
178                             if !bcx.unreachable.get() {
179                                 base::call_memcpy(&B(bcx), lldest, llcstr, llbytes, 1);
180                             }
181                             return bcx;
182                         }
183                     }
184                 }
185                 _ => {
186                     span_bug!(content_expr.span, "unexpected evec content");
187                 }
188             }
189         }
190         hir::ExprVec(ref elements) => {
191             match dest {
192                 Ignore => {
193                     for element in elements {
194                         bcx = expr::trans_into(bcx, &element, Ignore);
195                     }
196                 }
197
198                 SaveIn(lldest) => {
199                     let temp_scope = fcx.push_custom_cleanup_scope();
200                     for (i, element) in elements.iter().enumerate() {
201                         let lleltptr = GEPi(bcx, lldest, &[i]);
202                         debug!("writing index {} with lleltptr={:?}",
203                                i, Value(lleltptr));
204                         bcx = expr::trans_into(bcx, &element,
205                                                SaveIn(lleltptr));
206                         let scope = cleanup::CustomScope(temp_scope);
207                         // Issue #30822: mark memory as dropped after running destructor
208                         fcx.schedule_drop_and_fill_mem(scope, lleltptr, vt.unit_ty, None);
209                     }
210                     fcx.pop_custom_cleanup_scope(temp_scope);
211                 }
212             }
213             return bcx;
214         }
215         hir::ExprRepeat(ref element, ref count_expr) => {
216             match dest {
217                 Ignore => {
218                     return expr::trans_into(bcx, &element, Ignore);
219                 }
220                 SaveIn(lldest) => {
221                     match eval_length(bcx.tcx(), &count_expr, "repeat count").unwrap() {
222                         0 => expr::trans_into(bcx, &element, Ignore),
223                         1 => expr::trans_into(bcx, &element, SaveIn(lldest)),
224                         count => {
225                             let elem = unpack_datum!(bcx, expr::trans(bcx, &element));
226                             let bcx = iter_vec_loop(bcx, lldest, vt,
227                                                     C_uint(bcx.ccx(), count),
228                                                     |set_bcx, lleltptr, _| {
229                                                         elem.shallow_copy(set_bcx, lleltptr)
230                                                     });
231                             bcx
232                         }
233                     }
234                 }
235             }
236         }
237         _ => {
238             span_bug!(content_expr.span, "unexpected vec content");
239         }
240     }
241 }
242
243 fn vec_types_from_expr<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, vec_expr: &hir::Expr)
244                                    -> VecTypes<'tcx> {
245     let vec_ty = node_id_type(bcx, vec_expr.id);
246     vec_types(bcx, vec_ty.sequence_element_type(bcx.tcx()))
247 }
248
249 fn vec_types<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, unit_ty: Ty<'tcx>)
250                          -> VecTypes<'tcx> {
251     VecTypes {
252         unit_ty: unit_ty,
253         llunit_ty: type_of::type_of(bcx.ccx(), unit_ty)
254     }
255 }
256
257 fn elements_required(bcx: Block, content_expr: &hir::Expr) -> usize {
258     //! Figure out the number of elements we need to store this content
259
260     match content_expr.node {
261         hir::ExprLit(ref lit) => {
262             match lit.node {
263                 ast::LitKind::Str(ref s, _) => s.len(),
264                 _ => {
265                     span_bug!(content_expr.span, "unexpected evec content")
266                 }
267             }
268         },
269         hir::ExprVec(ref es) => es.len(),
270         hir::ExprRepeat(_, ref count_expr) => {
271             eval_length(bcx.tcx(), &count_expr, "repeat count").unwrap()
272         }
273         _ => span_bug!(content_expr.span, "unexpected vec content")
274     }
275 }
276
277 /// Converts a fixed-length vector into the slice pair. The vector should be stored in `llval`
278 /// which should be by ref.
279 pub fn get_fixed_base_and_len(bcx: Block,
280                               llval: ValueRef,
281                               vec_length: usize)
282                               -> (ValueRef, ValueRef) {
283     let ccx = bcx.ccx();
284
285     let base = expr::get_dataptr(bcx, llval);
286     let len = C_uint(ccx, vec_length);
287     (base, len)
288 }
289
290 /// Converts a vector into the slice pair.  The vector should be stored in `llval` which should be
291 /// by-reference.  If you have a datum, you would probably prefer to call
292 /// `Datum::get_base_and_len()` which will handle any conversions for you.
293 pub fn get_base_and_len<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
294                                     llval: ValueRef,
295                                     vec_ty: Ty<'tcx>)
296                                     -> (ValueRef, ValueRef) {
297     match vec_ty.sty {
298         ty::TyArray(_, n) => get_fixed_base_and_len(bcx, llval, n),
299         ty::TySlice(_) | ty::TyStr => {
300             let base = Load(bcx, expr::get_dataptr(bcx, llval));
301             let len = Load(bcx, expr::get_meta(bcx, llval));
302             (base, len)
303         }
304
305         // Only used for pattern matching.
306         ty::TyBox(ty) | ty::TyRef(_, ty::TypeAndMut{ty, ..}) => {
307             let inner = if type_is_sized(bcx.tcx(), ty) {
308                 Load(bcx, llval)
309             } else {
310                 llval
311             };
312             get_base_and_len(bcx, inner, ty)
313         },
314         _ => bug!("unexpected type in get_base_and_len"),
315     }
316 }
317
318 fn iter_vec_loop<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
319                                 data_ptr: ValueRef,
320                                 vt: &VecTypes<'tcx>,
321                                 count: ValueRef,
322                                 f: F)
323                                 -> Block<'blk, 'tcx> where
324     F: FnOnce(Block<'blk, 'tcx>, ValueRef, Ty<'tcx>) -> Block<'blk, 'tcx>,
325 {
326     let _icx = push_ctxt("tvec::iter_vec_loop");
327
328     if bcx.unreachable.get() {
329         return bcx;
330     }
331
332     let fcx = bcx.fcx;
333     let loop_bcx = fcx.new_temp_block("expr_repeat");
334     let next_bcx = fcx.new_temp_block("expr_repeat: next");
335
336     Br(bcx, loop_bcx.llbb, DebugLoc::None);
337
338     let loop_counter = Phi(loop_bcx, bcx.ccx().int_type(),
339                            &[C_uint(bcx.ccx(), 0 as usize)], &[bcx.llbb]);
340
341     let bcx = loop_bcx;
342
343     let lleltptr = if llsize_of_alloc(bcx.ccx(), vt.llunit_ty) == 0 {
344         data_ptr
345     } else {
346         InBoundsGEP(bcx, data_ptr, &[loop_counter])
347     };
348     let bcx = f(bcx, lleltptr, vt.unit_ty);
349     let plusone = Add(bcx, loop_counter, C_uint(bcx.ccx(), 1usize), DebugLoc::None);
350     AddIncomingToPhi(loop_counter, plusone, bcx.llbb);
351
352     let cond_val = ICmp(bcx, llvm::IntULT, plusone, count, DebugLoc::None);
353     CondBr(bcx, cond_val, loop_bcx.llbb, next_bcx.llbb, DebugLoc::None);
354
355     next_bcx
356 }
357
358 pub fn iter_vec_raw<'blk, 'tcx, F>(bcx: Block<'blk, 'tcx>,
359                                    data_ptr: ValueRef,
360                                    unit_ty: Ty<'tcx>,
361                                    len: ValueRef,
362                                    f: F)
363                                    -> Block<'blk, 'tcx> where
364     F: FnOnce(Block<'blk, 'tcx>, ValueRef, Ty<'tcx>) -> Block<'blk, 'tcx>,
365 {
366     let _icx = push_ctxt("tvec::iter_vec_raw");
367     let fcx = bcx.fcx;
368
369     let vt = vec_types(bcx, unit_ty);
370
371     if llsize_of_alloc(bcx.ccx(), vt.llunit_ty) == 0 {
372         // Special-case vectors with elements of size 0  so they don't go out of bounds (#9890)
373         iter_vec_loop(bcx, data_ptr, &vt, len, f)
374     } else {
375         // Calculate the last pointer address we want to handle.
376         let data_end_ptr = InBoundsGEP(bcx, data_ptr, &[len]);
377
378         // Now perform the iteration.
379         let header_bcx = fcx.new_temp_block("iter_vec_loop_header");
380         Br(bcx, header_bcx.llbb, DebugLoc::None);
381         let data_ptr =
382             Phi(header_bcx, val_ty(data_ptr), &[data_ptr], &[bcx.llbb]);
383         let not_yet_at_end =
384             ICmp(header_bcx, llvm::IntULT, data_ptr, data_end_ptr, DebugLoc::None);
385         let body_bcx = fcx.new_temp_block("iter_vec_loop_body");
386         let next_bcx = fcx.new_temp_block("iter_vec_next");
387         CondBr(header_bcx, not_yet_at_end, body_bcx.llbb, next_bcx.llbb, DebugLoc::None);
388         let body_bcx = f(body_bcx, data_ptr, unit_ty);
389         AddIncomingToPhi(data_ptr, InBoundsGEP(body_bcx, data_ptr,
390                                                &[C_int(bcx.ccx(), 1)]),
391                          body_bcx.llbb);
392         Br(body_bcx, header_bcx.llbb, DebugLoc::None);
393         next_bcx
394     }
395 }