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