]> git.lizzy.rs Git - rust.git/blob - src/constant.rs
efc7d8240840070996eb0a5edbab5ac6b59e4283
[rust.git] / src / constant.rs
1 use rustc_span::DUMMY_SP;
2
3 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
4 use rustc_middle::mir::interpret::{
5     read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, Pointer, Scalar,
6 };
7 use rustc_middle::ty::{Const, ConstKind};
8 use rustc_target::abi::Align;
9 use rustc_data_structures::fx::FxHashSet;
10
11 use cranelift_codegen::ir::GlobalValue;
12 use cranelift_module::*;
13
14 use crate::prelude::*;
15
16 #[derive(Default)]
17 pub(crate) struct ConstantCx {
18     todo: Vec<TodoItem>,
19     done: FxHashSet<DataId>,
20 }
21
22 #[derive(Copy, Clone, Debug)]
23 enum TodoItem {
24     Alloc(AllocId),
25     Static(DefId),
26 }
27
28 impl ConstantCx {
29     pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut Module<impl Backend>) {
30         //println!("todo {:?}", self.todo);
31         define_all_allocs(tcx, module, &mut self);
32         //println!("done {:?}", self.done);
33         self.done.clear();
34     }
35 }
36
37 pub(crate) fn codegen_static(constants_cx: &mut ConstantCx, def_id: DefId) {
38     constants_cx.todo.push(TodoItem::Static(def_id));
39 }
40
41 fn codegen_static_ref<'tcx>(
42     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
43     def_id: DefId,
44     layout: TyAndLayout<'tcx>,
45 ) -> CPlace<'tcx> {
46     let linkage = crate::linkage::get_static_ref_linkage(fx.tcx, def_id);
47     let data_id = data_id_for_static(fx.tcx, fx.module, def_id, linkage);
48     let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
49     #[cfg(debug_assertions)]
50     fx.add_comment(local_data_id, format!("{:?}", def_id));
51     cplace_for_dataid(fx, layout, local_data_id)
52 }
53
54 pub(crate) fn trans_constant<'tcx>(
55     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
56     constant: &Constant<'tcx>,
57 ) -> CValue<'tcx> {
58     let const_ = match constant.literal.val {
59         ConstKind::Unevaluated(def_id, ref substs, promoted) if fx.tcx.is_static(def_id) => {
60             assert!(substs.is_empty());
61             assert!(promoted.is_none());
62
63             return codegen_static_ref(
64                 fx,
65                 def_id,
66                 fx.layout_of(fx.monomorphize(&constant.literal.ty)),
67             ).to_cvalue(fx);
68         }
69         _ => fx.monomorphize(&constant.literal).eval(fx.tcx, ParamEnv::reveal_all()),
70     };
71
72     trans_const_value(fx, const_)
73 }
74
75 pub(crate) fn trans_const_value<'tcx>(
76     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
77     const_: &'tcx Const<'tcx>,
78 ) -> CValue<'tcx> {
79     let ty = fx.monomorphize(&const_.ty);
80     let layout = fx.layout_of(ty);
81     assert!(!layout.is_unsized(), "sized const value");
82
83     if layout.is_zst() {
84         return CValue::by_ref(
85             crate::Pointer::const_addr(fx, i64::try_from(layout.align.pref.bytes()).unwrap()),
86             layout,
87         );
88     }
89     let const_val = match const_.val {
90         ConstKind::Value(const_val) => const_val,
91         _ => unreachable!("Const {:?} should have been evaluated", const_),
92     };
93
94     match const_val {
95         ConstValue::Scalar(x) => {
96             if fx.clif_type(layout.ty).is_none() {
97                 let (size, align) = (layout.size, layout.align.pref);
98                 let mut alloc = Allocation::from_bytes(
99                     std::iter::repeat(0).take(size.bytes_usize()).collect::<Vec<u8>>(),
100                     align,
101                 );
102                 let ptr = Pointer::new(AllocId(!0), Size::ZERO); // The alloc id is never used
103                 alloc.write_scalar(fx, ptr, x.into(), size).unwrap();
104                 let alloc = fx.tcx.intern_const_alloc(alloc);
105                 return CValue::by_ref(pointer_for_allocation(fx, alloc), layout);
106             }
107
108             match x {
109                 Scalar::Raw { data, size } => {
110                     assert_eq!(u64::from(size), layout.size.bytes());
111                     return CValue::const_val(fx, layout, data);
112                 }
113                 Scalar::Ptr(ptr) => {
114                     let alloc_kind = fx.tcx.get_global_alloc(ptr.alloc_id);
115                     let base_addr = match alloc_kind {
116                         Some(GlobalAlloc::Memory(alloc)) => {
117                             fx.constants_cx.todo.push(TodoItem::Alloc(ptr.alloc_id));
118                             let data_id = data_id_for_alloc_id(fx.module, ptr.alloc_id, alloc.align);
119                             let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
120                             #[cfg(debug_assertions)]
121                             fx.add_comment(local_data_id, format!("{:?}", ptr.alloc_id));
122                             fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
123                         }
124                         Some(GlobalAlloc::Function(instance)) => {
125                             let func_id = crate::abi::import_function(fx.tcx, fx.module, instance);
126                             let local_func_id = fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
127                             fx.bcx.ins().func_addr(fx.pointer_type, local_func_id)
128                         }
129                         Some(GlobalAlloc::Static(def_id)) => {
130                             assert!(fx.tcx.is_static(def_id));
131                             let linkage = crate::linkage::get_static_ref_linkage(fx.tcx, def_id);
132                             let data_id = data_id_for_static(fx.tcx, fx.module, def_id, linkage);
133                             let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
134                             #[cfg(debug_assertions)]
135                             fx.add_comment(local_data_id, format!("{:?}", def_id));
136                             fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
137                         }
138                         None => bug!("missing allocation {:?}", ptr.alloc_id),
139                     };
140                     let val = fx.bcx.ins().iadd_imm(base_addr, i64::try_from(ptr.offset.bytes()).unwrap());
141                     return CValue::by_val(val, layout);
142                 }
143             }
144         }
145         ConstValue::ByRef { alloc, offset } => {
146             CValue::by_ref(
147                 pointer_for_allocation(fx, alloc)
148                     .offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
149                 layout,
150             )
151         }
152         ConstValue::Slice { data, start, end } => {
153             let ptr = pointer_for_allocation(fx, data)
154                 .offset_i64(fx, i64::try_from(start).unwrap())
155                 .get_addr(fx);
156             let len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(end.checked_sub(start).unwrap()).unwrap());
157             CValue::by_val_pair(ptr, len, layout)
158         }
159     }
160 }
161
162 fn pointer_for_allocation<'tcx>(
163     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
164     alloc: &'tcx Allocation,
165 ) -> crate::pointer::Pointer {
166     let alloc_id = fx.tcx.create_memory_alloc(alloc);
167     fx.constants_cx.todo.push(TodoItem::Alloc(alloc_id));
168     let data_id = data_id_for_alloc_id(fx.module, alloc_id, alloc.align);
169
170     let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
171     #[cfg(debug_assertions)]
172     fx.add_comment(local_data_id, format!("{:?}", alloc_id));
173     let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
174     crate::pointer::Pointer::new(global_ptr)
175 }
176
177 fn data_id_for_alloc_id<B: Backend>(
178     module: &mut Module<B>,
179     alloc_id: AllocId,
180     align: Align,
181 ) -> DataId {
182     module
183         .declare_data(
184             &format!("__alloc_{}", alloc_id.0),
185             Linkage::Local,
186             false,
187             false,
188             Some(align.bytes() as u8),
189         )
190         .unwrap()
191 }
192
193 fn data_id_for_static(
194     tcx: TyCtxt<'_>,
195     module: &mut Module<impl Backend>,
196     def_id: DefId,
197     linkage: Linkage,
198 ) -> DataId {
199     let instance = Instance::mono(tcx, def_id);
200     let symbol_name = tcx.symbol_name(instance).name.as_str();
201     let ty = instance.monomorphic_ty(tcx);
202     let is_mutable = if tcx.is_mutable_static(def_id) {
203         true
204     } else {
205         !ty.is_freeze(tcx, ParamEnv::reveal_all(), DUMMY_SP)
206     };
207     let align = tcx
208         .layout_of(ParamEnv::reveal_all().and(ty))
209         .unwrap()
210         .align
211         .pref
212         .bytes();
213
214     let attrs = tcx.codegen_fn_attrs(def_id);
215
216     let data_id = module
217         .declare_data(
218             &*symbol_name,
219             linkage,
220             is_mutable,
221             attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL),
222             Some(align.try_into().unwrap()),
223         )
224         .unwrap();
225
226     if linkage == Linkage::Preemptible {
227         if let ty::RawPtr(_) = ty.kind {
228         } else {
229             tcx.sess.span_fatal(
230                 tcx.def_span(def_id),
231                 "must have type `*const T` or `*mut T` due to `#[linkage]` attribute",
232             )
233         }
234
235         let mut data_ctx = DataContext::new();
236         data_ctx.define_zeroinit(pointer_ty(tcx).bytes() as usize);
237         match module.define_data(data_id, &data_ctx) {
238             // Everytime a weak static is referenced, there will be a zero pointer definition,
239             // so duplicate definitions are expected and allowed.
240             Err(ModuleError::DuplicateDefinition(_)) => {}
241             res => res.unwrap(),
242         }
243     }
244
245     data_id
246 }
247
248 fn cplace_for_dataid<'tcx>(
249     fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
250     layout: TyAndLayout<'tcx>,
251     local_data_id: GlobalValue,
252 ) -> CPlace<'tcx> {
253     let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
254     assert!(!layout.is_unsized(), "unsized statics aren't supported");
255     CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout)
256 }
257
258 fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut Module<impl Backend>, cx: &mut ConstantCx) {
259     while let Some(todo_item) = cx.todo.pop() {
260         let (data_id, alloc) = match todo_item {
261             TodoItem::Alloc(alloc_id) => {
262                 //println!("alloc_id {}", alloc_id);
263                 let alloc = match tcx.get_global_alloc(alloc_id).unwrap() {
264                     GlobalAlloc::Memory(alloc) => alloc,
265                     GlobalAlloc::Function(_) | GlobalAlloc::Static(_) => unreachable!(),
266                 };
267                 let data_id = data_id_for_alloc_id(module, alloc_id, alloc.align);
268                 (data_id, alloc)
269             }
270             TodoItem::Static(def_id) => {
271                 //println!("static {:?}", def_id);
272
273                 if tcx.is_foreign_item(def_id) {
274                     continue;
275                 }
276
277                 let const_ = tcx.const_eval_poly(def_id).unwrap();
278
279                 let alloc = match const_ {
280                     ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => alloc,
281                     _ => bug!("static const eval returned {:#?}", const_),
282                 };
283
284                 let data_id = data_id_for_static(
285                     tcx,
286                     module,
287                     def_id,
288                     if tcx.is_reachable_non_generic(def_id) {
289                         Linkage::Export
290                     } else {
291                         Linkage::Export // FIXME Set hidden visibility
292                     },
293                 );
294                 (data_id, alloc)
295             }
296         };
297
298         //("data_id {}", data_id);
299         if cx.done.contains(&data_id) {
300             continue;
301         }
302
303         let mut data_ctx = DataContext::new();
304
305         let bytes = alloc.inspect_with_undef_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
306         data_ctx.define(bytes.into_boxed_slice());
307
308         for &(offset, (_tag, reloc)) in alloc.relocations().iter() {
309             let addend = {
310                 let endianness = tcx.data_layout.endian;
311                 let offset = offset.bytes() as usize;
312                 let ptr_size = tcx.data_layout.pointer_size;
313                 let bytes = &alloc.inspect_with_undef_and_ptr_outside_interpreter(offset..offset + ptr_size.bytes() as usize);
314                 read_target_uint(endianness, bytes).unwrap()
315             };
316
317             let reloc_target_alloc = tcx.get_global_alloc(reloc).unwrap();
318             let data_id = match reloc_target_alloc {
319                 GlobalAlloc::Function(instance) => {
320                     assert_eq!(addend, 0);
321                     let func_id = crate::abi::import_function(tcx, module, instance);
322                     let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
323                     data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
324                     continue;
325                 }
326                 GlobalAlloc::Memory(_) => {
327                     cx.todo.push(TodoItem::Alloc(reloc));
328                     data_id_for_alloc_id(module, reloc, alloc.align)
329                 }
330                 GlobalAlloc::Static(def_id) => {
331                     if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
332                         tcx.sess.fatal(&format!("Allocation {:?} contains reference to TLS value {:?}", alloc, def_id));
333                     }
334
335                     // Don't push a `TodoItem::Static` here, as it will cause statics used by
336                     // multiple crates to be duplicated between them. It isn't necessary anyway,
337                     // as it will get pushed by `codegen_static` when necessary.
338                     data_id_for_static(
339                         tcx,
340                         module,
341                         def_id,
342                         crate::linkage::get_static_ref_linkage(tcx, def_id),
343                     )
344                 }
345             };
346
347             let global_value = module.declare_data_in_data(data_id, &mut data_ctx);
348             data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
349         }
350
351         module.define_data(data_id, &data_ctx).unwrap();
352         cx.done.insert(data_id);
353     }
354
355     assert!(cx.todo.is_empty(), "{:?}", cx.todo);
356 }
357
358 pub(crate) fn mir_operand_get_const_val<'tcx>(
359     fx: &FunctionCx<'_, 'tcx, impl Backend>,
360     operand: &Operand<'tcx>,
361 ) -> Option<&'tcx Const<'tcx>> {
362     match operand {
363         Operand::Copy(_) | Operand::Move(_) => None,
364         Operand::Constant(const_) => {
365             Some(fx.monomorphize(&const_.literal).eval(fx.tcx, ParamEnv::reveal_all()))
366         }
367     }
368 }