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