]> git.lizzy.rs Git - rust.git/blob - src/constant.rs
Implement weak linkage for statics
[rust.git] / src / constant.rs
1 use std::borrow::Cow;
2
3 use rustc::mir::interpret::{
4     read_target_uint, AllocId, AllocKind, Allocation, ConstValue, EvalResult, GlobalId, Scalar,
5 };
6 use rustc::ty::{Const, LazyConst};
7 use rustc_mir::interpret::{
8     EvalContext, ImmTy, MPlaceTy, Machine, Memory, MemoryKind, OpTy, PlaceTy, Pointer,
9     StackPopCleanup,
10 };
11
12 use cranelift_module::*;
13
14 use crate::prelude::*;
15
16 #[derive(Default)]
17 pub struct ConstantCx {
18     todo: HashSet<TodoItem>,
19     done: HashSet<DataId>,
20 }
21
22 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
23 enum TodoItem {
24     Alloc(AllocId),
25     Static(DefId),
26 }
27
28 impl ConstantCx {
29     pub fn finalize<'a, 'tcx: 'a, B: Backend>(
30         mut self,
31         tcx: TyCtxt<'a, 'tcx, 'tcx>,
32         module: &mut Module<B>,
33     ) {
34         //println!("todo {:?}", self.todo);
35         define_all_allocs(tcx, module, &mut self);
36         //println!("done {:?}", self.done);
37         self.done.clear();
38     }
39 }
40
41 pub fn codegen_static<'a, 'tcx: 'a>(ccx: &mut ConstantCx, def_id: DefId) {
42     ccx.todo.insert(TodoItem::Static(def_id));
43 }
44
45 pub fn codegen_static_ref<'a, 'tcx: 'a>(
46     fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
47     static_: &Static<'tcx>,
48 ) -> CPlace<'tcx> {
49     let linkage = crate::linkage::get_static_ref_linkage(fx.tcx, static_.def_id);
50     let data_id = data_id_for_static(fx.tcx, fx.module, static_.def_id, linkage);
51     cplace_for_dataid(fx, static_.ty, data_id)
52 }
53
54 pub fn trans_promoted<'a, 'tcx: 'a>(
55     fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
56     promoted: Promoted,
57     dest_ty: Ty<'tcx>,
58 ) -> CPlace<'tcx> {
59     match fx
60         .tcx
61         .const_eval(ParamEnv::reveal_all().and(GlobalId {
62             instance: fx.instance,
63             promoted: Some(promoted),
64         }))
65     {
66         Ok(const_) => {
67             let cplace = trans_const_place(fx, const_);
68             debug_assert_eq!(cplace.layout(), fx.layout_of(dest_ty));
69             cplace
70         }
71         Err(_) => crate::trap::trap_unreachable_ret_place(fx, fx.layout_of(dest_ty)),
72     }
73 }
74
75 pub fn trans_constant<'a, 'tcx: 'a>(
76     fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
77     constant: &Constant<'tcx>,
78 ) -> CValue<'tcx> {
79     let const_ = fx.monomorphize(&constant.literal);
80     let const_ = force_eval_const(fx, const_);
81     trans_const_value(fx, const_)
82 }
83
84 pub fn force_eval_const<'a, 'tcx: 'a>(
85     fx: &FunctionCx<'a, 'tcx, impl Backend>,
86     const_: &'tcx LazyConst<'tcx>,
87 ) -> Const<'tcx> {
88     match *const_ {
89         LazyConst::Unevaluated(def_id, ref substs) => {
90             let param_env = ParamEnv::reveal_all();
91             let instance = Instance::resolve(fx.tcx, param_env, def_id, substs).unwrap();
92             let cid = GlobalId {
93                 instance,
94                 promoted: None,
95             };
96             fx.tcx.const_eval(param_env.and(cid)).unwrap()
97         }
98         LazyConst::Evaluated(const_) => const_,
99     }
100 }
101
102 fn trans_const_value<'a, 'tcx: 'a>(
103     fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
104     const_: Const<'tcx>,
105 ) -> CValue<'tcx> {
106     let ty = fx.monomorphize(&const_.ty);
107     let layout = fx.layout_of(ty);
108     match ty.sty {
109         ty::Bool => {
110             let bits = const_.val.try_to_bits(layout.size).unwrap();
111             CValue::const_val(fx, ty, bits as u64 as i64)
112         }
113         ty::Uint(_) => {
114             let bits = const_.val.try_to_bits(layout.size).unwrap();
115             CValue::const_val(fx, ty, bits as u64 as i64)
116         }
117         ty::Int(_) => {
118             let bits = const_.val.try_to_bits(layout.size).unwrap();
119             CValue::const_val(fx, ty, bits as i128 as i64)
120         }
121         ty::FnDef(_def_id, _substs) => CValue::ByRef(
122             fx.bcx
123                 .ins()
124                 .iconst(fx.pointer_type, fx.pointer_type.bytes() as i64),
125             layout,
126         ),
127         _ => trans_const_place(fx, const_).to_cvalue(fx),
128     }
129 }
130
131 fn trans_const_place<'a, 'tcx: 'a>(
132     fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
133     const_: Const<'tcx>,
134 ) -> CPlace<'tcx> {
135     // Adapted from https://github.com/rust-lang/rust/pull/53671/files#diff-e0b58bb6712edaa8595ad7237542c958L551
136     let result = || -> EvalResult<'tcx, &'tcx Allocation> {
137         let mut ecx = EvalContext::new(
138             fx.tcx.at(DUMMY_SP),
139             ty::ParamEnv::reveal_all(),
140             TransPlaceInterpreter,
141         );
142         ecx.push_stack_frame(
143             fx.instance,
144             DUMMY_SP,
145             fx.mir,
146             None,
147             StackPopCleanup::None { cleanup: false },
148         )
149         .unwrap();
150         let op = ecx.eval_operand(
151             &Operand::Constant(Box::new(Constant {
152                 span: DUMMY_SP,
153                 ty: const_.ty,
154                 user_ty: None,
155                 literal: fx.tcx.mk_lazy_const(LazyConst::Evaluated(const_)),
156             })),
157             None,
158         )?;
159         let ptr = ecx.allocate(op.layout, MemoryKind::Stack);
160         ecx.copy_op(op, ptr.into())?;
161         let alloc = ecx.memory().get(ptr.to_ptr()?.alloc_id)?;
162         Ok(fx.tcx.intern_const_alloc(alloc.clone()))
163     };
164     let alloc = result().expect("unable to convert ConstValue to Allocation");
165
166     //println!("const value: {:?} allocation: {:?}", value, alloc);
167     let alloc_id = fx.tcx.alloc_map.lock().allocate(alloc);
168     fx.constants.todo.insert(TodoItem::Alloc(alloc_id));
169     let data_id = data_id_for_alloc_id(fx.module, alloc_id);
170     cplace_for_dataid(fx, const_.ty, data_id)
171 }
172
173 fn data_id_for_alloc_id<B: Backend>(module: &mut Module<B>, alloc_id: AllocId) -> DataId {
174     module
175         .declare_data(&format!("__alloc_{}", alloc_id.0), Linkage::Local, false)
176         .unwrap()
177 }
178
179 fn data_id_for_static<'a, 'tcx: 'a, B: Backend>(
180     tcx: TyCtxt<'a, 'tcx, 'tcx>,
181     module: &mut Module<B>,
182     def_id: DefId,
183     linkage: Linkage,
184 ) -> DataId {
185     let symbol_name = tcx.symbol_name(Instance::mono(tcx, def_id)).as_str();
186     let is_mutable = if let ::rustc::hir::Mutability::MutMutable = tcx.is_static(def_id).unwrap() {
187         true
188     } else {
189         !tcx.type_of(def_id)
190             .is_freeze(tcx, ParamEnv::reveal_all(), DUMMY_SP)
191     };
192
193     let data_id = module
194         .declare_data(&*symbol_name, linkage, is_mutable)
195         .unwrap();
196
197     if linkage == Linkage::Preemptible {
198         if let ty::RawPtr(_) = tcx.type_of(def_id).sty {
199         } else {
200             tcx.sess.span_fatal(tcx.def_span(def_id), "must have type `*const T` or `*mut T`")
201         }
202
203         let mut data_ctx = DataContext::new();
204         let zero_bytes = std::iter::repeat(0)
205             .take(pointer_ty(tcx).bytes() as usize)
206             .collect::<Vec<u8>>()
207             .into_boxed_slice();
208         data_ctx.define(zero_bytes);
209         match module.define_data(data_id, &data_ctx) {
210             // Everytime a weak static is referenced, there will be a zero pointer definition,
211             // so duplicate definitions are expected and allowed.
212             Err(ModuleError::DuplicateDefinition(_)) => {}
213             res => res.unwrap(),
214         }
215     }
216
217     data_id
218 }
219
220 fn cplace_for_dataid<'a, 'tcx: 'a>(
221     fx: &mut FunctionCx<'a, 'tcx, impl Backend>,
222     ty: Ty<'tcx>,
223     data_id: DataId,
224 ) -> CPlace<'tcx> {
225     let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
226     let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
227     let layout = fx.layout_of(fx.monomorphize(&ty));
228     assert!(!layout.is_unsized(), "unsized statics aren't supported");
229     CPlace::Addr(global_ptr, None, layout)
230 }
231
232 fn define_all_allocs<'a, 'tcx: 'a, B: Backend + 'a>(
233     tcx: TyCtxt<'a, 'tcx, 'tcx>,
234     module: &mut Module<B>,
235     cx: &mut ConstantCx,
236 ) {
237     let memory = Memory::<TransPlaceInterpreter>::new(tcx.at(DUMMY_SP));
238
239     while let Some(todo_item) = pop_set(&mut cx.todo) {
240         let (data_id, alloc) = match todo_item {
241             TodoItem::Alloc(alloc_id) => {
242                 //println!("alloc_id {}", alloc_id);
243                 let data_id = data_id_for_alloc_id(module, alloc_id);
244                 let alloc = memory.get(alloc_id).unwrap();
245                 (data_id, alloc)
246             }
247             TodoItem::Static(def_id) => {
248                 //println!("static {:?}", def_id);
249
250                 if tcx.is_foreign_item(def_id) {
251                     continue;
252                 }
253
254                 let instance = ty::Instance::mono(tcx, def_id);
255                 let cid = GlobalId {
256                     instance,
257                     promoted: None,
258                 };
259                 let const_ = tcx.const_eval(ParamEnv::reveal_all().and(cid)).unwrap();
260
261                 let alloc = match const_.val {
262                     ConstValue::ByRef(ptr, alloc) if ptr.offset.bytes() == 0 => alloc,
263                     _ => bug!("static const eval returned {:#?}", const_),
264                 };
265
266                 // FIXME set correct linkage
267                 let data_id = data_id_for_static(tcx, module, def_id, Linkage::Export);
268                 (data_id, alloc)
269             }
270         };
271
272         //("data_id {}", data_id);
273         if cx.done.contains(&data_id) {
274             continue;
275         }
276
277         let mut data_ctx = DataContext::new();
278
279         data_ctx.define(alloc.bytes.to_vec().into_boxed_slice());
280
281         for &(offset, (_tag, reloc)) in alloc.relocations.iter() {
282             let addend = {
283                 let endianness = tcx.data_layout.endian;
284                 let offset = offset.bytes() as usize;
285                 let ptr_size = tcx.data_layout.pointer_size;
286                 let bytes = &alloc.bytes[offset..offset + ptr_size.bytes() as usize];
287                 read_target_uint(endianness, bytes).unwrap()
288             };
289
290             let data_id = match tcx.alloc_map.lock().get(reloc).unwrap() {
291                 AllocKind::Function(instance) => {
292                     assert_eq!(addend, 0);
293                     let func_id = crate::abi::import_function(tcx, module, instance);
294                     let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
295                     data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
296                     continue;
297                 }
298                 AllocKind::Memory(_) => {
299                     cx.todo.insert(TodoItem::Alloc(reloc));
300                     data_id_for_alloc_id(module, reloc)
301                 }
302                 AllocKind::Static(def_id) => {
303                     cx.todo.insert(TodoItem::Static(def_id));
304                     let linkage = crate::linkage::get_static_ref_linkage(tcx, def_id);
305                     data_id_for_static(tcx, module, def_id, linkage)
306                 }
307             };
308
309             let global_value = module.declare_data_in_data(data_id, &mut data_ctx);
310             data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
311         }
312
313         module.define_data(data_id, &data_ctx).unwrap();
314         cx.done.insert(data_id);
315     }
316
317     assert!(cx.todo.is_empty(), "{:?}", cx.todo);
318 }
319
320 fn pop_set<T: Copy + Eq + ::std::hash::Hash>(set: &mut HashSet<T>) -> Option<T> {
321     if let Some(elem) = set.iter().next().map(|elem| *elem) {
322         set.remove(&elem);
323         Some(elem)
324     } else {
325         None
326     }
327 }
328
329 struct TransPlaceInterpreter;
330
331 impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for TransPlaceInterpreter {
332     type MemoryKinds = !;
333     type PointerTag = ();
334     type AllocExtra = ();
335     type MemoryExtra = ();
336     type FrameExtra = ();
337     type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation<()>)>;
338     const STATIC_KIND: Option<!> = None;
339
340     fn enforce_validity(_: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool {
341         false
342     }
343
344     fn before_terminator(_: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx> {
345         panic!();
346     }
347
348     fn find_fn(
349         _: &mut EvalContext<'a, 'mir, 'tcx, Self>,
350         _: Instance<'tcx>,
351         _: &[OpTy<'tcx>],
352         _: Option<PlaceTy<'tcx>>,
353         _: Option<BasicBlock>,
354     ) -> EvalResult<'tcx, Option<&'mir Mir<'tcx>>> {
355         panic!();
356     }
357
358     fn call_intrinsic(
359         _: &mut EvalContext<'a, 'mir, 'tcx, Self>,
360         _: Instance<'tcx>,
361         _: &[OpTy<'tcx>],
362         _: PlaceTy<'tcx>,
363     ) -> EvalResult<'tcx> {
364         panic!();
365     }
366
367     fn find_foreign_static(
368         _: DefId,
369         _: ::rustc::ty::query::TyCtxtAt<'a, 'tcx, 'tcx>,
370         _: &(),
371     ) -> EvalResult<'tcx, Cow<'tcx, Allocation>> {
372         panic!();
373     }
374
375     fn ptr_op(
376         _: &EvalContext<'a, 'mir, 'tcx, Self>,
377         _: mir::BinOp,
378         _: ImmTy<'tcx>,
379         _: ImmTy<'tcx>,
380     ) -> EvalResult<'tcx, (Scalar, bool)> {
381         panic!();
382     }
383
384     fn box_alloc(_: &mut EvalContext<'a, 'mir, 'tcx, Self>, _: PlaceTy<'tcx>) -> EvalResult<'tcx> {
385         panic!();
386     }
387
388     fn tag_dereference(
389         _: &EvalContext<'a, 'mir, 'tcx, Self>,
390         _: MPlaceTy<'tcx>,
391         _: Option<::rustc::hir::Mutability>,
392     ) -> EvalResult<'tcx, Scalar> {
393         panic!();
394     }
395
396     fn adjust_static_allocation<'alloc>(
397         alloc: &'alloc Allocation,
398         _: &(),
399     ) -> Cow<'alloc, Allocation> {
400         Cow::Borrowed(alloc)
401     }
402
403     fn tag_new_allocation(
404         _: &mut EvalContext<'a, 'mir, 'tcx, Self>,
405         ptr: Pointer,
406         _: MemoryKind<!>,
407     ) -> Pointer {
408         ptr
409     }
410
411     fn stack_push(_: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx> {
412         Ok(())
413     }
414
415     fn stack_pop(_: &mut EvalContext<'a, 'mir, 'tcx, Self>, _: ()) -> EvalResult<'tcx> {
416         Ok(())
417     }
418 }