]> git.lizzy.rs Git - rust.git/blob - src/constant.rs
53e0941d7e9c1b705920a313341fed9b012d75fa
[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, PanicInfo, PlaceTy, Pointer,
9     StackPopCleanup, 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             crate::pointer::Pointer::const_addr(fx, fx.pointer_type.bytes() as i64),
136             layout,
137         ),
138         _ => trans_const_place(fx, const_).to_cvalue(fx),
139     }
140 }
141
142 fn trans_const_place<'tcx>(
143     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
144     const_: &'tcx Const<'tcx>,
145 ) -> CPlace<'tcx> {
146     // Adapted from https://github.com/rust-lang/rust/pull/53671/files#diff-e0b58bb6712edaa8595ad7237542c958L551
147     let result = || -> InterpResult<'tcx, &'tcx Allocation> {
148         let mut ecx = InterpCx::new(
149             fx.tcx.at(DUMMY_SP),
150             ty::ParamEnv::reveal_all(),
151             TransPlaceInterpreter,
152             (),
153         );
154         ecx.push_stack_frame(
155             fx.instance,
156             DUMMY_SP,
157             fx.mir,
158             None,
159             StackPopCleanup::None { cleanup: false },
160         )
161         .unwrap();
162         let op = ecx.eval_operand(
163             &Operand::Constant(Box::new(Constant {
164                 span: DUMMY_SP,
165                 user_ty: None,
166                 literal: const_,
167             })),
168             None,
169         )?;
170         let ptr = ecx.allocate(op.layout, MemoryKind::Stack);
171         ecx.copy_op(op, ptr.into())?;
172         let alloc = ecx
173             .memory
174             .get_raw(ptr.to_ref().to_scalar()?.to_ptr()?.alloc_id)?;
175         Ok(fx.tcx.intern_const_alloc(alloc.clone()))
176     };
177     let alloc = result().expect("unable to convert ConstKind to Allocation");
178
179     //println!("const value: {:?} allocation: {:?}", value, alloc);
180     let alloc_id = fx.tcx.alloc_map.lock().create_memory_alloc(alloc);
181     fx.constants_cx.todo.insert(TodoItem::Alloc(alloc_id));
182     let data_id = data_id_for_alloc_id(fx.module, alloc_id, alloc.align);
183     cplace_for_dataid(fx, const_.ty, data_id)
184 }
185
186 fn data_id_for_alloc_id<B: Backend>(
187     module: &mut Module<B>,
188     alloc_id: AllocId,
189     align: Align,
190 ) -> DataId {
191     module
192         .declare_data(
193             &format!("__alloc_{}", alloc_id.0),
194             Linkage::Local,
195             false,
196             Some(align.bytes() as u8),
197         )
198         .unwrap()
199 }
200
201 fn data_id_for_static(
202     tcx: TyCtxt<'_>,
203     module: &mut Module<impl Backend>,
204     def_id: DefId,
205     linkage: Linkage,
206 ) -> DataId {
207     let instance = Instance::mono(tcx, def_id);
208     let symbol_name = tcx.symbol_name(instance).name.as_str();
209     let ty = instance.ty(tcx);
210     let is_mutable = if tcx.is_mutable_static(def_id) {
211         true
212     } else {
213         !ty.is_freeze(tcx, ParamEnv::reveal_all(), DUMMY_SP)
214     };
215     let align = tcx
216         .layout_of(ParamEnv::reveal_all().and(ty))
217         .unwrap()
218         .align
219         .pref
220         .bytes();
221
222     let data_id = module
223         .declare_data(
224             &*symbol_name,
225             linkage,
226             is_mutable,
227             Some(align.try_into().unwrap()),
228         )
229         .unwrap();
230
231     if linkage == Linkage::Preemptible {
232         if let ty::RawPtr(_) = ty.kind {
233         } else {
234             tcx.sess.span_fatal(
235                 tcx.def_span(def_id),
236                 "must have type `*const T` or `*mut T` due to `#[linkage]` attribute",
237             )
238         }
239
240         let mut data_ctx = DataContext::new();
241         let zero_bytes = std::iter::repeat(0)
242             .take(pointer_ty(tcx).bytes() as usize)
243             .collect::<Vec<u8>>()
244             .into_boxed_slice();
245         data_ctx.define(zero_bytes);
246         match module.define_data(data_id, &data_ctx) {
247             // Everytime a weak static is referenced, there will be a zero pointer definition,
248             // so duplicate definitions are expected and allowed.
249             Err(ModuleError::DuplicateDefinition(_)) => {}
250             res => res.unwrap(),
251         }
252     }
253
254     data_id
255 }
256
257 fn cplace_for_dataid<'tcx>(
258     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
259     ty: Ty<'tcx>,
260     data_id: DataId,
261 ) -> CPlace<'tcx> {
262     let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
263     let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
264     let layout = fx.layout_of(fx.monomorphize(&ty));
265     assert!(!layout.is_unsized(), "unsized statics aren't supported");
266     CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout)
267 }
268
269 fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut Module<impl Backend>, cx: &mut ConstantCx) {
270     let memory = Memory::<TransPlaceInterpreter>::new(tcx.at(DUMMY_SP), ());
271
272     while let Some(todo_item) = pop_set(&mut cx.todo) {
273         let (data_id, alloc) = match todo_item {
274             TodoItem::Alloc(alloc_id) => {
275                 //println!("alloc_id {}", alloc_id);
276                 let alloc = memory.get_raw(alloc_id).unwrap();
277                 let data_id = data_id_for_alloc_id(module, alloc_id, alloc.align);
278                 (data_id, alloc)
279             }
280             TodoItem::Static(def_id) => {
281                 //println!("static {:?}", def_id);
282
283                 if tcx.is_foreign_item(def_id) {
284                     continue;
285                 }
286
287                 let instance = ty::Instance::mono(tcx, def_id);
288                 let cid = GlobalId {
289                     instance,
290                     promoted: None,
291                 };
292                 let const_ = tcx.const_eval(ParamEnv::reveal_all().and(cid)).unwrap();
293
294                 let alloc = match const_.val {
295                     ConstKind::Value(ConstValue::ByRef { alloc, offset }) if offset.bytes() == 0 => alloc,
296                     _ => bug!("static const eval returned {:#?}", const_),
297                 };
298
299                 let data_id = data_id_for_static(
300                     tcx,
301                     module,
302                     def_id,
303                     if tcx.is_reachable_non_generic(def_id) {
304                         Linkage::Export
305                     } else {
306                         Linkage::Local
307                     },
308                 );
309                 (data_id, alloc)
310             }
311         };
312
313         //("data_id {}", data_id);
314         if cx.done.contains(&data_id) {
315             continue;
316         }
317
318         let mut data_ctx = DataContext::new();
319
320         let mut bytes = alloc.inspect_with_undef_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
321         // The machO backend of faerie doesn't align data objects correctly unless we do this.
322         while bytes.len() as u64 % 16 != 0 {
323             bytes.push(0xde);
324         }
325         data_ctx.define(bytes.into_boxed_slice());
326
327         for &(offset, (_tag, reloc)) in alloc.relocations().iter() {
328             let addend = {
329                 let endianness = tcx.data_layout.endian;
330                 let offset = offset.bytes() as usize;
331                 let ptr_size = tcx.data_layout.pointer_size;
332                 let bytes = &alloc.inspect_with_undef_and_ptr_outside_interpreter(offset..offset + ptr_size.bytes() as usize);
333                 read_target_uint(endianness, bytes).unwrap()
334             };
335
336             // Don't inline `reloc_target_alloc` into the match. That would cause `tcx.alloc_map`
337             // to be locked for the duration of the match. `data_id_for_static` however may try
338             // to lock `tcx.alloc_map` itself while calculating the layout of the target static.
339             // This would cause a panic in single threaded rustc and a deadlock for parallel rustc.
340             let reloc_target_alloc = tcx.alloc_map.lock().get(reloc).unwrap();
341             let data_id = match reloc_target_alloc {
342                 GlobalAlloc::Function(instance) => {
343                     assert_eq!(addend, 0);
344                     let func_id = crate::abi::import_function(tcx, module, instance);
345                     let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
346                     data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
347                     continue;
348                 }
349                 GlobalAlloc::Memory(_) => {
350                     cx.todo.insert(TodoItem::Alloc(reloc));
351                     data_id_for_alloc_id(module, reloc, alloc.align)
352                 }
353                 GlobalAlloc::Static(def_id) => {
354                     // Don't push a `TodoItem::Static` here, as it will cause statics used by
355                     // multiple crates to be duplicated between them. It isn't necessary anyway,
356                     // as it will get pushed by `codegen_static` when necessary.
357                     data_id_for_static(
358                         tcx,
359                         module,
360                         def_id,
361                         crate::linkage::get_static_ref_linkage(tcx, def_id),
362                     )
363                 }
364             };
365
366             let global_value = module.declare_data_in_data(data_id, &mut data_ctx);
367             data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
368         }
369
370         module.define_data(data_id, &data_ctx).unwrap();
371         cx.done.insert(data_id);
372     }
373
374     assert!(cx.todo.is_empty(), "{:?}", cx.todo);
375 }
376
377 fn pop_set<T: Copy + Eq + ::std::hash::Hash>(set: &mut HashSet<T>) -> Option<T> {
378     if let Some(elem) = set.iter().next().map(|elem| *elem) {
379         set.remove(&elem);
380         Some(elem)
381     } else {
382         None
383     }
384 }
385
386 struct TransPlaceInterpreter;
387
388 impl<'mir, 'tcx> Machine<'mir, 'tcx> for TransPlaceInterpreter {
389     type MemoryKinds = !;
390     type ExtraFnVal = !;
391     type PointerTag = ();
392     type AllocExtra = ();
393     type MemoryExtra = ();
394     type FrameExtra = ();
395     type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation<()>)>;
396
397     const CHECK_ALIGN: bool = true;
398     const STATIC_KIND: Option<!> = None;
399
400     fn enforce_validity(_: &InterpCx<'mir, 'tcx, Self>) -> bool {
401         false
402     }
403
404     fn before_terminator(_: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
405         panic!();
406     }
407
408     fn find_mir_or_eval_fn(
409         _: &mut InterpCx<'mir, 'tcx, Self>,
410         _: Instance<'tcx>,
411         _: &[OpTy<'tcx>],
412         _: Option<(PlaceTy<'tcx>, BasicBlock)>,
413         _: Option<BasicBlock>,
414     ) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
415         panic!();
416     }
417
418     fn call_intrinsic(
419         _: &mut InterpCx<'mir, 'tcx, Self>,
420         _: Span,
421         _: Instance<'tcx>,
422         _: &[OpTy<'tcx>],
423         _: Option<(PlaceTy<'tcx>, BasicBlock)>,
424         _: Option<BasicBlock>,
425     ) -> InterpResult<'tcx> {
426         panic!();
427     }
428
429     fn find_foreign_static(_: TyCtxt<'tcx>, _: DefId) -> InterpResult<'tcx, Cow<'tcx, Allocation>> {
430         panic!();
431     }
432
433     fn binary_ptr_op(
434         _: &InterpCx<'mir, 'tcx, Self>,
435         _: mir::BinOp,
436         _: ImmTy<'tcx>,
437         _: ImmTy<'tcx>,
438     ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
439         panic!();
440     }
441
442     fn ptr_to_int(_: &Memory<'mir, 'tcx, Self>, _: Pointer<()>) -> InterpResult<'tcx, u64> {
443         panic!();
444     }
445
446     fn box_alloc(_: &mut InterpCx<'mir, 'tcx, Self>, _: PlaceTy<'tcx>) -> InterpResult<'tcx> {
447         panic!();
448     }
449
450     fn init_allocation_extra<'b>(
451         _: &(),
452         _: AllocId,
453         alloc: Cow<'b, Allocation>,
454         _: Option<MemoryKind<!>>,
455     ) -> (Cow<'b, Allocation<(), ()>>, ()) {
456         (alloc, ())
457     }
458
459     fn tag_static_base_pointer(_: &(), _: AllocId) -> Self::PointerTag {
460         ()
461     }
462
463     fn call_extra_fn(
464         _: &mut InterpCx<'mir, 'tcx, Self>,
465         _: !,
466         _: &[OpTy<'tcx, ()>],
467         _: Option<(PlaceTy<'tcx, ()>, BasicBlock)>,
468         _: Option<BasicBlock>,
469     ) -> InterpResult<'tcx> {
470         unreachable!();
471     }
472
473     fn stack_push(_: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
474         Ok(())
475     }
476
477     fn stack_pop(_: &mut InterpCx<'mir, 'tcx, Self>, _: (), _: bool) -> InterpResult<'tcx, StackPopInfo> {
478         Ok(StackPopInfo::Normal)
479     }
480
481     fn assert_panic(
482         _: &mut InterpCx<'mir, 'tcx, Self>,
483         _: Span,
484         _: &PanicInfo<Operand<'tcx>>,
485         _: Option<BasicBlock>,
486     ) -> InterpResult<'tcx> {
487         unreachable!()
488     }
489 }
490
491 pub fn mir_operand_get_const_val<'tcx>(
492     fx: &FunctionCx<'_, 'tcx, impl Backend>,
493     operand: &Operand<'tcx>,
494 ) -> Option<&'tcx Const<'tcx>> {
495     let place = match operand {
496         Operand::Copy(place) | Operand::Move(place) => place,
497         Operand::Constant(const_) => return Some(force_eval_const(fx, const_.literal)),
498     };
499
500     assert!(place.projection.is_empty());
501     let static_ = match &place.base {
502         PlaceBase::Static(static_) => static_,
503         PlaceBase::Local(_) => return None,
504     };
505
506     Some(match &static_.kind {
507         StaticKind::Static => unimplemented!(),
508         StaticKind::Promoted(promoted, substs) => {
509             let instance = Instance::new(static_.def_id, fx.monomorphize(substs));
510             fx.tcx
511                 .const_eval(ParamEnv::reveal_all().and(GlobalId {
512                     instance,
513                     promoted: Some(*promoted),
514                 }))
515                 .unwrap()
516         }
517     })
518 }