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