]> git.lizzy.rs Git - rust.git/blob - src/constant.rs
Sync from rust 4b9f5cc4c10a161047475cb9bbe02c4fda57fb07
[rust.git] / src / constant.rs
1 //! Handling of `static`s, `const`s and promoted allocations
2
3 use rustc_span::DUMMY_SP;
4
5 use rustc_data_structures::fx::FxHashSet;
6 use rustc_errors::ErrorReported;
7 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
8 use rustc_middle::mir::interpret::{
9     read_target_uint, AllocId, Allocation, ConstValue, ErrorHandled, GlobalAlloc, Pointer, Scalar,
10 };
11 use rustc_middle::ty::{Const, ConstKind};
12
13 use cranelift_codegen::ir::GlobalValueData;
14 use cranelift_module::*;
15
16 use crate::prelude::*;
17
18 #[derive(Default)]
19 pub(crate) struct ConstantCx {
20     todo: Vec<TodoItem>,
21     done: FxHashSet<DataId>,
22 }
23
24 #[derive(Copy, Clone, Debug)]
25 enum TodoItem {
26     Alloc(AllocId),
27     Static(DefId),
28 }
29
30 impl ConstantCx {
31     pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut dyn Module) {
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(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool {
40     let mut all_constants_ok = true;
41     for constant in &fx.mir.required_consts {
42         let const_ = fx.monomorphize(constant.literal);
43         match const_.val {
44             ConstKind::Value(_) => {}
45             ConstKind::Unevaluated(def, ref substs, promoted) => {
46                 if let Err(err) =
47                     fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None)
48                 {
49                     all_constants_ok = false;
50                     match err {
51                         ErrorHandled::Reported(ErrorReported) | ErrorHandled::Linted => {
52                             fx.tcx.sess.span_err(constant.span, "erroneous constant encountered");
53                         }
54                         ErrorHandled::TooGeneric => {
55                             span_bug!(
56                                 constant.span,
57                                 "codgen encountered polymorphic constant: {:?}",
58                                 err
59                             );
60                         }
61                     }
62                 }
63             }
64             ConstKind::Param(_)
65             | ConstKind::Infer(_)
66             | ConstKind::Bound(_, _)
67             | ConstKind::Placeholder(_)
68             | ConstKind::Error(_) => unreachable!("{:?}", const_),
69         }
70     }
71     all_constants_ok
72 }
73
74 pub(crate) fn codegen_static(constants_cx: &mut ConstantCx, def_id: DefId) {
75     constants_cx.todo.push(TodoItem::Static(def_id));
76 }
77
78 pub(crate) fn codegen_tls_ref<'tcx>(
79     fx: &mut FunctionCx<'_, '_, 'tcx>,
80     def_id: DefId,
81     layout: TyAndLayout<'tcx>,
82 ) -> CValue<'tcx> {
83     let data_id = data_id_for_static(fx.tcx, fx.cx.module, def_id, false);
84     let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
85     if fx.clif_comments.enabled() {
86         fx.add_comment(local_data_id, format!("tls {:?}", def_id));
87     }
88     let tls_ptr = fx.bcx.ins().tls_value(fx.pointer_type, local_data_id);
89     CValue::by_val(tls_ptr, layout)
90 }
91
92 fn codegen_static_ref<'tcx>(
93     fx: &mut FunctionCx<'_, '_, 'tcx>,
94     def_id: DefId,
95     layout: TyAndLayout<'tcx>,
96 ) -> CPlace<'tcx> {
97     let data_id = data_id_for_static(fx.tcx, fx.cx.module, def_id, false);
98     let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
99     if fx.clif_comments.enabled() {
100         fx.add_comment(local_data_id, format!("{:?}", def_id));
101     }
102     let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
103     assert!(!layout.is_unsized(), "unsized statics aren't supported");
104     assert!(
105         matches!(
106             fx.bcx.func.global_values[local_data_id],
107             GlobalValueData::Symbol { tls: false, .. }
108         ),
109         "tls static referenced without Rvalue::ThreadLocalRef"
110     );
111     CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout)
112 }
113
114 pub(crate) fn codegen_constant<'tcx>(
115     fx: &mut FunctionCx<'_, '_, 'tcx>,
116     constant: &Constant<'tcx>,
117 ) -> CValue<'tcx> {
118     let const_ = fx.monomorphize(constant.literal);
119     let const_val = match const_.val {
120         ConstKind::Value(const_val) => const_val,
121         ConstKind::Unevaluated(def, ref substs, promoted) if fx.tcx.is_static(def.did) => {
122             assert!(substs.is_empty());
123             assert!(promoted.is_none());
124
125             return codegen_static_ref(
126                 fx,
127                 def.did,
128                 fx.layout_of(fx.monomorphize(&constant.literal.ty)),
129             )
130             .to_cvalue(fx);
131         }
132         ConstKind::Unevaluated(def, ref substs, promoted) => {
133             match fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), def, substs, promoted, None) {
134                 Ok(const_val) => const_val,
135                 Err(_) => {
136                     span_bug!(constant.span, "erroneous constant not captured by required_consts");
137                 }
138             }
139         }
140         ConstKind::Param(_)
141         | ConstKind::Infer(_)
142         | ConstKind::Bound(_, _)
143         | ConstKind::Placeholder(_)
144         | ConstKind::Error(_) => unreachable!("{:?}", const_),
145     };
146
147     codegen_const_value(fx, const_val, const_.ty)
148 }
149
150 pub(crate) fn codegen_const_value<'tcx>(
151     fx: &mut FunctionCx<'_, '_, 'tcx>,
152     const_val: ConstValue<'tcx>,
153     ty: Ty<'tcx>,
154 ) -> CValue<'tcx> {
155     let layout = fx.layout_of(ty);
156     assert!(!layout.is_unsized(), "sized const value");
157
158     if layout.is_zst() {
159         return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout);
160     }
161
162     match const_val {
163         ConstValue::Scalar(x) => {
164             if fx.clif_type(layout.ty).is_none() {
165                 let (size, align) = (layout.size, layout.align.pref);
166                 let mut alloc = Allocation::from_bytes(
167                     std::iter::repeat(0).take(size.bytes_usize()).collect::<Vec<u8>>(),
168                     align,
169                 );
170                 let ptr = Pointer::new(AllocId(!0), Size::ZERO); // The alloc id is never used
171                 alloc.write_scalar(fx, ptr, x.into(), size).unwrap();
172                 let alloc = fx.tcx.intern_const_alloc(alloc);
173                 return CValue::by_ref(pointer_for_allocation(fx, alloc), layout);
174             }
175
176             match x {
177                 Scalar::Int(int) => CValue::const_val(fx, layout, int),
178                 Scalar::Ptr(ptr) => {
179                     let alloc_kind = fx.tcx.get_global_alloc(ptr.alloc_id);
180                     let base_addr = match alloc_kind {
181                         Some(GlobalAlloc::Memory(alloc)) => {
182                             fx.cx.constants_cx.todo.push(TodoItem::Alloc(ptr.alloc_id));
183                             let data_id =
184                                 data_id_for_alloc_id(fx.cx.module, ptr.alloc_id, alloc.mutability);
185                             let local_data_id =
186                                 fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
187                             if fx.clif_comments.enabled() {
188                                 fx.add_comment(local_data_id, format!("{:?}", ptr.alloc_id));
189                             }
190                             fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
191                         }
192                         Some(GlobalAlloc::Function(instance)) => {
193                             let func_id =
194                                 crate::abi::import_function(fx.tcx, fx.cx.module, instance);
195                             let local_func_id =
196                                 fx.cx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
197                             fx.bcx.ins().func_addr(fx.pointer_type, local_func_id)
198                         }
199                         Some(GlobalAlloc::Static(def_id)) => {
200                             assert!(fx.tcx.is_static(def_id));
201                             let data_id = data_id_for_static(fx.tcx, fx.cx.module, def_id, false);
202                             let local_data_id =
203                                 fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
204                             if fx.clif_comments.enabled() {
205                                 fx.add_comment(local_data_id, format!("{:?}", def_id));
206                             }
207                             fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
208                         }
209                         None => bug!("missing allocation {:?}", ptr.alloc_id),
210                     };
211                     let val = if ptr.offset.bytes() != 0 {
212                         fx.bcx.ins().iadd_imm(base_addr, i64::try_from(ptr.offset.bytes()).unwrap())
213                     } else {
214                         base_addr
215                     };
216                     CValue::by_val(val, layout)
217                 }
218             }
219         }
220         ConstValue::ByRef { alloc, offset } => CValue::by_ref(
221             pointer_for_allocation(fx, alloc)
222                 .offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
223             layout,
224         ),
225         ConstValue::Slice { data, start, end } => {
226             let ptr = pointer_for_allocation(fx, data)
227                 .offset_i64(fx, i64::try_from(start).unwrap())
228                 .get_addr(fx);
229             let len = fx
230                 .bcx
231                 .ins()
232                 .iconst(fx.pointer_type, i64::try_from(end.checked_sub(start).unwrap()).unwrap());
233             CValue::by_val_pair(ptr, len, layout)
234         }
235     }
236 }
237
238 fn pointer_for_allocation<'tcx>(
239     fx: &mut FunctionCx<'_, '_, 'tcx>,
240     alloc: &'tcx Allocation,
241 ) -> crate::pointer::Pointer {
242     let alloc_id = fx.tcx.create_memory_alloc(alloc);
243     fx.cx.constants_cx.todo.push(TodoItem::Alloc(alloc_id));
244     let data_id = data_id_for_alloc_id(fx.cx.module, alloc_id, alloc.mutability);
245
246     let local_data_id = fx.cx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
247     if fx.clif_comments.enabled() {
248         fx.add_comment(local_data_id, format!("{:?}", alloc_id));
249     }
250     let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
251     crate::pointer::Pointer::new(global_ptr)
252 }
253
254 fn data_id_for_alloc_id(
255     module: &mut dyn Module,
256     alloc_id: AllocId,
257     mutability: rustc_hir::Mutability,
258 ) -> DataId {
259     module
260         .declare_data(
261             &format!(".L__alloc_{:x}", alloc_id.0),
262             Linkage::Local,
263             mutability == rustc_hir::Mutability::Mut,
264             false,
265         )
266         .unwrap()
267 }
268
269 fn data_id_for_static(
270     tcx: TyCtxt<'_>,
271     module: &mut dyn Module,
272     def_id: DefId,
273     definition: bool,
274 ) -> DataId {
275     let rlinkage = tcx.codegen_fn_attrs(def_id).linkage;
276     let linkage = if definition {
277         crate::linkage::get_static_linkage(tcx, def_id)
278     } else if rlinkage == Some(rustc_middle::mir::mono::Linkage::ExternalWeak)
279         || rlinkage == Some(rustc_middle::mir::mono::Linkage::WeakAny)
280     {
281         Linkage::Preemptible
282     } else {
283         Linkage::Import
284     };
285
286     let instance = Instance::mono(tcx, def_id).polymorphize(tcx);
287     let symbol_name = tcx.symbol_name(instance).name;
288     let ty = instance.ty(tcx, ParamEnv::reveal_all());
289     let is_mutable = if tcx.is_mutable_static(def_id) {
290         true
291     } else {
292         !ty.is_freeze(tcx.at(DUMMY_SP), ParamEnv::reveal_all())
293     };
294     let align = tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap().align.pref.bytes();
295
296     let attrs = tcx.codegen_fn_attrs(def_id);
297
298     let data_id = module
299         .declare_data(
300             &*symbol_name,
301             linkage,
302             is_mutable,
303             attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL),
304         )
305         .unwrap();
306
307     if rlinkage.is_some() {
308         // Comment copied from https://github.com/rust-lang/rust/blob/45060c2a66dfd667f88bd8b94261b28a58d85bd5/src/librustc_codegen_llvm/consts.rs#L141
309         // Declare an internal global `extern_with_linkage_foo` which
310         // is initialized with the address of `foo`.  If `foo` is
311         // discarded during linking (for example, if `foo` has weak
312         // linkage and there are no definitions), then
313         // `extern_with_linkage_foo` will instead be initialized to
314         // zero.
315
316         let ref_name = format!("_rust_extern_with_linkage_{}", symbol_name);
317         let ref_data_id = module.declare_data(&ref_name, Linkage::Local, false, false).unwrap();
318         let mut data_ctx = DataContext::new();
319         data_ctx.set_align(align);
320         let data = module.declare_data_in_data(data_id, &mut data_ctx);
321         data_ctx.define(std::iter::repeat(0).take(pointer_ty(tcx).bytes() as usize).collect());
322         data_ctx.write_data_addr(0, data, 0);
323         match module.define_data(ref_data_id, &data_ctx) {
324             // Every time the static is referenced there will be another definition of this global,
325             // so duplicate definitions are expected and allowed.
326             Err(ModuleError::DuplicateDefinition(_)) => {}
327             res => res.unwrap(),
328         }
329         ref_data_id
330     } else {
331         data_id
332     }
333 }
334
335 fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut ConstantCx) {
336     while let Some(todo_item) = cx.todo.pop() {
337         let (data_id, alloc, section_name) = match todo_item {
338             TodoItem::Alloc(alloc_id) => {
339                 //println!("alloc_id {}", alloc_id);
340                 let alloc = match tcx.get_global_alloc(alloc_id).unwrap() {
341                     GlobalAlloc::Memory(alloc) => alloc,
342                     GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(),
343                 };
344                 let data_id = data_id_for_alloc_id(module, alloc_id, alloc.mutability);
345                 (data_id, alloc, None)
346             }
347             TodoItem::Static(def_id) => {
348                 //println!("static {:?}", def_id);
349
350                 let section_name = tcx.codegen_fn_attrs(def_id).link_section.map(|s| s.as_str());
351
352                 let alloc = tcx.eval_static_initializer(def_id).unwrap();
353
354                 let data_id = data_id_for_static(tcx, module, def_id, true);
355                 (data_id, alloc, section_name)
356             }
357         };
358
359         //("data_id {}", data_id);
360         if cx.done.contains(&data_id) {
361             continue;
362         }
363
364         let mut data_ctx = DataContext::new();
365         data_ctx.set_align(alloc.align.bytes());
366
367         if let Some(section_name) = section_name {
368             // FIXME set correct segment for Mach-O files
369             data_ctx.set_segment_section("", &*section_name);
370         }
371
372         let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
373         data_ctx.define(bytes.into_boxed_slice());
374
375         for &(offset, (_tag, reloc)) in alloc.relocations().iter() {
376             let addend = {
377                 let endianness = tcx.data_layout.endian;
378                 let offset = offset.bytes() as usize;
379                 let ptr_size = tcx.data_layout.pointer_size;
380                 let bytes = &alloc.inspect_with_uninit_and_ptr_outside_interpreter(
381                     offset..offset + ptr_size.bytes() as usize,
382                 );
383                 read_target_uint(endianness, bytes).unwrap()
384             };
385
386             let reloc_target_alloc = tcx.get_global_alloc(reloc).unwrap();
387             let data_id = match reloc_target_alloc {
388                 GlobalAlloc::Function(instance) => {
389                     assert_eq!(addend, 0);
390                     let func_id = crate::abi::import_function(tcx, module, instance);
391                     let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
392                     data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
393                     continue;
394                 }
395                 GlobalAlloc::Memory(target_alloc) => {
396                     cx.todo.push(TodoItem::Alloc(reloc));
397                     data_id_for_alloc_id(module, reloc, target_alloc.mutability)
398                 }
399                 GlobalAlloc::Static(def_id) => {
400                     if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
401                     {
402                         tcx.sess.fatal(&format!(
403                             "Allocation {:?} contains reference to TLS value {:?}",
404                             alloc, def_id
405                         ));
406                     }
407
408                     // Don't push a `TodoItem::Static` here, as it will cause statics used by
409                     // multiple crates to be duplicated between them. It isn't necessary anyway,
410                     // as it will get pushed by `codegen_static` when necessary.
411                     data_id_for_static(tcx, module, def_id, false)
412                 }
413             };
414
415             let global_value = module.declare_data_in_data(data_id, &mut data_ctx);
416             data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
417         }
418
419         // FIXME don't duplicate definitions in lazy jit mode
420         let _ = module.define_data(data_id, &data_ctx);
421         cx.done.insert(data_id);
422     }
423
424     assert!(cx.todo.is_empty(), "{:?}", cx.todo);
425 }
426
427 pub(crate) fn mir_operand_get_const_val<'tcx>(
428     fx: &FunctionCx<'_, '_, 'tcx>,
429     operand: &Operand<'tcx>,
430 ) -> Option<&'tcx Const<'tcx>> {
431     match operand {
432         Operand::Copy(_) | Operand::Move(_) => None,
433         Operand::Constant(const_) => {
434             Some(fx.monomorphize(const_.literal).eval(fx.tcx, ParamEnv::reveal_all()))
435         }
436     }
437 }