]> git.lizzy.rs Git - rust.git/blob - src/constant.rs
Rustup to rustc 1.41.0-nightly (82cf3a448 2019-11-14)
[rust.git] / src / constant.rs
1 use std::borrow::Cow;
2
3 use rustc::mir::interpret::{
4     read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, GlobalId, InterpResult, Scalar,
5 };
6 use rustc::ty::{layout::Align, Const, ConstKind};
7 use rustc_mir::interpret::{
8     ImmTy, InterpCx, Machine, Memory, MemoryKind, OpTy, PlaceTy, Pointer, StackPopCleanup,
9     StackPopInfo,
10 };
11
12 use cranelift_module::*;
13
14 use crate::prelude::*;
15
16 #[derive(Default)]
17 pub struct ConstantCx {
18     todo: HashSet<TodoItem>,
19     done: HashSet<DataId>,
20 }
21
22 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
23 enum TodoItem {
24     Alloc(AllocId),
25     Static(DefId),
26 }
27
28 impl ConstantCx {
29     pub fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut Module<impl Backend>) {
30         //println!("todo {:?}", self.todo);
31         define_all_allocs(tcx, module, &mut self);
32         //println!("done {:?}", self.done);
33         self.done.clear();
34     }
35 }
36
37 pub fn codegen_static(constants_cx: &mut ConstantCx, def_id: DefId) {
38     constants_cx.todo.insert(TodoItem::Static(def_id));
39 }
40
41 pub fn codegen_static_ref<'tcx>(
42     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
43     def_id: DefId,
44     ty: Ty<'tcx>,
45 ) -> CPlace<'tcx> {
46     let linkage = crate::linkage::get_static_ref_linkage(fx.tcx, def_id);
47     let data_id = data_id_for_static(fx.tcx, fx.module, def_id, linkage);
48     cplace_for_dataid(fx, ty, data_id)
49 }
50
51 pub fn trans_promoted<'tcx>(
52     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
53     instance: Instance<'tcx>,
54     promoted: Promoted,
55     dest_ty: Ty<'tcx>,
56 ) -> CPlace<'tcx> {
57     match fx.tcx.const_eval(ParamEnv::reveal_all().and(GlobalId {
58         instance,
59         promoted: Some(promoted),
60     })) {
61         Ok(const_) => {
62             let cplace = trans_const_place(fx, const_);
63             debug_assert_eq!(cplace.layout(), fx.layout_of(dest_ty));
64             cplace
65         }
66         Err(_) => crate::trap::trap_unreachable_ret_place(
67             fx,
68             fx.layout_of(dest_ty),
69             "[panic] Tried to get value of promoted value with errored during const eval.",
70         ),
71     }
72 }
73
74 pub fn trans_constant<'tcx>(
75     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
76     constant: &Constant<'tcx>,
77 ) -> CValue<'tcx> {
78     let const_ = force_eval_const(fx, &constant.literal);
79     trans_const_value(fx, const_)
80 }
81
82 pub fn force_eval_const<'tcx>(
83     fx: &FunctionCx<'_, 'tcx, impl Backend>,
84     const_: &'tcx Const,
85 ) -> &'tcx Const<'tcx> {
86     match const_.val {
87         ConstKind::Unevaluated(def_id, ref substs) => {
88             let param_env = ParamEnv::reveal_all();
89             let substs = fx.monomorphize(substs);
90             let instance = Instance::resolve(fx.tcx, param_env, def_id, substs).unwrap();
91             let cid = GlobalId {
92                 instance,
93                 promoted: None,
94             };
95             fx.tcx.const_eval(param_env.and(cid)).unwrap()
96         }
97         _ => fx.monomorphize(&const_),
98     }
99 }
100
101 pub fn trans_const_value<'tcx>(
102     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
103     const_: &'tcx Const<'tcx>,
104 ) -> CValue<'tcx> {
105     let ty = fx.monomorphize(&const_.ty);
106     let layout = fx.layout_of(ty);
107     match ty.kind {
108         ty::Bool | ty::Uint(_) => {
109             let bits = const_.val.try_to_bits(layout.size).unwrap();
110             CValue::const_val(fx, ty, bits)
111         }
112         ty::Int(_) => {
113             let bits = const_.val.try_to_bits(layout.size).unwrap();
114             CValue::const_val(
115                 fx,
116                 ty,
117                 rustc::mir::interpret::sign_extend(bits, layout.size),
118             )
119         }
120         ty::Float(fty) => {
121             let bits = const_.val.try_to_bits(layout.size).unwrap();
122             let val = match fty {
123                 FloatTy::F32 => fx
124                     .bcx
125                     .ins()
126                     .f32const(Ieee32::with_bits(u32::try_from(bits).unwrap())),
127                 FloatTy::F64 => fx
128                     .bcx
129                     .ins()
130                     .f64const(Ieee64::with_bits(u64::try_from(bits).unwrap())),
131             };
132             CValue::by_val(val, layout)
133         }
134         ty::FnDef(_def_id, _substs) => CValue::by_ref(
135             fx.bcx
136                 .ins()
137                 .iconst(fx.pointer_type, fx.pointer_type.bytes() as i64),
138             layout,
139         ),
140         _ => trans_const_place(fx, const_).to_cvalue(fx),
141     }
142 }
143
144 fn trans_const_place<'tcx>(
145     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
146     const_: &'tcx Const<'tcx>,
147 ) -> CPlace<'tcx> {
148     // Adapted from https://github.com/rust-lang/rust/pull/53671/files#diff-e0b58bb6712edaa8595ad7237542c958L551
149     let result = || -> InterpResult<'tcx, &'tcx Allocation> {
150         let mut ecx = InterpCx::new(
151             fx.tcx.at(DUMMY_SP),
152             ty::ParamEnv::reveal_all(),
153             TransPlaceInterpreter,
154             (),
155         );
156         ecx.push_stack_frame(
157             fx.instance,
158             DUMMY_SP,
159             fx.mir,
160             None,
161             StackPopCleanup::None { cleanup: false },
162         )
163         .unwrap();
164         let op = ecx.eval_operand(
165             &Operand::Constant(Box::new(Constant {
166                 span: DUMMY_SP,
167                 user_ty: None,
168                 literal: const_,
169             })),
170             None,
171         )?;
172         let ptr = ecx.allocate(op.layout, MemoryKind::Stack);
173         ecx.copy_op(op, ptr.into())?;
174         let alloc = ecx
175             .memory
176             .get_raw(ptr.to_ref().to_scalar()?.to_ptr()?.alloc_id)?;
177         Ok(fx.tcx.intern_const_alloc(alloc.clone()))
178     };
179     let alloc = result().expect("unable to convert ConstKind to Allocation");
180
181     //println!("const value: {:?} allocation: {:?}", value, alloc);
182     let alloc_id = fx.tcx.alloc_map.lock().create_memory_alloc(alloc);
183     fx.constants_cx.todo.insert(TodoItem::Alloc(alloc_id));
184     let data_id = data_id_for_alloc_id(fx.module, alloc_id, alloc.align);
185     cplace_for_dataid(fx, const_.ty, data_id)
186 }
187
188 fn data_id_for_alloc_id<B: Backend>(
189     module: &mut Module<B>,
190     alloc_id: AllocId,
191     align: Align,
192 ) -> DataId {
193     module
194         .declare_data(
195             &format!("__alloc_{}", alloc_id.0),
196             Linkage::Local,
197             false,
198             Some(align.bytes() as u8),
199         )
200         .unwrap()
201 }
202
203 fn data_id_for_static(
204     tcx: TyCtxt<'_>,
205     module: &mut Module<impl Backend>,
206     def_id: DefId,
207     linkage: Linkage,
208 ) -> DataId {
209     let instance = Instance::mono(tcx, def_id);
210     let symbol_name = tcx.symbol_name(instance).name.as_str();
211     let ty = instance.ty(tcx);
212     let is_mutable = if tcx.is_mutable_static(def_id) {
213         true
214     } else {
215         !ty.is_freeze(tcx, ParamEnv::reveal_all(), DUMMY_SP)
216     };
217     let align = tcx
218         .layout_of(ParamEnv::reveal_all().and(ty))
219         .unwrap()
220         .align
221         .pref
222         .bytes();
223
224     let data_id = module
225         .declare_data(
226             &*symbol_name,
227             linkage,
228             is_mutable,
229             Some(align.try_into().unwrap()),
230         )
231         .unwrap();
232
233     if linkage == Linkage::Preemptible {
234         if let ty::RawPtr(_) = ty.kind {
235         } else {
236             tcx.sess.span_fatal(
237                 tcx.def_span(def_id),
238                 "must have type `*const T` or `*mut T` due to `#[linkage]` attribute",
239             )
240         }
241
242         let mut data_ctx = DataContext::new();
243         let zero_bytes = std::iter::repeat(0)
244             .take(pointer_ty(tcx).bytes() as usize)
245             .collect::<Vec<u8>>()
246             .into_boxed_slice();
247         data_ctx.define(zero_bytes);
248         match module.define_data(data_id, &data_ctx) {
249             // Everytime a weak static is referenced, there will be a zero pointer definition,
250             // so duplicate definitions are expected and allowed.
251             Err(ModuleError::DuplicateDefinition(_)) => {}
252             res => res.unwrap(),
253         }
254     }
255
256     data_id
257 }
258
259 fn cplace_for_dataid<'tcx>(
260     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
261     ty: Ty<'tcx>,
262     data_id: DataId,
263 ) -> CPlace<'tcx> {
264     let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
265     let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
266     let layout = fx.layout_of(fx.monomorphize(&ty));
267     assert!(!layout.is_unsized(), "unsized statics aren't supported");
268     CPlace::for_addr(global_ptr, layout)
269 }
270
271 fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut Module<impl Backend>, cx: &mut ConstantCx) {
272     let memory = Memory::<TransPlaceInterpreter>::new(tcx.at(DUMMY_SP), ());
273
274     while let Some(todo_item) = pop_set(&mut cx.todo) {
275         let (data_id, alloc) = match todo_item {
276             TodoItem::Alloc(alloc_id) => {
277                 //println!("alloc_id {}", alloc_id);
278                 let alloc = memory.get_raw(alloc_id).unwrap();
279                 let data_id = data_id_for_alloc_id(module, alloc_id, alloc.align);
280                 (data_id, alloc)
281             }
282             TodoItem::Static(def_id) => {
283                 //println!("static {:?}", def_id);
284
285                 if tcx.is_foreign_item(def_id) {
286                     continue;
287                 }
288
289                 let instance = ty::Instance::mono(tcx, def_id);
290                 let cid = GlobalId {
291                     instance,
292                     promoted: None,
293                 };
294                 let const_ = tcx.const_eval(ParamEnv::reveal_all().and(cid)).unwrap();
295
296                 let alloc = match const_.val {
297                     ConstKind::Value(ConstValue::ByRef { alloc, offset }) if offset.bytes() == 0 => alloc,
298                     _ => bug!("static const eval returned {:#?}", const_),
299                 };
300
301                 let data_id = data_id_for_static(
302                     tcx,
303                     module,
304                     def_id,
305                     if tcx.is_reachable_non_generic(def_id) {
306                         Linkage::Export
307                     } else {
308                         Linkage::Local
309                     },
310                 );
311                 (data_id, alloc)
312             }
313         };
314
315         //("data_id {}", data_id);
316         if cx.done.contains(&data_id) {
317             continue;
318         }
319
320         let mut data_ctx = DataContext::new();
321
322         let mut bytes = alloc.inspect_with_undef_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
323         // The machO backend of faerie doesn't align data objects correctly unless we do this.
324         while bytes.len() as u64 % 16 != 0 {
325             bytes.push(0xde);
326         }
327         data_ctx.define(bytes.into_boxed_slice());
328
329         for &(offset, (_tag, reloc)) in alloc.relocations().iter() {
330             let addend = {
331                 let endianness = tcx.data_layout.endian;
332                 let offset = offset.bytes() as usize;
333                 let ptr_size = tcx.data_layout.pointer_size;
334                 let bytes = &alloc.inspect_with_undef_and_ptr_outside_interpreter(offset..offset + ptr_size.bytes() as usize);
335                 read_target_uint(endianness, bytes).unwrap()
336             };
337
338             // Don't inline `reloc_target_alloc` into the match. That would cause `tcx.alloc_map`
339             // to be locked for the duration of the match. `data_id_for_static` however may try
340             // to lock `tcx.alloc_map` itself while calculating the layout of the target static.
341             // This would cause a panic in single threaded rustc and a deadlock for parallel rustc.
342             let reloc_target_alloc = tcx.alloc_map.lock().get(reloc).unwrap();
343             let data_id = match reloc_target_alloc {
344                 GlobalAlloc::Function(instance) => {
345                     assert_eq!(addend, 0);
346                     let func_id = crate::abi::import_function(tcx, module, instance);
347                     let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
348                     data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
349                     continue;
350                 }
351                 GlobalAlloc::Memory(_) => {
352                     cx.todo.insert(TodoItem::Alloc(reloc));
353                     data_id_for_alloc_id(module, reloc, alloc.align)
354                 }
355                 GlobalAlloc::Static(def_id) => {
356                     // Don't push a `TodoItem::Static` here, as it will cause statics used by
357                     // multiple crates to be duplicated between them. It isn't necessary anyway,
358                     // as it will get pushed by `codegen_static` when necessary.
359                     data_id_for_static(tcx, module, def_id, Linkage::Import)
360                 }
361             };
362
363             let global_value = module.declare_data_in_data(data_id, &mut data_ctx);
364             data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
365         }
366
367         module.define_data(data_id, &data_ctx).unwrap();
368         cx.done.insert(data_id);
369     }
370
371     assert!(cx.todo.is_empty(), "{:?}", cx.todo);
372 }
373
374 fn pop_set<T: Copy + Eq + ::std::hash::Hash>(set: &mut HashSet<T>) -> Option<T> {
375     if let Some(elem) = set.iter().next().map(|elem| *elem) {
376         set.remove(&elem);
377         Some(elem)
378     } else {
379         None
380     }
381 }
382
383 struct TransPlaceInterpreter;
384
385 impl<'mir, 'tcx> Machine<'mir, 'tcx> for TransPlaceInterpreter {
386     type MemoryKinds = !;
387     type ExtraFnVal = !;
388     type PointerTag = ();
389     type AllocExtra = ();
390     type MemoryExtra = ();
391     type FrameExtra = ();
392     type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation<()>)>;
393
394     const CHECK_ALIGN: bool = true;
395     const STATIC_KIND: Option<!> = None;
396
397     fn enforce_validity(_: &InterpCx<'mir, 'tcx, Self>) -> bool {
398         false
399     }
400
401     fn before_terminator(_: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
402         panic!();
403     }
404
405     fn find_fn(
406         _: &mut InterpCx<'mir, 'tcx, Self>,
407         _: Instance<'tcx>,
408         _: &[OpTy<'tcx>],
409         _: Option<PlaceTy<'tcx>>,
410         _: Option<BasicBlock>,
411         _: Option<BasicBlock>,
412     ) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
413         panic!();
414     }
415
416     fn call_intrinsic(
417         _: &mut InterpCx<'mir, 'tcx, Self>,
418         _: Span,
419         _: Instance<'tcx>,
420         _: &[OpTy<'tcx>],
421         _: Option<PlaceTy<'tcx>>,
422         _: Option<BasicBlock>,
423         _: Option<BasicBlock>,
424     ) -> InterpResult<'tcx> {
425         panic!();
426     }
427
428     fn find_foreign_static(_: TyCtxt<'tcx>, _: DefId) -> InterpResult<'tcx, Cow<'tcx, Allocation>> {
429         panic!();
430     }
431
432     fn binary_ptr_op(
433         _: &InterpCx<'mir, 'tcx, Self>,
434         _: mir::BinOp,
435         _: ImmTy<'tcx>,
436         _: ImmTy<'tcx>,
437     ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
438         panic!();
439     }
440
441     fn ptr_to_int(_: &Memory<'mir, 'tcx, Self>, _: Pointer<()>) -> InterpResult<'tcx, u64> {
442         panic!();
443     }
444
445     fn box_alloc(_: &mut InterpCx<'mir, 'tcx, Self>, _: PlaceTy<'tcx>) -> InterpResult<'tcx> {
446         panic!();
447     }
448
449     fn tag_allocation<'b>(
450         _: &(),
451         _: AllocId,
452         alloc: Cow<'b, Allocation>,
453         _: Option<MemoryKind<!>>,
454     ) -> (Cow<'b, Allocation<(), ()>>, ()) {
455         (alloc, ())
456     }
457
458     fn tag_static_base_pointer(_: &(), _: AllocId) -> Self::PointerTag {
459         ()
460     }
461
462     fn call_extra_fn(
463         _: &mut InterpCx<'mir, 'tcx, Self>,
464         _: !,
465         _: &[OpTy<'tcx, ()>],
466         _: Option<PlaceTy<'tcx, ()>>,
467         _: Option<BasicBlock>,
468     ) -> InterpResult<'tcx> {
469         unreachable!();
470     }
471
472     fn stack_push(_: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
473         Ok(())
474     }
475
476     fn stack_pop(_: &mut InterpCx<'mir, 'tcx, Self>, _: (), _: bool) -> InterpResult<'tcx, StackPopInfo> {
477         Ok(StackPopInfo::Normal)
478     }
479 }
480
481 pub fn mir_operand_get_const_val<'tcx>(
482     fx: &FunctionCx<'_, 'tcx, impl Backend>,
483     operand: &Operand<'tcx>,
484 ) -> Option<&'tcx Const<'tcx>> {
485     let place = match operand {
486         Operand::Copy(place) | Operand::Move(place) => place,
487         Operand::Constant(const_) => return Some(force_eval_const(fx, const_.literal)),
488     };
489
490     assert!(place.projection.is_empty());
491     let static_ = match &place.base {
492         PlaceBase::Static(static_) => static_,
493         PlaceBase::Local(_) => return None,
494     };
495
496     Some(match &static_.kind {
497         StaticKind::Static => unimplemented!(),
498         StaticKind::Promoted(promoted, substs) => {
499             let instance = Instance::new(static_.def_id, fx.monomorphize(substs));
500             fx.tcx
501                 .const_eval(ParamEnv::reveal_all().and(GlobalId {
502                     instance,
503                     promoted: Some(*promoted),
504                 }))
505                 .unwrap()
506         }
507     })
508 }