]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/mir/constant.rs
Hide the RefCell inside InterpretInterner
[rust.git] / src / librustc_trans / mir / constant.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::{self, ValueRef};
12 use rustc_const_math::{ConstInt, ConstMathErr};
13 use rustc::middle::const_val::{ConstVal, ConstEvalErr};
14 use rustc_mir::interpret::{read_target_uint, const_val_field};
15 use rustc::hir::def_id::DefId;
16 use rustc::traits;
17 use rustc::mir;
18 use rustc_data_structures::indexed_vec::Idx;
19 use rustc::mir::interpret::{Allocation, GlobalId, MemoryPointer, PrimVal, Value as MiriValue};
20 use rustc::ty::{self, Ty, TyCtxt};
21 use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar};
22 use base;
23 use builder::Builder;
24 use common::{CodegenCx};
25 use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize};
26 use common::const_to_opt_u128;
27 use consts;
28 use type_of::LayoutLlvmExt;
29 use type_::Type;
30
31 use super::super::callee;
32 use super::FunctionCx;
33
34 fn to_const_int(value: ValueRef, t: Ty, tcx: TyCtxt) -> Option<ConstInt> {
35     match t.sty {
36         ty::TyInt(int_type) => const_to_opt_u128(value, true)
37             .and_then(|input| ConstInt::new_signed(input as i128, int_type,
38                                                    tcx.sess.target.isize_ty)),
39         ty::TyUint(uint_type) => const_to_opt_u128(value, false)
40             .and_then(|input| ConstInt::new_unsigned(input, uint_type,
41                                                      tcx.sess.target.usize_ty)),
42         _ => None
43
44     }
45 }
46
47 pub fn const_scalar_binop(op: mir::BinOp,
48                           lhs: ValueRef,
49                           rhs: ValueRef,
50                           input_ty: Ty) -> ValueRef {
51     assert!(!input_ty.is_simd());
52     let is_float = input_ty.is_fp();
53     let signed = input_ty.is_signed();
54
55     unsafe {
56         match op {
57             mir::BinOp::Add if is_float => llvm::LLVMConstFAdd(lhs, rhs),
58             mir::BinOp::Add             => llvm::LLVMConstAdd(lhs, rhs),
59
60             mir::BinOp::Sub if is_float => llvm::LLVMConstFSub(lhs, rhs),
61             mir::BinOp::Sub             => llvm::LLVMConstSub(lhs, rhs),
62
63             mir::BinOp::Mul if is_float => llvm::LLVMConstFMul(lhs, rhs),
64             mir::BinOp::Mul             => llvm::LLVMConstMul(lhs, rhs),
65
66             mir::BinOp::Div if is_float => llvm::LLVMConstFDiv(lhs, rhs),
67             mir::BinOp::Div if signed   => llvm::LLVMConstSDiv(lhs, rhs),
68             mir::BinOp::Div             => llvm::LLVMConstUDiv(lhs, rhs),
69
70             mir::BinOp::Rem if is_float => llvm::LLVMConstFRem(lhs, rhs),
71             mir::BinOp::Rem if signed   => llvm::LLVMConstSRem(lhs, rhs),
72             mir::BinOp::Rem             => llvm::LLVMConstURem(lhs, rhs),
73
74             mir::BinOp::BitXor => llvm::LLVMConstXor(lhs, rhs),
75             mir::BinOp::BitAnd => llvm::LLVMConstAnd(lhs, rhs),
76             mir::BinOp::BitOr  => llvm::LLVMConstOr(lhs, rhs),
77             mir::BinOp::Shl    => {
78                 let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
79                 llvm::LLVMConstShl(lhs, rhs)
80             }
81             mir::BinOp::Shr    => {
82                 let rhs = base::cast_shift_const_rhs(op.to_hir_binop(), lhs, rhs);
83                 if signed { llvm::LLVMConstAShr(lhs, rhs) }
84                 else      { llvm::LLVMConstLShr(lhs, rhs) }
85             }
86             mir::BinOp::Eq | mir::BinOp::Ne |
87             mir::BinOp::Lt | mir::BinOp::Le |
88             mir::BinOp::Gt | mir::BinOp::Ge => {
89                 if is_float {
90                     let cmp = base::bin_op_to_fcmp_predicate(op.to_hir_binop());
91                     llvm::LLVMConstFCmp(cmp, lhs, rhs)
92                 } else {
93                     let cmp = base::bin_op_to_icmp_predicate(op.to_hir_binop(),
94                                                                 signed);
95                     llvm::LLVMConstICmp(cmp, lhs, rhs)
96                 }
97             }
98             mir::BinOp::Offset => unreachable!("BinOp::Offset in const-eval!")
99         }
100     }
101 }
102
103 pub fn const_scalar_checked_binop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
104                                             op: mir::BinOp,
105                                             lllhs: ValueRef,
106                                             llrhs: ValueRef,
107                                             input_ty: Ty<'tcx>)
108                                             -> Option<(ValueRef, bool)> {
109     if let (Some(lhs), Some(rhs)) = (to_const_int(lllhs, input_ty, tcx),
110                                      to_const_int(llrhs, input_ty, tcx)) {
111         let result = match op {
112             mir::BinOp::Add => lhs + rhs,
113             mir::BinOp::Sub => lhs - rhs,
114             mir::BinOp::Mul => lhs * rhs,
115             mir::BinOp::Shl => lhs << rhs,
116             mir::BinOp::Shr => lhs >> rhs,
117             _ => {
118                 bug!("Operator `{:?}` is not a checkable operator", op)
119             }
120         };
121
122         let of = match result {
123             Ok(_) => false,
124             Err(ConstMathErr::Overflow(_)) |
125             Err(ConstMathErr::ShiftNegative) => true,
126             Err(err) => {
127                 bug!("Operator `{:?}` on `{:?}` and `{:?}` errored: {}",
128                      op, lhs, rhs, err.description());
129             }
130         };
131
132         Some((const_scalar_binop(op, lllhs, llrhs, input_ty), of))
133     } else {
134         None
135     }
136 }
137
138 pub fn primval_to_llvm(cx: &CodegenCx,
139                        cv: PrimVal,
140                        scalar: &Scalar,
141                        llty: Type) -> ValueRef {
142     let bits = if scalar.is_bool() { 1 } else { scalar.value.size(cx).bits() };
143     match cv {
144         PrimVal::Undef => C_undef(Type::ix(cx, bits)),
145         PrimVal::Bytes(b) => {
146             let llval = C_uint_big(Type::ix(cx, bits), b);
147             if scalar.value == layout::Pointer {
148                 unsafe { llvm::LLVMConstIntToPtr(llval, llty.to_ref()) }
149             } else {
150                 consts::bitcast(llval, llty)
151             }
152         },
153         PrimVal::Ptr(ptr) => {
154             if let Some(fn_instance) = cx.tcx.interpret_interner.get_fn(ptr.alloc_id) {
155                 callee::get_fn(cx, fn_instance)
156             } else {
157                 let static_ = cx
158                     .tcx
159                     .interpret_interner
160                     .get_corresponding_static_def_id(ptr.alloc_id);
161                 let base_addr = if let Some(def_id) = static_ {
162                     assert!(cx.tcx.is_static(def_id).is_some());
163                     consts::get_static(cx, def_id)
164                 } else if let Some(alloc) = cx.tcx.interpret_interner
165                                               .get_alloc(ptr.alloc_id) {
166                     let init = global_initializer(cx, alloc);
167                     if alloc.mutable {
168                         consts::addr_of_mut(cx, init, alloc.align, "byte_str")
169                     } else {
170                         consts::addr_of(cx, init, alloc.align, "byte_str")
171                     }
172                 } else {
173                     bug!("missing allocation {:?}", ptr.alloc_id);
174                 };
175
176                 let llval = unsafe { llvm::LLVMConstInBoundsGEP(
177                     consts::bitcast(base_addr, Type::i8p(cx)),
178                     &C_usize(cx, ptr.offset),
179                     1,
180                 ) };
181                 if scalar.value != layout::Pointer {
182                     unsafe { llvm::LLVMConstPtrToInt(llval, llty.to_ref()) }
183                 } else {
184                     consts::bitcast(llval, llty)
185                 }
186             }
187         }
188     }
189 }
190
191 pub fn global_initializer(cx: &CodegenCx, alloc: &Allocation) -> ValueRef {
192     let mut llvals = Vec::with_capacity(alloc.relocations.len() + 1);
193     let layout = cx.data_layout();
194     let pointer_size = layout.pointer_size.bytes() as usize;
195
196     let mut next_offset = 0;
197     for (&offset, &alloc_id) in &alloc.relocations {
198         assert_eq!(offset as usize as u64, offset);
199         let offset = offset as usize;
200         if offset > next_offset {
201             llvals.push(C_bytes(cx, &alloc.bytes[next_offset..offset]));
202         }
203         let ptr_offset = read_target_uint(
204             layout.endian,
205             &alloc.bytes[offset..(offset + pointer_size)],
206         ).expect("global_initializer: could not read relocation pointer") as u64;
207         llvals.push(primval_to_llvm(
208             cx,
209             PrimVal::Ptr(MemoryPointer { alloc_id, offset: ptr_offset }),
210             &Scalar {
211                 value: layout::Primitive::Pointer,
212                 valid_range: 0..=!0
213             },
214             Type::i8p(cx)
215         ));
216         next_offset = offset + pointer_size;
217     }
218     if alloc.bytes.len() >= next_offset {
219         llvals.push(C_bytes(cx, &alloc.bytes[next_offset ..]));
220     }
221
222     C_struct(cx, &llvals, true)
223 }
224
225 pub fn trans_static_initializer<'a, 'tcx>(
226     cx: &CodegenCx<'a, 'tcx>,
227     def_id: DefId)
228     -> Result<ValueRef, ConstEvalErr<'tcx>>
229 {
230     let instance = ty::Instance::mono(cx.tcx, def_id);
231     let cid = GlobalId {
232         instance,
233         promoted: None
234     };
235     let param_env = ty::ParamEnv::empty(traits::Reveal::All);
236     cx.tcx.const_eval(param_env.and(cid))?;
237
238     let alloc_id = cx
239         .tcx
240         .interpret_interner
241         .get_cached(def_id)
242         .expect("global not cached");
243
244     let alloc = cx
245         .tcx
246         .interpret_interner
247         .get_alloc(alloc_id)
248         .expect("miri allocation never successfully created");
249     Ok(global_initializer(cx, alloc))
250 }
251
252 impl<'a, 'tcx> FunctionCx<'a, 'tcx> {
253     fn const_to_miri_value(
254         &mut self,
255         bx: &Builder<'a, 'tcx>,
256         constant: &'tcx ty::Const<'tcx>,
257     ) -> Result<MiriValue, ConstEvalErr<'tcx>> {
258         match constant.val {
259             ConstVal::Unevaluated(def_id, ref substs) => {
260                 let tcx = bx.tcx();
261                 let param_env = ty::ParamEnv::empty(traits::Reveal::All);
262                 let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap();
263                 let cid = GlobalId {
264                     instance,
265                     promoted: None,
266                 };
267                 let c = tcx.const_eval(param_env.and(cid))?;
268                 self.const_to_miri_value(bx, c)
269             },
270             ConstVal::Value(miri_val) => Ok(miri_val),
271         }
272     }
273
274     pub fn mir_constant_to_miri_value(
275         &mut self,
276         bx: &Builder<'a, 'tcx>,
277         constant: &mir::Constant<'tcx>,
278     ) -> Result<MiriValue, ConstEvalErr<'tcx>> {
279         match constant.literal {
280             mir::Literal::Promoted { index } => {
281                 let param_env = ty::ParamEnv::empty(traits::Reveal::All);
282                 let cid = mir::interpret::GlobalId {
283                     instance: self.instance,
284                     promoted: Some(index),
285                 };
286                 bx.tcx().const_eval(param_env.and(cid))
287             }
288             mir::Literal::Value { value } => {
289                 Ok(self.monomorphize(&value))
290             }
291         }.and_then(|c| self.const_to_miri_value(bx, c))
292     }
293
294     // Old version of trans_constant now used just for SIMD shuffle
295     pub fn remove_me_shuffle_indices(&mut self,
296                                       bx: &Builder<'a, 'tcx>,
297                                       constant: &mir::Constant<'tcx>)
298                                       -> (ValueRef, Ty<'tcx>)
299     {
300         let layout = bx.cx.layout_of(constant.ty);
301         self.mir_constant_to_miri_value(bx, constant)
302             .and_then(|c| {
303                 let llval = match c {
304                     MiriValue::ByVal(val) => {
305                         let scalar = match layout.abi {
306                             layout::Abi::Scalar(ref x) => x,
307                             _ => bug!("from_const: invalid ByVal layout: {:#?}", layout)
308                         };
309                         primval_to_llvm(bx.cx, val, scalar, layout.immediate_llvm_type(bx.cx))
310                     },
311                     MiriValue::ByValPair(a_val, b_val) => {
312                         let (a_scalar, b_scalar) = match layout.abi {
313                             layout::Abi::ScalarPair(ref a, ref b) => (a, b),
314                             _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout)
315                         };
316                         let a_llval = primval_to_llvm(
317                             bx.cx,
318                             a_val,
319                             a_scalar,
320                             layout.scalar_pair_element_llvm_type(bx.cx, 0),
321                         );
322                         let b_llval = primval_to_llvm(
323                             bx.cx,
324                             b_val,
325                             b_scalar,
326                             layout.scalar_pair_element_llvm_type(bx.cx, 1),
327                         );
328                         C_struct(bx.cx, &[a_llval, b_llval], false)
329                     },
330                     MiriValue::ByRef(..) => {
331                         let field_ty = constant.ty.builtin_index().unwrap();
332                         let fields = match constant.ty.sty {
333                             ty::TyArray(_, n) => n.val.unwrap_u64(),
334                             ref other => bug!("invalid simd shuffle type: {}", other),
335                         };
336                         let values: Result<Vec<ValueRef>, _> = (0..fields).map(|field| {
337                             let field = const_val_field(
338                                 bx.tcx(),
339                                 ty::ParamEnv::empty(traits::Reveal::All),
340                                 self.instance,
341                                 None,
342                                 mir::Field::new(field as usize),
343                                 c,
344                                 constant.ty,
345                             )?;
346                             match field.val {
347                                 ConstVal::Value(MiriValue::ByVal(prim)) => {
348                                     let layout = bx.cx.layout_of(field_ty);
349                                     let scalar = match layout.abi {
350                                         layout::Abi::Scalar(ref x) => x,
351                                         _ => bug!("from_const: invalid ByVal layout: {:#?}", layout)
352                                     };
353                                     Ok(primval_to_llvm(
354                                         bx.cx, prim, scalar,
355                                         layout.immediate_llvm_type(bx.cx),
356                                     ))
357                                 },
358                                 other => bug!("simd shuffle field {:?}, {}", other, constant.ty),
359                             }
360                         }).collect();
361                         C_struct(bx.cx, &values?, false)
362                     },
363                 };
364                 Ok((llval, constant.ty))
365             })
366             .unwrap_or_else(|e| {
367                 e.report(bx.tcx(), constant.span, "shuffle_indices");
368                 // We've errored, so we don't have to produce working code.
369                 let ty = self.monomorphize(&constant.ty);
370                 let llty = bx.cx.layout_of(ty).llvm_type(bx.cx);
371                 (C_undef(llty), ty)
372             })
373     }
374 }