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