3 use rustc_span::DUMMY_SP;
5 use rustc::mir::interpret::{
6 read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, InterpResult, Scalar,
8 use rustc::ty::{layout::Align, Const, ConstKind};
9 use rustc_mir::interpret::{
10 ImmTy, InterpCx, Machine, Memory, MemoryKind, OpTy, PanicInfo, PlaceTy, Pointer,
11 StackPopCleanup, StackPopInfo,
14 use cranelift_module::*;
16 use crate::prelude::*;
19 pub struct ConstantCx {
20 todo: HashSet<TodoItem>,
21 done: HashSet<DataId>,
24 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
31 pub fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut Module<impl Backend>) {
32 //println!("todo {:?}", self.todo);
33 define_all_allocs(tcx, module, &mut self);
34 //println!("done {:?}", self.done);
39 pub fn codegen_static(constants_cx: &mut ConstantCx, def_id: DefId) {
40 constants_cx.todo.insert(TodoItem::Static(def_id));
43 fn codegen_static_ref<'tcx>(
44 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
48 let linkage = crate::linkage::get_static_ref_linkage(fx.tcx, def_id);
49 let data_id = data_id_for_static(fx.tcx, fx.module, def_id, linkage);
50 cplace_for_dataid(fx, ty, data_id)
53 pub fn trans_constant<'tcx>(
54 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
55 constant: &Constant<'tcx>,
57 let const_ = match constant.literal.val {
58 ConstKind::Unevaluated(def_id, ref substs, promoted) if fx.tcx.is_static(def_id) => {
59 assert!(substs.is_empty());
60 assert!(promoted.is_none());
62 return codegen_static_ref(
65 fx.monomorphize(&constant.literal.ty),
68 ConstKind::Unevaluated(def_id, ref substs, promoted) => {
69 let substs = fx.monomorphize(substs);
70 fx.tcx.const_eval_resolve(
71 ParamEnv::reveal_all(),
75 None, // FIXME use correct span
76 ).unwrap_or_else(|_| {
77 fx.tcx.sess.abort_if_errors();
81 _ => fx.monomorphize(&constant.literal),
84 trans_const_value(fx, const_)
87 pub fn force_eval_const<'tcx>(
88 fx: &FunctionCx<'_, 'tcx, impl Backend>,
90 ) -> &'tcx Const<'tcx> {
92 ConstKind::Unevaluated(def_id, ref substs, promoted) => {
93 let substs = fx.monomorphize(substs);
94 fx.tcx.const_eval_resolve(
95 ParamEnv::reveal_all(),
99 None, // FIXME pass correct span
100 ).unwrap_or_else(|_| {
101 fx.tcx.sess.abort_if_errors();
105 _ => fx.monomorphize(&const_),
109 pub fn trans_const_value<'tcx>(
110 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
111 const_: &'tcx Const<'tcx>,
113 let ty = fx.monomorphize(&const_.ty);
114 let layout = fx.layout_of(ty);
117 return CValue::by_ref(
118 crate::Pointer::const_addr(fx, i64::try_from(layout.align.pref.bytes()).unwrap()),
123 let const_val = match const_.val {
124 ConstKind::Value(const_val) => const_val,
125 _ => unreachable!("Const {:?} should have been evaluated", const_),
129 ConstValue::Scalar(x) => {
130 let scalar = match layout.abi {
131 layout::Abi::Scalar(ref x) => x,
132 _ => bug!("from_const: invalid ByVal layout: {:#?}", layout),
136 ty::Bool | ty::Uint(_) => {
137 let bits = const_.val.try_to_bits(layout.size).unwrap_or_else(|| {
138 panic!("{:?}\n{:?}", const_, layout);
140 CValue::const_val(fx, ty, bits)
143 let bits = const_.val.try_to_bits(layout.size).unwrap();
147 rustc::mir::interpret::sign_extend(bits, layout.size),
151 let bits = const_.val.try_to_bits(layout.size).unwrap();
152 let val = match fty {
156 .f32const(Ieee32::with_bits(u32::try_from(bits).unwrap())),
160 .f64const(Ieee64::with_bits(u64::try_from(bits).unwrap())),
162 CValue::by_val(val, layout)
164 ty::FnDef(_def_id, _substs) => CValue::by_ref(
165 crate::pointer::Pointer::const_addr(fx, fx.pointer_type.bytes() as i64),
168 _ => trans_const_place(fx, const_).to_cvalue(fx),
171 ConstValue::ByRef { alloc, offset } => {
172 let alloc_id = fx.tcx.alloc_map.lock().create_memory_alloc(alloc);
173 fx.constants_cx.todo.insert(TodoItem::Alloc(alloc_id));
174 let data_id = data_id_for_alloc_id(fx.module, alloc_id, alloc.align);
175 let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
176 let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
177 assert!(!layout.is_unsized(), "unsized ConstValue::ByRef not supported");
179 crate::pointer::Pointer::new(global_ptr)
180 .offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
184 ConstValue::Slice { data: _, start: _, end: _ } => {
185 trans_const_place(fx, const_).to_cvalue(fx)
190 fn trans_const_place<'tcx>(
191 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
192 const_: &'tcx Const<'tcx>,
194 // Adapted from https://github.com/rust-lang/rust/pull/53671/files#diff-e0b58bb6712edaa8595ad7237542c958L551
195 let result = || -> InterpResult<'tcx, &'tcx Allocation> {
196 let mut ecx = InterpCx::new(
198 ty::ParamEnv::reveal_all(),
199 TransPlaceInterpreter,
202 ecx.push_stack_frame(
207 StackPopCleanup::None { cleanup: false },
210 let op = ecx.eval_operand(
211 &Operand::Constant(Box::new(Constant {
218 let ptr = ecx.allocate(op.layout, MemoryKind::Stack);
219 ecx.copy_op(op, ptr.into())?;
222 .get_raw(ptr.to_ref().to_scalar()?.assert_ptr().alloc_id)?;
223 Ok(fx.tcx.intern_const_alloc(alloc.clone()))
225 let alloc = result().expect("unable to convert ConstKind to Allocation");
227 //println!("const value: {:?} allocation: {:?}", value, alloc);
228 let alloc_id = fx.tcx.alloc_map.lock().create_memory_alloc(alloc);
229 fx.constants_cx.todo.insert(TodoItem::Alloc(alloc_id));
230 let data_id = data_id_for_alloc_id(fx.module, alloc_id, alloc.align);
231 cplace_for_dataid(fx, const_.ty, data_id)
234 fn data_id_for_alloc_id<B: Backend>(
235 module: &mut Module<B>,
241 &format!("__alloc_{}", alloc_id.0),
244 Some(align.bytes() as u8),
249 fn data_id_for_static(
251 module: &mut Module<impl Backend>,
255 let instance = Instance::mono(tcx, def_id);
256 let symbol_name = tcx.symbol_name(instance).name.as_str();
257 let ty = instance.monomorphic_ty(tcx);
258 let is_mutable = if tcx.is_mutable_static(def_id) {
261 !ty.is_freeze(tcx, ParamEnv::reveal_all(), DUMMY_SP)
264 .layout_of(ParamEnv::reveal_all().and(ty))
275 Some(align.try_into().unwrap()),
279 if linkage == Linkage::Preemptible {
280 if let ty::RawPtr(_) = ty.kind {
283 tcx.def_span(def_id),
284 "must have type `*const T` or `*mut T` due to `#[linkage]` attribute",
288 let mut data_ctx = DataContext::new();
289 let zero_bytes = std::iter::repeat(0)
290 .take(pointer_ty(tcx).bytes() as usize)
291 .collect::<Vec<u8>>()
293 data_ctx.define(zero_bytes);
294 match module.define_data(data_id, &data_ctx) {
295 // Everytime a weak static is referenced, there will be a zero pointer definition,
296 // so duplicate definitions are expected and allowed.
297 Err(ModuleError::DuplicateDefinition(_)) => {}
305 fn cplace_for_dataid<'tcx>(
306 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
310 let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
311 let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
312 let layout = fx.layout_of(fx.monomorphize(&ty));
313 assert!(!layout.is_unsized(), "unsized statics aren't supported");
314 CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout)
317 fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut Module<impl Backend>, cx: &mut ConstantCx) {
318 let memory = Memory::<TransPlaceInterpreter>::new(tcx.at(DUMMY_SP), ());
320 while let Some(todo_item) = pop_set(&mut cx.todo) {
321 let (data_id, alloc) = match todo_item {
322 TodoItem::Alloc(alloc_id) => {
323 //println!("alloc_id {}", alloc_id);
324 let alloc = memory.get_raw(alloc_id).unwrap();
325 let data_id = data_id_for_alloc_id(module, alloc_id, alloc.align);
328 TodoItem::Static(def_id) => {
329 //println!("static {:?}", def_id);
331 if tcx.is_foreign_item(def_id) {
335 let const_ = tcx.const_eval_poly(def_id).unwrap();
337 let alloc = match const_.val {
338 ConstKind::Value(ConstValue::ByRef { alloc, offset }) if offset.bytes() == 0 => alloc,
339 _ => bug!("static const eval returned {:#?}", const_),
342 let data_id = data_id_for_static(
346 if tcx.is_reachable_non_generic(def_id) {
356 //("data_id {}", data_id);
357 if cx.done.contains(&data_id) {
361 let mut data_ctx = DataContext::new();
363 let bytes = alloc.inspect_with_undef_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
364 data_ctx.define(bytes.into_boxed_slice());
366 for &(offset, (_tag, reloc)) in alloc.relocations().iter() {
368 let endianness = tcx.data_layout.endian;
369 let offset = offset.bytes() as usize;
370 let ptr_size = tcx.data_layout.pointer_size;
371 let bytes = &alloc.inspect_with_undef_and_ptr_outside_interpreter(offset..offset + ptr_size.bytes() as usize);
372 read_target_uint(endianness, bytes).unwrap()
375 // Don't inline `reloc_target_alloc` into the match. That would cause `tcx.alloc_map`
376 // to be locked for the duration of the match. `data_id_for_static` however may try
377 // to lock `tcx.alloc_map` itself while calculating the layout of the target static.
378 // This would cause a panic in single threaded rustc and a deadlock for parallel rustc.
379 let reloc_target_alloc = tcx.alloc_map.lock().get(reloc).unwrap();
380 let data_id = match reloc_target_alloc {
381 GlobalAlloc::Function(instance) => {
382 assert_eq!(addend, 0);
383 let func_id = crate::abi::import_function(tcx, module, instance);
384 let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
385 data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
388 GlobalAlloc::Memory(_) => {
389 cx.todo.insert(TodoItem::Alloc(reloc));
390 data_id_for_alloc_id(module, reloc, alloc.align)
392 GlobalAlloc::Static(def_id) => {
393 // Don't push a `TodoItem::Static` here, as it will cause statics used by
394 // multiple crates to be duplicated between them. It isn't necessary anyway,
395 // as it will get pushed by `codegen_static` when necessary.
400 crate::linkage::get_static_ref_linkage(tcx, def_id),
405 let global_value = module.declare_data_in_data(data_id, &mut data_ctx);
406 data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
409 module.define_data(data_id, &data_ctx).unwrap();
410 cx.done.insert(data_id);
413 assert!(cx.todo.is_empty(), "{:?}", cx.todo);
416 fn pop_set<T: Copy + Eq + ::std::hash::Hash>(set: &mut HashSet<T>) -> Option<T> {
417 if let Some(elem) = set.iter().next().map(|elem| *elem) {
425 struct TransPlaceInterpreter;
427 impl<'mir, 'tcx> Machine<'mir, 'tcx> for TransPlaceInterpreter {
428 type MemoryKinds = !;
430 type PointerTag = ();
431 type AllocExtra = ();
432 type MemoryExtra = ();
433 type FrameExtra = ();
434 type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation<()>)>;
436 const CHECK_ALIGN: bool = true;
437 const STATIC_KIND: Option<!> = None;
439 fn enforce_validity(_: &InterpCx<'mir, 'tcx, Self>) -> bool {
443 fn before_terminator(_: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
447 fn find_mir_or_eval_fn(
448 _: &mut InterpCx<'mir, 'tcx, Self>,
452 _: Option<(PlaceTy<'tcx>, BasicBlock)>,
453 _: Option<BasicBlock>,
454 ) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
459 _: &mut InterpCx<'mir, 'tcx, Self>,
463 _: Option<(PlaceTy<'tcx>, BasicBlock)>,
464 _: Option<BasicBlock>,
465 ) -> InterpResult<'tcx> {
469 fn find_foreign_static(_: TyCtxt<'tcx>, _: DefId) -> InterpResult<'tcx, Cow<'tcx, Allocation>> {
474 _: &InterpCx<'mir, 'tcx, Self>,
478 ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
482 fn ptr_to_int(_: &Memory<'mir, 'tcx, Self>, _: Pointer<()>) -> InterpResult<'tcx, u64> {
486 fn box_alloc(_: &mut InterpCx<'mir, 'tcx, Self>, _: PlaceTy<'tcx>) -> InterpResult<'tcx> {
490 fn init_allocation_extra<'b>(
493 alloc: Cow<'b, Allocation>,
494 _: Option<MemoryKind<!>>,
495 ) -> (Cow<'b, Allocation<(), ()>>, ()) {
499 fn tag_static_base_pointer(_: &(), _: AllocId) -> Self::PointerTag {
504 _: &mut InterpCx<'mir, 'tcx, Self>,
506 _: &[OpTy<'tcx, ()>],
507 _: Option<(PlaceTy<'tcx, ()>, BasicBlock)>,
508 _: Option<BasicBlock>,
509 ) -> InterpResult<'tcx> {
513 fn stack_push(_: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
517 fn stack_pop(_: &mut InterpCx<'mir, 'tcx, Self>, _: (), _: bool) -> InterpResult<'tcx, StackPopInfo> {
518 Ok(StackPopInfo::Normal)
522 _: &mut InterpCx<'mir, 'tcx, Self>,
524 _: &PanicInfo<Operand<'tcx>>,
525 _: Option<BasicBlock>,
526 ) -> InterpResult<'tcx> {
531 pub fn mir_operand_get_const_val<'tcx>(
532 fx: &FunctionCx<'_, 'tcx, impl Backend>,
533 operand: &Operand<'tcx>,
534 ) -> Option<&'tcx Const<'tcx>> {
536 Operand::Copy(_) | Operand::Move(_) => return None,
537 Operand::Constant(const_) => return Some(force_eval_const(fx, const_.literal)),