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