3 use syntax::source_map::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 pub 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_promoted<'tcx>(
54 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
55 instance: Instance<'tcx>,
59 match fx.tcx.const_eval_promoted(instance, promoted) {
61 let cplace = trans_const_place(fx, const_);
62 debug_assert_eq!(cplace.layout(), fx.layout_of(dest_ty));
65 Err(_) => crate::trap::trap_unreachable_ret_place(
67 fx.layout_of(dest_ty),
68 "[panic] Tried to get value of promoted value with errored during const eval.",
73 pub fn trans_constant<'tcx>(
74 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
75 constant: &Constant<'tcx>,
77 let const_ = force_eval_const(fx, &constant.literal);
78 trans_const_value(fx, const_)
81 pub fn force_eval_const<'tcx>(
82 fx: &FunctionCx<'_, 'tcx, impl Backend>,
84 ) -> &'tcx Const<'tcx> {
86 ConstKind::Unevaluated(def_id, ref substs) => {
87 let substs = fx.monomorphize(substs);
88 fx.tcx.const_eval_resolve(ParamEnv::reveal_all(), def_id, substs, None).unwrap()
90 _ => fx.monomorphize(&const_),
94 pub fn trans_const_value<'tcx>(
95 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
96 const_: &'tcx Const<'tcx>,
98 let ty = fx.monomorphize(&const_.ty);
99 let layout = fx.layout_of(ty);
101 ty::Bool | ty::Uint(_) => {
102 let bits = const_.val.try_to_bits(layout.size).unwrap();
103 CValue::const_val(fx, ty, bits)
106 let bits = const_.val.try_to_bits(layout.size).unwrap();
110 rustc::mir::interpret::sign_extend(bits, layout.size),
114 let bits = const_.val.try_to_bits(layout.size).unwrap();
115 let val = match fty {
119 .f32const(Ieee32::with_bits(u32::try_from(bits).unwrap())),
123 .f64const(Ieee64::with_bits(u64::try_from(bits).unwrap())),
125 CValue::by_val(val, layout)
127 ty::FnDef(_def_id, _substs) => CValue::by_ref(
128 crate::pointer::Pointer::const_addr(fx, fx.pointer_type.bytes() as i64),
131 _ => trans_const_place(fx, const_).to_cvalue(fx),
135 fn trans_const_place<'tcx>(
136 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
137 const_: &'tcx Const<'tcx>,
139 // Adapted from https://github.com/rust-lang/rust/pull/53671/files#diff-e0b58bb6712edaa8595ad7237542c958L551
140 let result = || -> InterpResult<'tcx, &'tcx Allocation> {
141 let mut ecx = InterpCx::new(
143 ty::ParamEnv::reveal_all(),
144 TransPlaceInterpreter,
147 ecx.push_stack_frame(
152 StackPopCleanup::None { cleanup: false },
155 let op = ecx.eval_operand(
156 &Operand::Constant(Box::new(Constant {
163 let ptr = ecx.allocate(op.layout, MemoryKind::Stack);
164 ecx.copy_op(op, ptr.into())?;
167 .get_raw(ptr.to_ref().to_scalar()?.assert_ptr().alloc_id)?;
168 Ok(fx.tcx.intern_const_alloc(alloc.clone()))
170 let alloc = result().expect("unable to convert ConstKind to Allocation");
172 //println!("const value: {:?} allocation: {:?}", value, alloc);
173 let alloc_id = fx.tcx.alloc_map.lock().create_memory_alloc(alloc);
174 fx.constants_cx.todo.insert(TodoItem::Alloc(alloc_id));
175 let data_id = data_id_for_alloc_id(fx.module, alloc_id, alloc.align);
176 cplace_for_dataid(fx, const_.ty, data_id)
179 fn data_id_for_alloc_id<B: Backend>(
180 module: &mut Module<B>,
186 &format!("__alloc_{}", alloc_id.0),
189 Some(align.bytes() as u8),
194 fn data_id_for_static(
196 module: &mut Module<impl Backend>,
200 let instance = Instance::mono(tcx, def_id);
201 let symbol_name = tcx.symbol_name(instance).name.as_str();
202 let ty = instance.ty(tcx);
203 let is_mutable = if tcx.is_mutable_static(def_id) {
206 !ty.is_freeze(tcx, ParamEnv::reveal_all(), DUMMY_SP)
209 .layout_of(ParamEnv::reveal_all().and(ty))
220 Some(align.try_into().unwrap()),
224 if linkage == Linkage::Preemptible {
225 if let ty::RawPtr(_) = ty.kind {
228 tcx.def_span(def_id),
229 "must have type `*const T` or `*mut T` due to `#[linkage]` attribute",
233 let mut data_ctx = DataContext::new();
234 let zero_bytes = std::iter::repeat(0)
235 .take(pointer_ty(tcx).bytes() as usize)
236 .collect::<Vec<u8>>()
238 data_ctx.define(zero_bytes);
239 match module.define_data(data_id, &data_ctx) {
240 // Everytime a weak static is referenced, there will be a zero pointer definition,
241 // so duplicate definitions are expected and allowed.
242 Err(ModuleError::DuplicateDefinition(_)) => {}
250 fn cplace_for_dataid<'tcx>(
251 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
255 let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
256 let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
257 let layout = fx.layout_of(fx.monomorphize(&ty));
258 assert!(!layout.is_unsized(), "unsized statics aren't supported");
259 CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout)
262 fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut Module<impl Backend>, cx: &mut ConstantCx) {
263 let memory = Memory::<TransPlaceInterpreter>::new(tcx.at(DUMMY_SP), ());
265 while let Some(todo_item) = pop_set(&mut cx.todo) {
266 let (data_id, alloc) = match todo_item {
267 TodoItem::Alloc(alloc_id) => {
268 //println!("alloc_id {}", alloc_id);
269 let alloc = memory.get_raw(alloc_id).unwrap();
270 let data_id = data_id_for_alloc_id(module, alloc_id, alloc.align);
273 TodoItem::Static(def_id) => {
274 //println!("static {:?}", def_id);
276 if tcx.is_foreign_item(def_id) {
280 let const_ = tcx.const_eval_poly(def_id).unwrap();
282 let alloc = match const_.val {
283 ConstKind::Value(ConstValue::ByRef { alloc, offset }) if offset.bytes() == 0 => alloc,
284 _ => bug!("static const eval returned {:#?}", const_),
287 let data_id = data_id_for_static(
291 if tcx.is_reachable_non_generic(def_id) {
301 //("data_id {}", data_id);
302 if cx.done.contains(&data_id) {
306 let mut data_ctx = DataContext::new();
308 let bytes = alloc.inspect_with_undef_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
309 data_ctx.define(bytes.into_boxed_slice());
311 for &(offset, (_tag, reloc)) in alloc.relocations().iter() {
313 let endianness = tcx.data_layout.endian;
314 let offset = offset.bytes() as usize;
315 let ptr_size = tcx.data_layout.pointer_size;
316 let bytes = &alloc.inspect_with_undef_and_ptr_outside_interpreter(offset..offset + ptr_size.bytes() as usize);
317 read_target_uint(endianness, bytes).unwrap()
320 // Don't inline `reloc_target_alloc` into the match. That would cause `tcx.alloc_map`
321 // to be locked for the duration of the match. `data_id_for_static` however may try
322 // to lock `tcx.alloc_map` itself while calculating the layout of the target static.
323 // This would cause a panic in single threaded rustc and a deadlock for parallel rustc.
324 let reloc_target_alloc = tcx.alloc_map.lock().get(reloc).unwrap();
325 let data_id = match reloc_target_alloc {
326 GlobalAlloc::Function(instance) => {
327 assert_eq!(addend, 0);
328 let func_id = crate::abi::import_function(tcx, module, instance);
329 let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
330 data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
333 GlobalAlloc::Memory(_) => {
334 cx.todo.insert(TodoItem::Alloc(reloc));
335 data_id_for_alloc_id(module, reloc, alloc.align)
337 GlobalAlloc::Static(def_id) => {
338 // Don't push a `TodoItem::Static` here, as it will cause statics used by
339 // multiple crates to be duplicated between them. It isn't necessary anyway,
340 // as it will get pushed by `codegen_static` when necessary.
345 crate::linkage::get_static_ref_linkage(tcx, def_id),
350 let global_value = module.declare_data_in_data(data_id, &mut data_ctx);
351 data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
354 module.define_data(data_id, &data_ctx).unwrap();
355 cx.done.insert(data_id);
358 assert!(cx.todo.is_empty(), "{:?}", cx.todo);
361 fn pop_set<T: Copy + Eq + ::std::hash::Hash>(set: &mut HashSet<T>) -> Option<T> {
362 if let Some(elem) = set.iter().next().map(|elem| *elem) {
370 struct TransPlaceInterpreter;
372 impl<'mir, 'tcx> Machine<'mir, 'tcx> for TransPlaceInterpreter {
373 type MemoryKinds = !;
375 type PointerTag = ();
376 type AllocExtra = ();
377 type MemoryExtra = ();
378 type FrameExtra = ();
379 type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation<()>)>;
381 const CHECK_ALIGN: bool = true;
382 const STATIC_KIND: Option<!> = None;
384 fn enforce_validity(_: &InterpCx<'mir, 'tcx, Self>) -> bool {
388 fn before_terminator(_: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
392 fn find_mir_or_eval_fn(
393 _: &mut InterpCx<'mir, 'tcx, Self>,
396 _: Option<(PlaceTy<'tcx>, BasicBlock)>,
397 _: Option<BasicBlock>,
398 ) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
403 _: &mut InterpCx<'mir, 'tcx, Self>,
407 _: Option<(PlaceTy<'tcx>, BasicBlock)>,
408 _: Option<BasicBlock>,
409 ) -> InterpResult<'tcx> {
413 fn find_foreign_static(_: TyCtxt<'tcx>, _: DefId) -> InterpResult<'tcx, Cow<'tcx, Allocation>> {
418 _: &InterpCx<'mir, 'tcx, Self>,
422 ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
426 fn ptr_to_int(_: &Memory<'mir, 'tcx, Self>, _: Pointer<()>) -> InterpResult<'tcx, u64> {
430 fn box_alloc(_: &mut InterpCx<'mir, 'tcx, Self>, _: PlaceTy<'tcx>) -> InterpResult<'tcx> {
434 fn init_allocation_extra<'b>(
437 alloc: Cow<'b, Allocation>,
438 _: Option<MemoryKind<!>>,
439 ) -> (Cow<'b, Allocation<(), ()>>, ()) {
443 fn tag_static_base_pointer(_: &(), _: AllocId) -> Self::PointerTag {
448 _: &mut InterpCx<'mir, 'tcx, Self>,
450 _: &[OpTy<'tcx, ()>],
451 _: Option<(PlaceTy<'tcx, ()>, BasicBlock)>,
452 _: Option<BasicBlock>,
453 ) -> InterpResult<'tcx> {
457 fn stack_push(_: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
461 fn stack_pop(_: &mut InterpCx<'mir, 'tcx, Self>, _: (), _: bool) -> InterpResult<'tcx, StackPopInfo> {
462 Ok(StackPopInfo::Normal)
466 _: &mut InterpCx<'mir, 'tcx, Self>,
468 _: &PanicInfo<Operand<'tcx>>,
469 _: Option<BasicBlock>,
470 ) -> InterpResult<'tcx> {
475 pub fn mir_operand_get_const_val<'tcx>(
476 fx: &FunctionCx<'_, 'tcx, impl Backend>,
477 operand: &Operand<'tcx>,
478 ) -> Option<&'tcx Const<'tcx>> {
479 let place = match operand {
480 Operand::Copy(place) | Operand::Move(place) => place,
481 Operand::Constant(const_) => return Some(force_eval_const(fx, const_.literal)),
484 assert!(place.projection.is_empty());
485 let static_ = match &place.base {
486 PlaceBase::Static(static_) => static_,
487 PlaceBase::Local(_) => return None,
490 Some(match &static_.kind {
491 StaticKind::Static => unimplemented!(),
492 StaticKind::Promoted(promoted, substs) => {
493 let instance = Instance::new(static_.def_id, fx.monomorphize(substs));
494 fx.tcx.const_eval_promoted(instance, *promoted).unwrap()