]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_codegen_cranelift/src/constant.rs
Auto merge of #103217 - mejrs:track, r=eholk
[rust.git] / compiler / rustc_codegen_cranelift / src / constant.rs
1 //! Handling of `static`s, `const`s and promoted allocations
2
3 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
4 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
5 use rustc_middle::mir::interpret::{
6     read_target_uint, AllocId, ConstAllocation, ConstValue, ErrorHandled, GlobalAlloc, Scalar,
7 };
8
9 use cranelift_module::*;
10
11 use crate::prelude::*;
12
13 pub(crate) struct ConstantCx {
14     todo: Vec<TodoItem>,
15     done: FxHashSet<DataId>,
16     anon_allocs: FxHashMap<AllocId, DataId>,
17 }
18
19 #[derive(Copy, Clone, Debug)]
20 enum TodoItem {
21     Alloc(AllocId),
22     Static(DefId),
23 }
24
25 impl ConstantCx {
26     pub(crate) fn new() -> Self {
27         ConstantCx { todo: vec![], done: FxHashSet::default(), anon_allocs: FxHashMap::default() }
28     }
29
30     pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut dyn Module) {
31         //println!("todo {:?}", self.todo);
32         define_all_allocs(tcx, module, &mut self);
33         //println!("done {:?}", self.done);
34         self.done.clear();
35     }
36 }
37
38 pub(crate) fn check_constants(fx: &mut FunctionCx<'_, '_, '_>) -> bool {
39     let mut all_constants_ok = true;
40     for constant in &fx.mir.required_consts {
41         let unevaluated = match fx.monomorphize(constant.literal) {
42             ConstantKind::Ty(_) => unreachable!(),
43             ConstantKind::Unevaluated(uv, _) => uv,
44             ConstantKind::Val(..) => continue,
45         };
46
47         if let Err(err) = fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), unevaluated, None) {
48             all_constants_ok = false;
49             match err {
50                 ErrorHandled::Reported(_) | ErrorHandled::Linted => {
51                     fx.tcx.sess.span_err(constant.span, "erroneous constant encountered");
52                 }
53                 ErrorHandled::TooGeneric => {
54                     span_bug!(constant.span, "codegen encountered polymorphic constant: {:?}", err);
55                 }
56             }
57         }
58     }
59     all_constants_ok
60 }
61
62 pub(crate) fn codegen_static(tcx: TyCtxt<'_>, module: &mut dyn Module, def_id: DefId) {
63     let mut constants_cx = ConstantCx::new();
64     constants_cx.todo.push(TodoItem::Static(def_id));
65     constants_cx.finalize(tcx, module);
66 }
67
68 pub(crate) fn codegen_tls_ref<'tcx>(
69     fx: &mut FunctionCx<'_, '_, 'tcx>,
70     def_id: DefId,
71     layout: TyAndLayout<'tcx>,
72 ) -> CValue<'tcx> {
73     let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false);
74     let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
75     if fx.clif_comments.enabled() {
76         fx.add_comment(local_data_id, format!("tls {:?}", def_id));
77     }
78     let tls_ptr = fx.bcx.ins().tls_value(fx.pointer_type, local_data_id);
79     CValue::by_val(tls_ptr, layout)
80 }
81
82 pub(crate) fn eval_mir_constant<'tcx>(
83     fx: &mut FunctionCx<'_, '_, 'tcx>,
84     constant: &Constant<'tcx>,
85 ) -> (ConstValue<'tcx>, Ty<'tcx>) {
86     let constant_kind = fx.monomorphize(constant.literal);
87     let uv = match constant_kind {
88         ConstantKind::Ty(const_) => match const_.kind() {
89             ty::ConstKind::Unevaluated(uv) => uv.expand(),
90             ty::ConstKind::Value(val) => {
91                 return (fx.tcx.valtree_to_const_val((const_.ty(), val)), const_.ty());
92             }
93             err => span_bug!(
94                 constant.span,
95                 "encountered bad ConstKind after monomorphizing: {:?}",
96                 err
97             ),
98         },
99         ConstantKind::Unevaluated(mir::UnevaluatedConst { def, .. }, _)
100             if fx.tcx.is_static(def.did) =>
101         {
102             span_bug!(constant.span, "MIR constant refers to static");
103         }
104         ConstantKind::Unevaluated(uv, _) => uv,
105         ConstantKind::Val(val, _) => return (val, constant_kind.ty()),
106     };
107
108     (
109         fx.tcx.const_eval_resolve(ty::ParamEnv::reveal_all(), uv, None).unwrap_or_else(|_err| {
110             span_bug!(constant.span, "erroneous constant not captured by required_consts");
111         }),
112         constant_kind.ty(),
113     )
114 }
115
116 pub(crate) fn codegen_constant_operand<'tcx>(
117     fx: &mut FunctionCx<'_, '_, 'tcx>,
118     constant: &Constant<'tcx>,
119 ) -> CValue<'tcx> {
120     let (const_val, ty) = eval_mir_constant(fx, constant);
121
122     codegen_const_value(fx, const_val, ty)
123 }
124
125 pub(crate) fn codegen_const_value<'tcx>(
126     fx: &mut FunctionCx<'_, '_, 'tcx>,
127     const_val: ConstValue<'tcx>,
128     ty: Ty<'tcx>,
129 ) -> CValue<'tcx> {
130     let layout = fx.layout_of(ty);
131     assert!(!layout.is_unsized(), "sized const value");
132
133     if layout.is_zst() {
134         return CValue::by_ref(crate::Pointer::dangling(layout.align.pref), layout);
135     }
136
137     match const_val {
138         ConstValue::ZeroSized => unreachable!(), // we already handles ZST above
139         ConstValue::Scalar(x) => match x {
140             Scalar::Int(int) => {
141                 if fx.clif_type(layout.ty).is_some() {
142                     return CValue::const_val(fx, layout, int);
143                 } else {
144                     let raw_val = int.to_bits(int.size()).unwrap();
145                     let val = match int.size().bytes() {
146                         1 => fx.bcx.ins().iconst(types::I8, raw_val as i64),
147                         2 => fx.bcx.ins().iconst(types::I16, raw_val as i64),
148                         4 => fx.bcx.ins().iconst(types::I32, raw_val as i64),
149                         8 => fx.bcx.ins().iconst(types::I64, raw_val as i64),
150                         16 => {
151                             let lsb = fx.bcx.ins().iconst(types::I64, raw_val as u64 as i64);
152                             let msb =
153                                 fx.bcx.ins().iconst(types::I64, (raw_val >> 64) as u64 as i64);
154                             fx.bcx.ins().iconcat(lsb, msb)
155                         }
156                         _ => unreachable!(),
157                     };
158
159                     let place = CPlace::new_stack_slot(fx, layout);
160                     place.to_ptr().store(fx, val, MemFlags::trusted());
161                     place.to_cvalue(fx)
162                 }
163             }
164             Scalar::Ptr(ptr, _size) => {
165                 let (alloc_id, offset) = ptr.into_parts(); // we know the `offset` is relative
166                 let base_addr = match fx.tcx.global_alloc(alloc_id) {
167                     GlobalAlloc::Memory(alloc) => {
168                         let data_id = data_id_for_alloc_id(
169                             &mut fx.constants_cx,
170                             fx.module,
171                             alloc_id,
172                             alloc.inner().mutability,
173                         );
174                         let local_data_id =
175                             fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
176                         if fx.clif_comments.enabled() {
177                             fx.add_comment(local_data_id, format!("{:?}", alloc_id));
178                         }
179                         fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
180                     }
181                     GlobalAlloc::Function(instance) => {
182                         let func_id = crate::abi::import_function(fx.tcx, fx.module, instance);
183                         let local_func_id =
184                             fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
185                         fx.bcx.ins().func_addr(fx.pointer_type, local_func_id)
186                     }
187                     GlobalAlloc::VTable(ty, trait_ref) => {
188                         let alloc_id = fx.tcx.vtable_allocation((ty, trait_ref));
189                         let alloc = fx.tcx.global_alloc(alloc_id).unwrap_memory();
190                         // FIXME: factor this common code with the `Memory` arm into a function?
191                         let data_id = data_id_for_alloc_id(
192                             &mut fx.constants_cx,
193                             fx.module,
194                             alloc_id,
195                             alloc.inner().mutability,
196                         );
197                         let local_data_id =
198                             fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
199                         fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
200                     }
201                     GlobalAlloc::Static(def_id) => {
202                         assert!(fx.tcx.is_static(def_id));
203                         let data_id = data_id_for_static(fx.tcx, fx.module, def_id, false);
204                         let local_data_id =
205                             fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
206                         if fx.clif_comments.enabled() {
207                             fx.add_comment(local_data_id, format!("{:?}", def_id));
208                         }
209                         fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
210                     }
211                 };
212                 let val = if offset.bytes() != 0 {
213                     fx.bcx.ins().iadd_imm(base_addr, i64::try_from(offset.bytes()).unwrap())
214                 } else {
215                     base_addr
216                 };
217                 CValue::by_val(val, layout)
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: ConstAllocation<'tcx>,
241 ) -> crate::pointer::Pointer {
242     let alloc_id = fx.tcx.create_memory_alloc(alloc);
243     let data_id = data_id_for_alloc_id(
244         &mut fx.constants_cx,
245         &mut *fx.module,
246         alloc_id,
247         alloc.inner().mutability,
248     );
249
250     let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
251     if fx.clif_comments.enabled() {
252         fx.add_comment(local_data_id, format!("{:?}", alloc_id));
253     }
254     let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
255     crate::pointer::Pointer::new(global_ptr)
256 }
257
258 pub(crate) fn data_id_for_alloc_id(
259     cx: &mut ConstantCx,
260     module: &mut dyn Module,
261     alloc_id: AllocId,
262     mutability: rustc_hir::Mutability,
263 ) -> DataId {
264     cx.todo.push(TodoItem::Alloc(alloc_id));
265     *cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
266         module.declare_anonymous_data(mutability == rustc_hir::Mutability::Mut, false).unwrap()
267     })
268 }
269
270 fn data_id_for_static(
271     tcx: TyCtxt<'_>,
272     module: &mut dyn Module,
273     def_id: DefId,
274     definition: bool,
275 ) -> DataId {
276     let rlinkage = tcx.codegen_fn_attrs(def_id).linkage;
277     let linkage = if definition {
278         crate::linkage::get_static_linkage(tcx, def_id)
279     } else if rlinkage == Some(rustc_middle::mir::mono::Linkage::ExternalWeak)
280         || rlinkage == Some(rustc_middle::mir::mono::Linkage::WeakAny)
281     {
282         Linkage::Preemptible
283     } else {
284         Linkage::Import
285     };
286
287     let instance = Instance::mono(tcx, def_id).polymorphize(tcx);
288     let symbol_name = tcx.symbol_name(instance).name;
289     let ty = instance.ty(tcx, ParamEnv::reveal_all());
290     let is_mutable = if tcx.is_mutable_static(def_id) {
291         true
292     } else {
293         !ty.is_freeze(tcx, ParamEnv::reveal_all())
294     };
295     let align = tcx.layout_of(ParamEnv::reveal_all().and(ty)).unwrap().align.pref.bytes();
296
297     let attrs = tcx.codegen_fn_attrs(def_id);
298
299     let data_id = match module.declare_data(
300         &*symbol_name,
301         linkage,
302         is_mutable,
303         attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL),
304     ) {
305         Ok(data_id) => data_id,
306         Err(ModuleError::IncompatibleDeclaration(_)) => tcx.sess.fatal(&format!(
307             "attempt to declare `{symbol_name}` as static, but it was already declared as function"
308         )),
309         Err(err) => Err::<_, _>(err).unwrap(),
310     };
311
312     if rlinkage.is_some() {
313         // Comment copied from https://github.com/rust-lang/rust/blob/45060c2a66dfd667f88bd8b94261b28a58d85bd5/src/librustc_codegen_llvm/consts.rs#L141
314         // Declare an internal global `extern_with_linkage_foo` which
315         // is initialized with the address of `foo`.  If `foo` is
316         // discarded during linking (for example, if `foo` has weak
317         // linkage and there are no definitions), then
318         // `extern_with_linkage_foo` will instead be initialized to
319         // zero.
320
321         let ref_name = format!("_rust_extern_with_linkage_{}", symbol_name);
322         let ref_data_id = module.declare_data(&ref_name, Linkage::Local, false, false).unwrap();
323         let mut data_ctx = DataContext::new();
324         data_ctx.set_align(align);
325         let data = module.declare_data_in_data(data_id, &mut data_ctx);
326         data_ctx.define(std::iter::repeat(0).take(pointer_ty(tcx).bytes() as usize).collect());
327         data_ctx.write_data_addr(0, data, 0);
328         match module.define_data(ref_data_id, &data_ctx) {
329             // Every time the static is referenced there will be another definition of this global,
330             // so duplicate definitions are expected and allowed.
331             Err(ModuleError::DuplicateDefinition(_)) => {}
332             res => res.unwrap(),
333         }
334         ref_data_id
335     } else {
336         data_id
337     }
338 }
339
340 fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut dyn Module, cx: &mut ConstantCx) {
341     while let Some(todo_item) = cx.todo.pop() {
342         let (data_id, alloc, section_name) = match todo_item {
343             TodoItem::Alloc(alloc_id) => {
344                 let alloc = match tcx.global_alloc(alloc_id) {
345                     GlobalAlloc::Memory(alloc) => alloc,
346                     GlobalAlloc::Function(_) | GlobalAlloc::Static(_) | GlobalAlloc::VTable(..) => {
347                         unreachable!()
348                     }
349                 };
350                 let data_id = *cx.anon_allocs.entry(alloc_id).or_insert_with(|| {
351                     module
352                         .declare_anonymous_data(
353                             alloc.inner().mutability == rustc_hir::Mutability::Mut,
354                             false,
355                         )
356                         .unwrap()
357                 });
358                 (data_id, alloc, None)
359             }
360             TodoItem::Static(def_id) => {
361                 //println!("static {:?}", def_id);
362
363                 let section_name = tcx.codegen_fn_attrs(def_id).link_section;
364
365                 let alloc = tcx.eval_static_initializer(def_id).unwrap();
366
367                 let data_id = data_id_for_static(tcx, module, def_id, true);
368                 (data_id, alloc, section_name)
369             }
370         };
371
372         //("data_id {}", data_id);
373         if cx.done.contains(&data_id) {
374             continue;
375         }
376
377         let mut data_ctx = DataContext::new();
378         let alloc = alloc.inner();
379         data_ctx.set_align(alloc.align.bytes());
380
381         if let Some(section_name) = section_name {
382             let (segment_name, section_name) = if tcx.sess.target.is_like_osx {
383                 let section_name = section_name.as_str();
384                 if let Some(names) = section_name.split_once(',') {
385                     names
386                 } else {
387                     tcx.sess.fatal(&format!(
388                         "#[link_section = \"{}\"] is not valid for macos target: must be segment and section separated by comma",
389                         section_name
390                     ));
391                 }
392             } else {
393                 ("", section_name.as_str())
394             };
395             data_ctx.set_segment_section(segment_name, section_name);
396         }
397
398         let bytes = alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
399         data_ctx.define(bytes.into_boxed_slice());
400
401         for &(offset, alloc_id) in alloc.provenance().iter() {
402             let addend = {
403                 let endianness = tcx.data_layout.endian;
404                 let offset = offset.bytes() as usize;
405                 let ptr_size = tcx.data_layout.pointer_size;
406                 let bytes = &alloc.inspect_with_uninit_and_ptr_outside_interpreter(
407                     offset..offset + ptr_size.bytes() as usize,
408                 );
409                 read_target_uint(endianness, bytes).unwrap()
410             };
411
412             let reloc_target_alloc = tcx.global_alloc(alloc_id);
413             let data_id = match reloc_target_alloc {
414                 GlobalAlloc::Function(instance) => {
415                     assert_eq!(addend, 0);
416                     let func_id =
417                         crate::abi::import_function(tcx, module, instance.polymorphize(tcx));
418                     let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
419                     data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
420                     continue;
421                 }
422                 GlobalAlloc::Memory(target_alloc) => {
423                     data_id_for_alloc_id(cx, module, alloc_id, target_alloc.inner().mutability)
424                 }
425                 GlobalAlloc::VTable(ty, trait_ref) => {
426                     let alloc_id = tcx.vtable_allocation((ty, trait_ref));
427                     data_id_for_alloc_id(cx, module, alloc_id, Mutability::Not)
428                 }
429                 GlobalAlloc::Static(def_id) => {
430                     if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL)
431                     {
432                         tcx.sess.fatal(&format!(
433                             "Allocation {:?} contains reference to TLS value {:?}",
434                             alloc, def_id
435                         ));
436                     }
437
438                     // Don't push a `TodoItem::Static` here, as it will cause statics used by
439                     // multiple crates to be duplicated between them. It isn't necessary anyway,
440                     // as it will get pushed by `codegen_static` when necessary.
441                     data_id_for_static(tcx, module, def_id, false)
442                 }
443             };
444
445             let global_value = module.declare_data_in_data(data_id, &mut data_ctx);
446             data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
447         }
448
449         module.define_data(data_id, &data_ctx).unwrap();
450         cx.done.insert(data_id);
451     }
452
453     assert!(cx.todo.is_empty(), "{:?}", cx.todo);
454 }
455
456 pub(crate) fn mir_operand_get_const_val<'tcx>(
457     fx: &FunctionCx<'_, '_, 'tcx>,
458     operand: &Operand<'tcx>,
459 ) -> Option<ConstValue<'tcx>> {
460     match operand {
461         Operand::Constant(const_) => match fx.monomorphize(const_.literal) {
462             ConstantKind::Ty(const_) => Some(
463                 const_.eval_for_mir(fx.tcx, ParamEnv::reveal_all()).try_to_value(fx.tcx).unwrap(),
464             ),
465             ConstantKind::Val(val, _) => Some(val),
466             ConstantKind::Unevaluated(uv, _) => {
467                 Some(fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), uv, None).unwrap())
468             }
469         },
470         // FIXME(rust-lang/rust#85105): Casts like `IMM8 as u32` result in the const being stored
471         // inside a temporary before being passed to the intrinsic requiring the const argument.
472         // This code tries to find a single constant defining definition of the referenced local.
473         Operand::Copy(place) | Operand::Move(place) => {
474             if !place.projection.is_empty() {
475                 return None;
476             }
477             let mut computed_const_val = None;
478             for bb_data in fx.mir.basic_blocks.iter() {
479                 for stmt in &bb_data.statements {
480                     match &stmt.kind {
481                         StatementKind::Assign(local_and_rvalue) if &local_and_rvalue.0 == place => {
482                             match &local_and_rvalue.1 {
483                                 Rvalue::Cast(
484                                     CastKind::IntToInt
485                                     | CastKind::FloatToFloat
486                                     | CastKind::FloatToInt
487                                     | CastKind::IntToFloat
488                                     | CastKind::FnPtrToPtr
489                                     | CastKind::PtrToPtr,
490                                     operand,
491                                     ty,
492                                 ) => {
493                                     if computed_const_val.is_some() {
494                                         return None; // local assigned twice
495                                     }
496                                     if !matches!(ty.kind(), ty::Uint(_) | ty::Int(_)) {
497                                         return None;
498                                     }
499                                     let const_val = mir_operand_get_const_val(fx, operand)?;
500                                     if fx.layout_of(*ty).size
501                                         != const_val.try_to_scalar_int()?.size()
502                                     {
503                                         return None;
504                                     }
505                                     computed_const_val = Some(const_val);
506                                 }
507                                 Rvalue::Use(operand) => {
508                                     computed_const_val = mir_operand_get_const_val(fx, operand)
509                                 }
510                                 _ => return None,
511                             }
512                         }
513                         StatementKind::SetDiscriminant { place: stmt_place, variant_index: _ }
514                             if &**stmt_place == place =>
515                         {
516                             return None;
517                         }
518                         StatementKind::Intrinsic(ref intrinsic) => match **intrinsic {
519                             NonDivergingIntrinsic::CopyNonOverlapping(..) => return None,
520                             NonDivergingIntrinsic::Assume(..) => {}
521                         },
522                         // conservative handling
523                         StatementKind::Assign(_)
524                         | StatementKind::FakeRead(_)
525                         | StatementKind::SetDiscriminant { .. }
526                         | StatementKind::Deinit(_)
527                         | StatementKind::StorageLive(_)
528                         | StatementKind::StorageDead(_)
529                         | StatementKind::Retag(_, _)
530                         | StatementKind::AscribeUserType(_, _)
531                         | StatementKind::Coverage(_)
532                         | StatementKind::Nop => {}
533                     }
534                 }
535                 match &bb_data.terminator().kind {
536                     TerminatorKind::Goto { .. }
537                     | TerminatorKind::SwitchInt { .. }
538                     | TerminatorKind::Resume
539                     | TerminatorKind::Abort
540                     | TerminatorKind::Return
541                     | TerminatorKind::Unreachable
542                     | TerminatorKind::Drop { .. }
543                     | TerminatorKind::Assert { .. } => {}
544                     TerminatorKind::DropAndReplace { .. }
545                     | TerminatorKind::Yield { .. }
546                     | TerminatorKind::GeneratorDrop
547                     | TerminatorKind::FalseEdge { .. }
548                     | TerminatorKind::FalseUnwind { .. } => unreachable!(),
549                     TerminatorKind::InlineAsm { .. } => return None,
550                     TerminatorKind::Call { destination, target: Some(_), .. }
551                         if destination == place =>
552                     {
553                         return None;
554                     }
555                     TerminatorKind::Call { .. } => {}
556                 }
557             }
558             computed_const_val
559         }
560     }
561 }