]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/trans/mir/rvalue.rs
Auto merge of #31391 - frewsxcv:test, r=alexcrichton
[rust.git] / src / librustc_trans / trans / mir / rvalue.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 use llvm::ValueRef;
12 use rustc::middle::ty::{self, Ty};
13 use middle::ty::cast::{CastTy, IntTy};
14 use rustc::mir::repr as mir;
15
16 use trans::asm;
17 use trans::base;
18 use trans::common::{self, BlockAndBuilder, Result};
19 use trans::debuginfo::DebugLoc;
20 use trans::declare;
21 use trans::expr;
22 use trans::adt;
23 use trans::machine;
24 use trans::type_::Type;
25 use trans::type_of;
26 use trans::tvec;
27 use trans::Disr;
28
29 use super::MirContext;
30 use super::operand::{OperandRef, OperandValue};
31 use super::lvalue::LvalueRef;
32
33 impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
34     pub fn trans_rvalue(&mut self,
35                         bcx: BlockAndBuilder<'bcx, 'tcx>,
36                         dest: LvalueRef<'tcx>,
37                         rvalue: &mir::Rvalue<'tcx>)
38                         -> BlockAndBuilder<'bcx, 'tcx>
39     {
40         debug!("trans_rvalue(dest.llval={}, rvalue={:?})",
41                bcx.val_to_string(dest.llval),
42                rvalue);
43
44         match *rvalue {
45             mir::Rvalue::Use(ref operand) => {
46                 self.trans_operand_into(&bcx, dest.llval, operand);
47                 bcx
48             }
49
50             mir::Rvalue::Cast(mir::CastKind::Unsize, ref operand, cast_ty) => {
51                 if common::type_is_fat_ptr(bcx.tcx(), cast_ty) {
52                     // into-coerce of a thin pointer to a fat pointer - just
53                     // use the operand path.
54                     let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
55                     self.store_operand(&bcx, dest.llval, temp);
56                     return bcx;
57                 }
58
59                 // Unsize of a nontrivial struct. I would prefer for
60                 // this to be eliminated by MIR translation, but
61                 // `CoerceUnsized` can be passed by a where-clause,
62                 // so the (generic) MIR may not be able to expand it.
63                 let operand = self.trans_operand(&bcx, operand);
64                 bcx.with_block(|bcx| {
65                     match operand.val {
66                         OperandValue::FatPtr(..) => unreachable!(),
67                         OperandValue::Immediate(llval) => {
68                             // unsize from an immediate structure. We don't
69                             // really need a temporary alloca here, but
70                             // avoiding it would require us to have
71                             // `coerce_unsized_into` use extractvalue to
72                             // index into the struct, and this case isn't
73                             // important enough for it.
74                             debug!("trans_rvalue: creating ugly alloca");
75                             let lltemp = base::alloc_ty(bcx, operand.ty, "__unsize_temp");
76                             base::store_ty(bcx, llval, lltemp, operand.ty);
77                             base::coerce_unsized_into(bcx,
78                                                       lltemp, operand.ty,
79                                                       dest.llval, cast_ty);
80                         }
81                         OperandValue::Ref(llref) => {
82                             base::coerce_unsized_into(bcx,
83                                                       llref, operand.ty,
84                                                       dest.llval, cast_ty);
85                         }
86                     }
87                 });
88                 bcx
89             }
90
91             mir::Rvalue::Repeat(ref elem, ref count) => {
92                 let elem = self.trans_operand(&bcx, elem);
93                 let size = self.trans_constval(&bcx, &count.value, count.ty).immediate();
94                 bcx.map_block(|block| {
95                     let base = expr::get_dataptr(block, dest.llval);
96                     tvec::iter_vec_raw(block, base, elem.ty, size, |block, llslot, _| {
97                         self.store_operand_direct(block, llslot, elem);
98                         block
99                     })
100                 })
101             }
102
103             mir::Rvalue::Aggregate(ref kind, ref operands) => {
104                 match *kind {
105                     mir::AggregateKind::Adt(adt_def, index, _) => {
106                         let repr = adt::represent_type(bcx.ccx(), dest.ty.to_ty(bcx.tcx()));
107                         let disr = Disr::from(adt_def.variants[index].disr_val);
108                         bcx.with_block(|bcx| {
109                             adt::trans_set_discr(bcx, &repr, dest.llval, Disr::from(disr));
110                         });
111                         for (i, operand) in operands.iter().enumerate() {
112                             let op = self.trans_operand(&bcx, operand);
113                             // Do not generate stores and GEPis for zero-sized fields.
114                             if !common::type_is_zero_size(bcx.ccx(), op.ty) {
115                                 let val = adt::MaybeSizedValue::sized(dest.llval);
116                                 let lldest_i = bcx.with_block(|bcx| {
117                                     adt::trans_field_ptr(bcx, &repr, val, disr, i)
118                                 });
119                                 self.store_operand(&bcx, lldest_i, op);
120                             }
121                         }
122                     },
123                     _ => {
124                         for (i, operand) in operands.iter().enumerate() {
125                             let op = self.trans_operand(&bcx, operand);
126                             // Do not generate stores and GEPis for zero-sized fields.
127                             if !common::type_is_zero_size(bcx.ccx(), op.ty) {
128                                 // Note: perhaps this should be StructGep, but
129                                 // note that in some cases the values here will
130                                 // not be structs but arrays.
131                                 let dest = bcx.gepi(dest.llval, &[0, i]);
132                                 self.store_operand(&bcx, dest, op);
133                             }
134                         }
135                     }
136                 }
137                 bcx
138             }
139
140             mir::Rvalue::Slice { ref input, from_start, from_end } => {
141                 let ccx = bcx.ccx();
142                 let input = self.trans_lvalue(&bcx, input);
143                 let (llbase, lllen) = bcx.with_block(|bcx| {
144                     tvec::get_base_and_len(bcx,
145                                            input.llval,
146                                            input.ty.to_ty(bcx.tcx()))
147                 });
148                 let llbase1 = bcx.gepi(llbase, &[from_start]);
149                 let adj = common::C_uint(ccx, from_start + from_end);
150                 let lllen1 = bcx.sub(lllen, adj);
151                 let (lladdrdest, llmetadest) = bcx.with_block(|bcx| {
152                     (expr::get_dataptr(bcx, dest.llval), expr::get_meta(bcx, dest.llval))
153                 });
154                 bcx.store(llbase1, lladdrdest);
155                 bcx.store(lllen1, llmetadest);
156                 bcx
157             }
158
159             mir::Rvalue::InlineAsm(ref inline_asm) => {
160                 bcx.map_block(|bcx| {
161                     asm::trans_inline_asm(bcx, inline_asm)
162                 })
163             }
164
165             _ => {
166                 assert!(rvalue_creates_operand(rvalue));
167                 let (bcx, temp) = self.trans_rvalue_operand(bcx, rvalue);
168                 self.store_operand(&bcx, dest.llval, temp);
169                 bcx
170             }
171         }
172     }
173
174     pub fn trans_rvalue_operand(&mut self,
175                                 bcx: BlockAndBuilder<'bcx, 'tcx>,
176                                 rvalue: &mir::Rvalue<'tcx>)
177                                 -> (BlockAndBuilder<'bcx, 'tcx>, OperandRef<'tcx>)
178     {
179         assert!(rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue);
180
181         match *rvalue {
182             mir::Rvalue::Use(ref operand) => {
183                 let operand = self.trans_operand(&bcx, operand);
184                 (bcx, operand)
185             }
186
187             mir::Rvalue::Cast(ref kind, ref operand, cast_ty) => {
188                 let operand = self.trans_operand(&bcx, operand);
189                 debug!("cast operand is {}", operand.repr(&bcx));
190                 let cast_ty = bcx.monomorphize(&cast_ty);
191
192                 let val = match *kind {
193                     mir::CastKind::ReifyFnPointer |
194                     mir::CastKind::UnsafeFnPointer => {
195                         // these are no-ops at the LLVM level
196                         operand.val
197                     }
198                     mir::CastKind::Unsize => {
199                         // unsize targets other than to a fat pointer currently
200                         // can't be operands.
201                         assert!(common::type_is_fat_ptr(bcx.tcx(), cast_ty));
202
203                         match operand.val {
204                             OperandValue::FatPtr(..) => {
205                                 // unsize from a fat pointer - this is a
206                                 // "trait-object-to-supertrait" coercion, for
207                                 // example,
208                                 //   &'a fmt::Debug+Send => &'a fmt::Debug,
209                                 // and is a no-op at the LLVM level
210                                 operand.val
211                             }
212                             OperandValue::Immediate(lldata) => {
213                                 // "standard" unsize
214                                 let (lldata, llextra) = bcx.with_block(|bcx| {
215                                     base::unsize_thin_ptr(bcx, lldata,
216                                                           operand.ty, cast_ty)
217                                 });
218                                 OperandValue::FatPtr(lldata, llextra)
219                             }
220                             OperandValue::Ref(_) => {
221                                 bcx.sess().bug(
222                                     &format!("by-ref operand {} in trans_rvalue_operand",
223                                              operand.repr(&bcx)));
224                             }
225                         }
226                     }
227                     mir::CastKind::Misc if common::type_is_immediate(bcx.ccx(), operand.ty) => {
228                         debug_assert!(common::type_is_immediate(bcx.ccx(), cast_ty));
229                         let r_t_in = CastTy::from_ty(operand.ty).expect("bad input type for cast");
230                         let r_t_out = CastTy::from_ty(cast_ty).expect("bad output type for cast");
231                         let ll_t_in = type_of::arg_type_of(bcx.ccx(), operand.ty);
232                         let ll_t_out = type_of::arg_type_of(bcx.ccx(), cast_ty);
233                         let (llval, ll_t_in, signed) = if let CastTy::Int(IntTy::CEnum) = r_t_in {
234                             let repr = adt::represent_type(bcx.ccx(), operand.ty);
235                             let llval = operand.immediate();
236                             let discr = bcx.with_block(|bcx| {
237                                 adt::trans_get_discr(bcx, &repr, llval, None, true)
238                             });
239                             (discr, common::val_ty(discr), adt::is_discr_signed(&repr))
240                         } else {
241                             (operand.immediate(), ll_t_in, operand.ty.is_signed())
242                         };
243
244                         let newval = match (r_t_in, r_t_out) {
245                             (CastTy::Int(_), CastTy::Int(_)) => {
246                                 let srcsz = ll_t_in.int_width();
247                                 let dstsz = ll_t_out.int_width();
248                                 if srcsz == dstsz {
249                                     bcx.bitcast(llval, ll_t_out)
250                                 } else if srcsz > dstsz {
251                                     bcx.trunc(llval, ll_t_out)
252                                 } else if signed {
253                                     bcx.sext(llval, ll_t_out)
254                                 } else {
255                                     bcx.zext(llval, ll_t_out)
256                                 }
257                             }
258                             (CastTy::Float, CastTy::Float) => {
259                                 let srcsz = ll_t_in.float_width();
260                                 let dstsz = ll_t_out.float_width();
261                                 if dstsz > srcsz {
262                                     bcx.fpext(llval, ll_t_out)
263                                 } else if srcsz > dstsz {
264                                     bcx.fptrunc(llval, ll_t_out)
265                                 } else {
266                                     llval
267                                 }
268                             }
269                             (CastTy::Ptr(_), CastTy::Ptr(_)) |
270                             (CastTy::FnPtr, CastTy::Ptr(_)) |
271                             (CastTy::RPtr(_), CastTy::Ptr(_)) =>
272                                 bcx.pointercast(llval, ll_t_out),
273                             (CastTy::Ptr(_), CastTy::Int(_)) |
274                             (CastTy::FnPtr, CastTy::Int(_)) =>
275                                 bcx.ptrtoint(llval, ll_t_out),
276                             (CastTy::Int(_), CastTy::Ptr(_)) =>
277                                 bcx.inttoptr(llval, ll_t_out),
278                             (CastTy::Int(_), CastTy::Float) if signed =>
279                                 bcx.sitofp(llval, ll_t_out),
280                             (CastTy::Int(_), CastTy::Float) =>
281                                 bcx.uitofp(llval, ll_t_out),
282                             (CastTy::Float, CastTy::Int(IntTy::I)) =>
283                                 bcx.fptosi(llval, ll_t_out),
284                             (CastTy::Float, CastTy::Int(_)) =>
285                                 bcx.fptoui(llval, ll_t_out),
286                             _ => bcx.ccx().sess().bug(
287                                 &format!("unsupported cast: {:?} to {:?}", operand.ty, cast_ty)
288                             )
289                         };
290                         OperandValue::Immediate(newval)
291                     }
292                     mir::CastKind::Misc => { // Casts from a fat-ptr.
293                         let ll_cast_ty = type_of::arg_type_of(bcx.ccx(), cast_ty);
294                         let ll_from_ty = type_of::arg_type_of(bcx.ccx(), operand.ty);
295                         if let OperandValue::FatPtr(data_ptr, meta_ptr) = operand.val {
296                             if common::type_is_fat_ptr(bcx.tcx(), cast_ty) {
297                                 let ll_cft = ll_cast_ty.field_types();
298                                 let ll_fft = ll_from_ty.field_types();
299                                 let data_cast = bcx.pointercast(data_ptr, ll_cft[0]);
300                                 assert_eq!(ll_cft[1].kind(), ll_fft[1].kind());
301                                 OperandValue::FatPtr(data_cast, meta_ptr)
302                             } else { // cast to thin-ptr
303                                 // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and
304                                 // pointer-cast of that pointer to desired pointer type.
305                                 let llval = bcx.pointercast(data_ptr, ll_cast_ty);
306                                 OperandValue::Immediate(llval)
307                             }
308                         } else {
309                             panic!("Unexpected non-FatPtr operand")
310                         }
311                     }
312                 };
313                 let operand = OperandRef {
314                     val: val,
315                     ty: cast_ty
316                 };
317                 (bcx, operand)
318             }
319
320             mir::Rvalue::Ref(_, bk, ref lvalue) => {
321                 let tr_lvalue = self.trans_lvalue(&bcx, lvalue);
322
323                 let ty = tr_lvalue.ty.to_ty(bcx.tcx());
324                 let ref_ty = bcx.tcx().mk_ref(
325                     bcx.tcx().mk_region(ty::ReStatic),
326                     ty::TypeAndMut { ty: ty, mutbl: bk.to_mutbl_lossy() }
327                 );
328
329                 // Note: lvalues are indirect, so storing the `llval` into the
330                 // destination effectively creates a reference.
331                 let operand = if common::type_is_sized(bcx.tcx(), ty) {
332                     OperandRef {
333                         val: OperandValue::Immediate(tr_lvalue.llval),
334                         ty: ref_ty,
335                     }
336                 } else {
337                     OperandRef {
338                         val: OperandValue::FatPtr(tr_lvalue.llval,
339                                                   tr_lvalue.llextra),
340                         ty: ref_ty,
341                     }
342                 };
343                 (bcx, operand)
344             }
345
346             mir::Rvalue::Len(ref lvalue) => {
347                 let tr_lvalue = self.trans_lvalue(&bcx, lvalue);
348                 let operand = OperandRef {
349                     val: OperandValue::Immediate(self.lvalue_len(&bcx, tr_lvalue)),
350                     ty: bcx.tcx().types.usize,
351                 };
352                 (bcx, operand)
353             }
354
355             mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
356                 let lhs = self.trans_operand(&bcx, lhs);
357                 let rhs = self.trans_operand(&bcx, rhs);
358                 let llresult = if common::type_is_fat_ptr(bcx.tcx(), lhs.ty) {
359                     match (lhs.val, rhs.val) {
360                         (OperandValue::FatPtr(lhs_addr, lhs_extra),
361                          OperandValue::FatPtr(rhs_addr, rhs_extra)) => {
362                             bcx.with_block(|bcx| {
363                                 base::compare_fat_ptrs(bcx,
364                                                        lhs_addr, lhs_extra,
365                                                        rhs_addr, rhs_extra,
366                                                        lhs.ty, op.to_hir_binop(),
367                                                        DebugLoc::None)
368                             })
369                         }
370                         _ => unreachable!()
371                     }
372
373                 } else {
374                     self.trans_scalar_binop(&bcx, op,
375                                             lhs.immediate(), rhs.immediate(),
376                                             lhs.ty)
377                 };
378                 let operand = OperandRef {
379                     val: OperandValue::Immediate(llresult),
380                     ty: self.mir.binop_ty(bcx.tcx(), op, lhs.ty, rhs.ty),
381                 };
382                 (bcx, operand)
383             }
384
385             mir::Rvalue::UnaryOp(op, ref operand) => {
386                 let operand = self.trans_operand(&bcx, operand);
387                 let lloperand = operand.immediate();
388                 let is_float = operand.ty.is_fp();
389                 let llval = match op {
390                     mir::UnOp::Not => bcx.not(lloperand),
391                     mir::UnOp::Neg => if is_float {
392                         bcx.fneg(lloperand)
393                     } else {
394                         bcx.neg(lloperand)
395                     }
396                 };
397                 (bcx, OperandRef {
398                     val: OperandValue::Immediate(llval),
399                     ty: operand.ty,
400                 })
401             }
402
403             mir::Rvalue::Box(content_ty) => {
404                 let content_ty: Ty<'tcx> = bcx.monomorphize(&content_ty);
405                 let llty = type_of::type_of(bcx.ccx(), content_ty);
406                 let llsize = machine::llsize_of(bcx.ccx(), llty);
407                 let align = type_of::align_of(bcx.ccx(), content_ty);
408                 let llalign = common::C_uint(bcx.ccx(), align);
409                 let llty_ptr = llty.ptr_to();
410                 let box_ty = bcx.tcx().mk_box(content_ty);
411                 let mut llval = None;
412                 let bcx = bcx.map_block(|bcx| {
413                     let Result { bcx, val } = base::malloc_raw_dyn(bcx,
414                                                                    llty_ptr,
415                                                                    box_ty,
416                                                                    llsize,
417                                                                    llalign,
418                                                                    DebugLoc::None);
419                     llval = Some(val);
420                     bcx
421                 });
422                 let operand = OperandRef {
423                     val: OperandValue::Immediate(llval.unwrap()),
424                     ty: box_ty,
425                 };
426                 (bcx, operand)
427             }
428
429             mir::Rvalue::Repeat(..) |
430             mir::Rvalue::Aggregate(..) |
431             mir::Rvalue::Slice { .. } |
432             mir::Rvalue::InlineAsm(..) => {
433                 bcx.tcx().sess.bug(&format!("cannot generate operand from rvalue {:?}", rvalue));
434             }
435         }
436     }
437
438     pub fn trans_scalar_binop(&mut self,
439                               bcx: &BlockAndBuilder<'bcx, 'tcx>,
440                               op: mir::BinOp,
441                               lhs: ValueRef,
442                               rhs: ValueRef,
443                               input_ty: Ty<'tcx>) -> ValueRef {
444         let is_float = input_ty.is_fp();
445         let is_signed = input_ty.is_signed();
446         match op {
447             mir::BinOp::Add => if is_float {
448                 bcx.fadd(lhs, rhs)
449             } else {
450                 bcx.add(lhs, rhs)
451             },
452             mir::BinOp::Sub => if is_float {
453                 bcx.fsub(lhs, rhs)
454             } else {
455                 bcx.sub(lhs, rhs)
456             },
457             mir::BinOp::Mul => if is_float {
458                 bcx.fmul(lhs, rhs)
459             } else {
460                 bcx.mul(lhs, rhs)
461             },
462             mir::BinOp::Div => if is_float {
463                 bcx.fdiv(lhs, rhs)
464             } else if is_signed {
465                 bcx.sdiv(lhs, rhs)
466             } else {
467                 bcx.udiv(lhs, rhs)
468             },
469             mir::BinOp::Rem => if is_float {
470                 // LLVM currently always lowers the `frem` instructions appropriate
471                 // library calls typically found in libm. Notably f64 gets wired up
472                 // to `fmod` and f32 gets wired up to `fmodf`. Inconveniently for
473                 // us, 32-bit MSVC does not actually have a `fmodf` symbol, it's
474                 // instead just an inline function in a header that goes up to a
475                 // f64, uses `fmod`, and then comes back down to a f32.
476                 //
477                 // Although LLVM knows that `fmodf` doesn't exist on MSVC, it will
478                 // still unconditionally lower frem instructions over 32-bit floats
479                 // to a call to `fmodf`. To work around this we special case MSVC
480                 // 32-bit float rem instructions and instead do the call out to
481                 // `fmod` ourselves.
482                 //
483                 // Note that this is currently duplicated with src/libcore/ops.rs
484                 // which does the same thing, and it would be nice to perhaps unify
485                 // these two implementations one day! Also note that we call `fmod`
486                 // for both 32 and 64-bit floats because if we emit any FRem
487                 // instruction at all then LLVM is capable of optimizing it into a
488                 // 32-bit FRem (which we're trying to avoid).
489                 let tcx = bcx.tcx();
490                 let use_fmod = tcx.sess.target.target.options.is_like_msvc &&
491                     tcx.sess.target.target.arch == "x86";
492                 if use_fmod {
493                     let f64t = Type::f64(bcx.ccx());
494                     let fty = Type::func(&[f64t, f64t], &f64t);
495                     let llfn = declare::declare_cfn(bcx.ccx(), "fmod", fty,
496                                                     tcx.types.f64);
497                     if input_ty == tcx.types.f32 {
498                         let lllhs = bcx.fpext(lhs, f64t);
499                         let llrhs = bcx.fpext(rhs, f64t);
500                         let llres = bcx.call(llfn, &[lllhs, llrhs], None, None);
501                         bcx.fptrunc(llres, Type::f32(bcx.ccx()))
502                     } else {
503                         bcx.call(llfn, &[lhs, rhs], None, None)
504                     }
505                 } else {
506                     bcx.frem(lhs, rhs)
507                 }
508             } else if is_signed {
509                 bcx.srem(lhs, rhs)
510             } else {
511                 bcx.urem(lhs, rhs)
512             },
513             mir::BinOp::BitOr => bcx.or(lhs, rhs),
514             mir::BinOp::BitAnd => bcx.and(lhs, rhs),
515             mir::BinOp::BitXor => bcx.xor(lhs, rhs),
516             mir::BinOp::Shl => {
517                 bcx.with_block(|bcx| {
518                     common::build_unchecked_lshift(bcx,
519                                                    lhs,
520                                                    rhs,
521                                                    DebugLoc::None)
522                 })
523             }
524             mir::BinOp::Shr => {
525                 bcx.with_block(|bcx| {
526                     common::build_unchecked_rshift(bcx,
527                                                    input_ty,
528                                                    lhs,
529                                                    rhs,
530                                                    DebugLoc::None)
531                 })
532             }
533             mir::BinOp::Eq | mir::BinOp::Lt | mir::BinOp::Gt |
534             mir::BinOp::Ne | mir::BinOp::Le | mir::BinOp::Ge => {
535                 bcx.with_block(|bcx| {
536                     base::compare_scalar_types(bcx, lhs, rhs, input_ty,
537                                                op.to_hir_binop(), DebugLoc::None)
538                 })
539             }
540         }
541     }
542 }
543
544 pub fn rvalue_creates_operand<'tcx>(rvalue: &mir::Rvalue<'tcx>) -> bool {
545     match *rvalue {
546         mir::Rvalue::Use(..) | // (*)
547         mir::Rvalue::Ref(..) |
548         mir::Rvalue::Len(..) |
549         mir::Rvalue::Cast(..) | // (*)
550         mir::Rvalue::BinaryOp(..) |
551         mir::Rvalue::UnaryOp(..) |
552         mir::Rvalue::Box(..) =>
553             true,
554         mir::Rvalue::Repeat(..) |
555         mir::Rvalue::Aggregate(..) |
556         mir::Rvalue::Slice { .. } |
557         mir::Rvalue::InlineAsm(..) =>
558             false,
559     }
560
561     // (*) this is only true if the type is suitable
562 }