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