3 use rustc_span::DUMMY_SP;
5 use rustc::middle::codegen_fn_attrs::CodegenFnAttrFlags;
6 use rustc::mir::interpret::{
7 read_target_uint, AllocId, Allocation, ConstValue, GlobalAlloc, InterpResult, Scalar,
9 use rustc::ty::{layout::Align, Const, ConstKind};
10 use rustc_data_structures::fx::FxHashSet;
11 use rustc_mir::interpret::{
12 ImmTy, InterpCx, Machine, Memory, MemoryKind, OpTy, PlaceTy, Pointer,
13 StackPopCleanup, StackPopJump,
16 use cranelift_codegen::ir::GlobalValue;
17 use cranelift_module::*;
19 use crate::prelude::*;
22 pub(crate) struct ConstantCx {
24 done: FxHashSet<DataId>,
27 #[derive(Copy, Clone, Debug)]
34 pub(crate) fn finalize(mut self, tcx: TyCtxt<'_>, module: &mut Module<impl Backend>) {
35 //println!("todo {:?}", self.todo);
36 define_all_allocs(tcx, module, &mut self);
37 //println!("done {:?}", self.done);
42 pub(crate) fn codegen_static(constants_cx: &mut ConstantCx, def_id: DefId) {
43 constants_cx.todo.push(TodoItem::Static(def_id));
46 fn codegen_static_ref<'tcx>(
47 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
49 layout: TyLayout<'tcx>,
51 let linkage = crate::linkage::get_static_ref_linkage(fx.tcx, def_id);
52 let data_id = data_id_for_static(fx.tcx, fx.module, def_id, linkage);
53 let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
54 #[cfg(debug_assertions)]
55 fx.add_comment(local_data_id, format!("{:?}", def_id));
56 cplace_for_dataid(fx, layout, local_data_id)
59 pub(crate) fn trans_constant<'tcx>(
60 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
61 constant: &Constant<'tcx>,
63 let const_ = match constant.literal.val {
64 ConstKind::Unevaluated(def_id, ref substs, promoted) if fx.tcx.is_static(def_id) => {
65 assert!(substs.is_empty());
66 assert!(promoted.is_none());
68 return codegen_static_ref(
71 fx.layout_of(fx.monomorphize(&constant.literal.ty)),
74 _ => fx.monomorphize(&constant.literal).eval(fx.tcx, ParamEnv::reveal_all()),
77 trans_const_value(fx, const_)
80 pub(crate) fn trans_const_value<'tcx>(
81 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
82 const_: &'tcx Const<'tcx>,
84 let ty = fx.monomorphize(&const_.ty);
85 let layout = fx.layout_of(ty);
88 return CValue::by_ref(
89 crate::Pointer::const_addr(fx, i64::try_from(layout.align.pref.bytes()).unwrap()),
93 let const_val = match const_.val {
94 ConstKind::Value(const_val) => const_val,
95 _ => unreachable!("Const {:?} should have been evaluated", const_),
99 ConstValue::Scalar(x) => {
100 if fx.clif_type(layout.ty).is_none() {
101 return trans_const_place(fx, const_).to_cvalue(fx);
105 Scalar::Raw { data, size } => {
106 assert_eq!(u64::from(size), layout.size.bytes());
107 return CValue::const_val(fx, layout, data);
109 Scalar::Ptr(ptr) => {
110 let alloc_kind = fx.tcx.alloc_map.lock().get(ptr.alloc_id);
111 let base_addr = match alloc_kind {
112 Some(GlobalAlloc::Memory(alloc)) => {
113 fx.constants_cx.todo.push(TodoItem::Alloc(ptr.alloc_id));
114 let data_id = data_id_for_alloc_id(fx.module, ptr.alloc_id, alloc.align);
115 let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
116 #[cfg(debug_assertions)]
117 fx.add_comment(local_data_id, format!("{:?}", ptr.alloc_id));
118 fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
120 Some(GlobalAlloc::Function(instance)) => {
121 let func_id = crate::abi::import_function(fx.tcx, fx.module, instance);
122 let local_func_id = fx.module.declare_func_in_func(func_id, &mut fx.bcx.func);
123 fx.bcx.ins().func_addr(fx.pointer_type, local_func_id)
125 Some(GlobalAlloc::Static(def_id)) => {
126 assert!(fx.tcx.is_static(def_id));
127 let linkage = crate::linkage::get_static_ref_linkage(fx.tcx, def_id);
128 let data_id = data_id_for_static(fx.tcx, fx.module, def_id, linkage);
129 let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
130 #[cfg(debug_assertions)]
131 fx.add_comment(local_data_id, format!("{:?}", def_id));
132 fx.bcx.ins().global_value(fx.pointer_type, local_data_id)
134 None => bug!("missing allocation {:?}", ptr.alloc_id),
136 let val = fx.bcx.ins().iadd_imm(base_addr, i64::try_from(ptr.offset.bytes()).unwrap());
137 return CValue::by_val(val, layout);
141 ConstValue::ByRef { alloc, offset } => {
142 let alloc_id = fx.tcx.alloc_map.lock().create_memory_alloc(alloc);
143 fx.constants_cx.todo.push(TodoItem::Alloc(alloc_id));
144 let data_id = data_id_for_alloc_id(fx.module, alloc_id, alloc.align);
145 let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
146 let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
147 assert!(!layout.is_unsized(), "unsized ConstValue::ByRef not supported");
149 crate::pointer::Pointer::new(global_ptr)
150 .offset_i64(fx, i64::try_from(offset.bytes()).unwrap()),
154 ConstValue::Slice { data: _, start: _, end: _ } => {
155 trans_const_place(fx, const_).to_cvalue(fx)
160 fn trans_const_place<'tcx>(
161 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
162 const_: &'tcx Const<'tcx>,
164 // Adapted from https://github.com/rust-lang/rust/pull/53671/files#diff-e0b58bb6712edaa8595ad7237542c958L551
165 let result = || -> InterpResult<'tcx, &'tcx Allocation> {
166 let mut ecx = InterpCx::new(
168 ty::ParamEnv::reveal_all(),
169 TransPlaceInterpreter,
172 ecx.push_stack_frame(
177 StackPopCleanup::None { cleanup: false },
180 let op = ecx.eval_operand(
181 &Operand::Constant(Box::new(Constant {
188 let ptr = ecx.allocate(op.layout, MemoryKind::Stack);
189 ecx.copy_op(op, ptr.into())?;
192 .get_raw(ptr.to_ref().to_scalar()?.assert_ptr().alloc_id)?;
193 Ok(fx.tcx.intern_const_alloc(alloc.clone()))
195 let alloc = result().expect("unable to convert ConstKind to Allocation");
197 //println!("const value: {:?} allocation: {:?}", value, alloc);
198 let alloc_id = fx.tcx.alloc_map.lock().create_memory_alloc(alloc);
199 fx.constants_cx.todo.push(TodoItem::Alloc(alloc_id));
200 let data_id = data_id_for_alloc_id(fx.module, alloc_id, alloc.align);
201 let local_data_id = fx.module.declare_data_in_func(data_id, &mut fx.bcx.func);
202 #[cfg(debug_assertions)]
203 fx.add_comment(local_data_id, format!("{:?}", alloc_id));
204 cplace_for_dataid(fx, fx.layout_of(const_.ty), local_data_id)
207 fn data_id_for_alloc_id<B: Backend>(
208 module: &mut Module<B>,
214 &format!("__alloc_{}", alloc_id.0),
218 Some(align.bytes() as u8),
223 fn data_id_for_static(
225 module: &mut Module<impl Backend>,
229 let instance = Instance::mono(tcx, def_id);
230 let symbol_name = tcx.symbol_name(instance).name.as_str();
231 let ty = instance.monomorphic_ty(tcx);
232 let is_mutable = if tcx.is_mutable_static(def_id) {
235 !ty.is_freeze(tcx, ParamEnv::reveal_all(), DUMMY_SP)
238 .layout_of(ParamEnv::reveal_all().and(ty))
244 let attrs = tcx.codegen_fn_attrs(def_id);
251 attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL),
252 Some(align.try_into().unwrap()),
256 if linkage == Linkage::Preemptible {
257 if let ty::RawPtr(_) = ty.kind {
260 tcx.def_span(def_id),
261 "must have type `*const T` or `*mut T` due to `#[linkage]` attribute",
265 let mut data_ctx = DataContext::new();
266 data_ctx.define_zeroinit(pointer_ty(tcx).bytes() as usize);
267 match module.define_data(data_id, &data_ctx) {
268 // Everytime a weak static is referenced, there will be a zero pointer definition,
269 // so duplicate definitions are expected and allowed.
270 Err(ModuleError::DuplicateDefinition(_)) => {}
278 fn cplace_for_dataid<'tcx>(
279 fx: &mut FunctionCx<'_, 'tcx, impl Backend>,
280 layout: TyLayout<'tcx>,
281 local_data_id: GlobalValue,
283 let global_ptr = fx.bcx.ins().global_value(fx.pointer_type, local_data_id);
284 assert!(!layout.is_unsized(), "unsized statics aren't supported");
285 CPlace::for_ptr(crate::pointer::Pointer::new(global_ptr), layout)
288 fn define_all_allocs(tcx: TyCtxt<'_>, module: &mut Module<impl Backend>, cx: &mut ConstantCx) {
289 let memory = Memory::<TransPlaceInterpreter>::new(tcx.at(DUMMY_SP), ());
291 while let Some(todo_item) = cx.todo.pop() {
292 let (data_id, alloc) = match todo_item {
293 TodoItem::Alloc(alloc_id) => {
294 //println!("alloc_id {}", alloc_id);
295 let alloc = memory.get_raw(alloc_id).unwrap();
296 let data_id = data_id_for_alloc_id(module, alloc_id, alloc.align);
299 TodoItem::Static(def_id) => {
300 //println!("static {:?}", def_id);
302 if tcx.is_foreign_item(def_id) {
306 let const_ = tcx.const_eval_poly(def_id).unwrap();
308 let alloc = match const_ {
309 ConstValue::ByRef { alloc, offset } if offset.bytes() == 0 => alloc,
310 _ => bug!("static const eval returned {:#?}", const_),
313 let data_id = data_id_for_static(
317 if tcx.is_reachable_non_generic(def_id) {
320 Linkage::Export // FIXME Set hidden visibility
327 //("data_id {}", data_id);
328 if cx.done.contains(&data_id) {
332 let mut data_ctx = DataContext::new();
334 let bytes = alloc.inspect_with_undef_and_ptr_outside_interpreter(0..alloc.len()).to_vec();
335 data_ctx.define(bytes.into_boxed_slice());
337 for &(offset, (_tag, reloc)) in alloc.relocations().iter() {
339 let endianness = tcx.data_layout.endian;
340 let offset = offset.bytes() as usize;
341 let ptr_size = tcx.data_layout.pointer_size;
342 let bytes = &alloc.inspect_with_undef_and_ptr_outside_interpreter(offset..offset + ptr_size.bytes() as usize);
343 read_target_uint(endianness, bytes).unwrap()
346 // Don't inline `reloc_target_alloc` into the match. That would cause `tcx.alloc_map`
347 // to be locked for the duration of the match. `data_id_for_static` however may try
348 // to lock `tcx.alloc_map` itself while calculating the layout of the target static.
349 // This would cause a panic in single threaded rustc and a deadlock for parallel rustc.
350 let reloc_target_alloc = tcx.alloc_map.lock().get(reloc).unwrap();
351 let data_id = match reloc_target_alloc {
352 GlobalAlloc::Function(instance) => {
353 assert_eq!(addend, 0);
354 let func_id = crate::abi::import_function(tcx, module, instance);
355 let local_func_id = module.declare_func_in_data(func_id, &mut data_ctx);
356 data_ctx.write_function_addr(offset.bytes() as u32, local_func_id);
359 GlobalAlloc::Memory(_) => {
360 cx.todo.push(TodoItem::Alloc(reloc));
361 data_id_for_alloc_id(module, reloc, alloc.align)
363 GlobalAlloc::Static(def_id) => {
364 if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
365 tcx.sess.fatal(&format!("Allocation {:?} contains reference to TLS value {:?}", alloc, def_id));
368 // Don't push a `TodoItem::Static` here, as it will cause statics used by
369 // multiple crates to be duplicated between them. It isn't necessary anyway,
370 // as it will get pushed by `codegen_static` when necessary.
375 crate::linkage::get_static_ref_linkage(tcx, def_id),
380 let global_value = module.declare_data_in_data(data_id, &mut data_ctx);
381 data_ctx.write_data_addr(offset.bytes() as u32, global_value, addend as i64);
384 module.define_data(data_id, &data_ctx).unwrap();
385 cx.done.insert(data_id);
388 assert!(cx.todo.is_empty(), "{:?}", cx.todo);
391 struct TransPlaceInterpreter;
393 impl<'mir, 'tcx> Machine<'mir, 'tcx> for TransPlaceInterpreter {
394 type MemoryKinds = !;
396 type PointerTag = ();
397 type AllocExtra = ();
398 type MemoryExtra = ();
399 type FrameExtra = ();
400 type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation<()>)>;
402 const CHECK_ALIGN: bool = true;
403 const STATIC_KIND: Option<!> = None;
405 fn enforce_validity(_: &InterpCx<'mir, 'tcx, Self>) -> bool {
409 fn before_terminator(_: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
413 fn find_mir_or_eval_fn(
414 _: &mut InterpCx<'mir, 'tcx, Self>,
418 _: Option<(PlaceTy<'tcx>, BasicBlock)>,
419 _: Option<BasicBlock>,
420 ) -> InterpResult<'tcx, Option<&'mir Body<'tcx>>> {
425 _: &mut InterpCx<'mir, 'tcx, Self>,
429 _: Option<(PlaceTy<'tcx>, BasicBlock)>,
430 _: Option<BasicBlock>,
431 ) -> InterpResult<'tcx> {
436 _: &InterpCx<'mir, 'tcx, Self>,
440 ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
444 fn ptr_to_int(_: &Memory<'mir, 'tcx, Self>, _: Pointer<()>) -> InterpResult<'tcx, u64> {
448 fn box_alloc(_: &mut InterpCx<'mir, 'tcx, Self>, _: PlaceTy<'tcx>) -> InterpResult<'tcx> {
452 fn init_allocation_extra<'b>(
455 alloc: Cow<'b, Allocation>,
456 _: Option<MemoryKind<!>>,
457 ) -> (Cow<'b, Allocation<(), ()>>, ()) {
461 fn tag_static_base_pointer(_: &(), _: AllocId) -> Self::PointerTag {
466 _: &mut InterpCx<'mir, 'tcx, Self>,
468 _: &[OpTy<'tcx, ()>],
469 _: Option<(PlaceTy<'tcx, ()>, BasicBlock)>,
470 _: Option<BasicBlock>,
471 ) -> InterpResult<'tcx> {
475 fn stack_push(_: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
479 fn stack_pop(_: &mut InterpCx<'mir, 'tcx, Self>, _: (), _: bool) -> InterpResult<'tcx, StackPopJump> {
480 Ok(StackPopJump::Normal)
484 _: &mut InterpCx<'mir, 'tcx, Self>,
485 _: &mir::AssertKind<Operand<'tcx>>,
486 _: Option<BasicBlock>,
487 ) -> InterpResult<'tcx> {
492 pub(crate) fn mir_operand_get_const_val<'tcx>(
493 fx: &FunctionCx<'_, 'tcx, impl Backend>,
494 operand: &Operand<'tcx>,
495 ) -> Option<&'tcx Const<'tcx>> {
497 Operand::Copy(_) | Operand::Move(_) => None,
498 Operand::Constant(const_) => {
499 Some(fx.monomorphize(&const_.literal).eval(fx.tcx, ParamEnv::reveal_all()))