]> git.lizzy.rs Git - rust.git/blob - src/machine.rs
Run fmt
[rust.git] / src / machine.rs
1 //! Global machine state as well as implementation of the interpreter engine
2 //! `Machine` trait.
3
4 use std::borrow::Cow;
5 use std::cell::RefCell;
6 use std::fmt;
7 use std::num::NonZeroU64;
8 use std::time::Instant;
9
10 use log::trace;
11 use measureme::{DetachedTiming, EventId, Profiler, StringId};
12 use rand::rngs::StdRng;
13 use rand::SeedableRng;
14
15 use rustc_data_structures::fx::FxHashMap;
16 use rustc_middle::{
17     mir,
18     ty::{
19         self,
20         layout::{LayoutCx, LayoutError, TyAndLayout},
21         TyCtxt,
22     },
23 };
24 use rustc_span::def_id::DefId;
25 use rustc_span::symbol::{sym, Symbol};
26 use rustc_target::abi::{LayoutOf, Size};
27 use rustc_target::spec::abi::Abi;
28
29 use crate::*;
30
31 // Some global facts about the emulated machine.
32 pub const PAGE_SIZE: u64 = 4 * 1024; // FIXME: adjust to target architecture
33 pub const STACK_ADDR: u64 = 32 * PAGE_SIZE; // not really about the "stack", but where we start assigning integer addresses to allocations
34 pub const STACK_SIZE: u64 = 16 * PAGE_SIZE; // whatever
35 pub const NUM_CPUS: u64 = 1;
36
37 /// Extra data stored with each stack frame
38 pub struct FrameData<'tcx> {
39     /// Extra data for Stacked Borrows.
40     pub call_id: stacked_borrows::CallId,
41
42     /// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn`
43     /// called by `try`). When this frame is popped during unwinding a panic,
44     /// we stop unwinding, use the `CatchUnwindData` to handle catching.
45     pub catch_unwind: Option<CatchUnwindData<'tcx>>,
46
47     /// If `measureme` profiling is enabled, holds timing information
48     /// for the start of this frame. When we finish executing this frame,
49     /// we use this to register a completed event with `measureme`.
50     pub timing: Option<DetachedTiming>,
51 }
52
53 /// Extra memory kinds
54 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
55 pub enum MiriMemoryKind {
56     /// `__rust_alloc` memory.
57     Rust,
58     /// `malloc` memory.
59     C,
60     /// Windows `HeapAlloc` memory.
61     WinHeap,
62     /// Memory for args, errno, and other parts of the machine-managed environment.
63     /// This memory may leak.
64     Machine,
65     /// Memory for env vars. Separate from `Machine` because we clean it up and leak-check it.
66     Env,
67     /// Globals copied from `tcx`.
68     /// This memory may leak.
69     Global,
70     /// Memory for extern statics.
71     /// This memory may leak.
72     ExternStatic,
73     /// Memory for thread-local statics.
74     /// This memory may leak.
75     Tls,
76 }
77
78 impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
79     #[inline(always)]
80     fn into(self) -> MemoryKind<MiriMemoryKind> {
81         MemoryKind::Machine(self)
82     }
83 }
84
85 impl MayLeak for MiriMemoryKind {
86     #[inline(always)]
87     fn may_leak(self) -> bool {
88         use self::MiriMemoryKind::*;
89         match self {
90             Rust | C | WinHeap | Env => false,
91             Machine | Global | ExternStatic | Tls => true,
92         }
93     }
94 }
95
96 impl fmt::Display for MiriMemoryKind {
97     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98         use self::MiriMemoryKind::*;
99         match self {
100             Rust => write!(f, "Rust heap"),
101             C => write!(f, "C heap"),
102             WinHeap => write!(f, "Windows heap"),
103             Machine => write!(f, "machine-managed memory"),
104             Env => write!(f, "environment variable"),
105             Global => write!(f, "global (static or const)"),
106             ExternStatic => write!(f, "extern static"),
107             Tls => write!(f, "thread-local static"),
108         }
109     }
110 }
111
112 /// Extra per-allocation data
113 #[derive(Debug, Clone)]
114 pub struct AllocExtra {
115     /// Stacked Borrows state is only added if it is enabled.
116     pub stacked_borrows: Option<stacked_borrows::AllocExtra>,
117     /// Data race detection via the use of a vector-clock,
118     ///  this is only added if it is enabled.
119     pub data_race: Option<data_race::AllocExtra>,
120 }
121
122 /// Extra global memory data
123 #[derive(Debug)]
124 pub struct MemoryExtra {
125     pub stacked_borrows: Option<stacked_borrows::MemoryExtra>,
126     pub data_race: Option<data_race::MemoryExtra>,
127     pub intptrcast: intptrcast::MemoryExtra,
128
129     /// Mapping extern static names to their canonical allocation.
130     extern_statics: FxHashMap<Symbol, AllocId>,
131
132     /// The random number generator used for resolving non-determinism.
133     /// Needs to be queried by ptr_to_int, hence needs interior mutability.
134     pub(crate) rng: RefCell<StdRng>,
135
136     /// An allocation ID to report when it is being allocated
137     /// (helps for debugging memory leaks and use after free bugs).
138     tracked_alloc_id: Option<AllocId>,
139
140     /// Controls whether alignment of memory accesses is being checked.
141     pub(crate) check_alignment: AlignmentCheck,
142
143     /// Failure rate of compare_exchange_weak, between 0.0 and 1.0
144     pub(crate) cmpxchg_weak_failure_rate: f64,
145 }
146
147 impl MemoryExtra {
148     pub fn new(config: &MiriConfig) -> Self {
149         let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
150         let stacked_borrows = if config.stacked_borrows {
151             Some(RefCell::new(stacked_borrows::GlobalState::new(
152                 config.tracked_pointer_tag,
153                 config.tracked_call_id,
154                 config.track_raw,
155             )))
156         } else {
157             None
158         };
159         let data_race =
160             if config.data_race_detector { Some(data_race::GlobalState::new()) } else { None };
161         MemoryExtra {
162             stacked_borrows,
163             data_race,
164             intptrcast: Default::default(),
165             extern_statics: FxHashMap::default(),
166             rng: RefCell::new(rng),
167             tracked_alloc_id: config.tracked_alloc_id,
168             check_alignment: config.check_alignment,
169             cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
170         }
171     }
172
173     fn add_extern_static<'tcx, 'mir>(
174         this: &mut MiriEvalContext<'mir, 'tcx>,
175         name: &str,
176         ptr: Scalar<Tag>,
177     ) {
178         let ptr = ptr.assert_ptr();
179         assert_eq!(ptr.offset, Size::ZERO);
180         this.memory.extra.extern_statics.try_insert(Symbol::intern(name), ptr.alloc_id).unwrap();
181     }
182
183     /// Sets up the "extern statics" for this machine.
184     pub fn init_extern_statics<'tcx, 'mir>(
185         this: &mut MiriEvalContext<'mir, 'tcx>,
186     ) -> InterpResult<'tcx> {
187         match this.tcx.sess.target.os.as_str() {
188             "linux" => {
189                 // "__cxa_thread_atexit_impl"
190                 // This should be all-zero, pointer-sized.
191                 let layout = this.machine.layouts.usize;
192                 let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into());
193                 this.write_scalar(Scalar::from_machine_usize(0, this), &place.into())?;
194                 Self::add_extern_static(this, "__cxa_thread_atexit_impl", place.ptr);
195                 // "environ"
196                 Self::add_extern_static(
197                     this,
198                     "environ",
199                     this.machine.env_vars.environ.unwrap().ptr,
200                 );
201             }
202             "windows" => {
203                 // "_tls_used"
204                 // This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
205                 let layout = this.machine.layouts.u8;
206                 let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into());
207                 this.write_scalar(Scalar::from_u8(0), &place.into())?;
208                 Self::add_extern_static(this, "_tls_used", place.ptr);
209             }
210             _ => {} // No "extern statics" supported on this target
211         }
212         Ok(())
213     }
214 }
215
216 /// Precomputed layouts of primitive types
217 pub struct PrimitiveLayouts<'tcx> {
218     pub unit: TyAndLayout<'tcx>,
219     pub i8: TyAndLayout<'tcx>,
220     pub i32: TyAndLayout<'tcx>,
221     pub isize: TyAndLayout<'tcx>,
222     pub u8: TyAndLayout<'tcx>,
223     pub u32: TyAndLayout<'tcx>,
224     pub usize: TyAndLayout<'tcx>,
225 }
226
227 impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
228     fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, LayoutError<'tcx>> {
229         Ok(Self {
230             unit: layout_cx.layout_of(layout_cx.tcx.mk_unit())?,
231             i8: layout_cx.layout_of(layout_cx.tcx.types.i8)?,
232             i32: layout_cx.layout_of(layout_cx.tcx.types.i32)?,
233             isize: layout_cx.layout_of(layout_cx.tcx.types.isize)?,
234             u8: layout_cx.layout_of(layout_cx.tcx.types.u8)?,
235             u32: layout_cx.layout_of(layout_cx.tcx.types.u32)?,
236             usize: layout_cx.layout_of(layout_cx.tcx.types.usize)?,
237         })
238     }
239 }
240
241 /// The machine itself.
242 pub struct Evaluator<'mir, 'tcx> {
243     /// Environment variables set by `setenv`.
244     /// Miri does not expose env vars from the host to the emulated program.
245     pub(crate) env_vars: EnvVars<'tcx>,
246
247     /// Program arguments (`Option` because we can only initialize them after creating the ecx).
248     /// These are *pointers* to argc/argv because macOS.
249     /// We also need the full command line as one string because of Windows.
250     pub(crate) argc: Option<Scalar<Tag>>,
251     pub(crate) argv: Option<Scalar<Tag>>,
252     pub(crate) cmd_line: Option<Scalar<Tag>>,
253
254     /// TLS state.
255     pub(crate) tls: TlsData<'tcx>,
256
257     /// If enabled, the `env_vars` field is populated with the host env vars during initialization
258     /// and random number generation is delegated to the host.
259     pub(crate) communicate: bool,
260
261     /// Whether to enforce the validity invariant.
262     pub(crate) validate: bool,
263
264     pub(crate) file_handler: shims::posix::FileHandler,
265     pub(crate) dir_handler: shims::posix::DirHandler,
266
267     /// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
268     pub(crate) time_anchor: Instant,
269
270     /// The set of threads.
271     pub(crate) threads: ThreadManager<'mir, 'tcx>,
272
273     /// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
274     pub(crate) layouts: PrimitiveLayouts<'tcx>,
275
276     /// Allocations that are considered roots of static memory (that may leak).
277     pub(crate) static_roots: Vec<AllocId>,
278
279     /// The `measureme` profiler used to record timing information about
280     /// the emulated program.
281     profiler: Option<Profiler>,
282     /// Used with `profiler` to cache the `StringId`s for event names
283     /// uesd with `measureme`.
284     string_cache: FxHashMap<String, StringId>,
285 }
286
287 impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
288     pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self {
289         let layouts =
290             PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
291         let profiler = config
292             .measureme_out
293             .as_ref()
294             .map(|out| Profiler::new(out).expect("Couldn't create `measureme` profiler"));
295         Evaluator {
296             // `env_vars` could be initialized properly here if `Memory` were available before
297             // calling this method.
298             env_vars: EnvVars::default(),
299             argc: None,
300             argv: None,
301             cmd_line: None,
302             tls: TlsData::default(),
303             communicate: config.communicate,
304             validate: config.validate,
305             file_handler: Default::default(),
306             dir_handler: Default::default(),
307             time_anchor: Instant::now(),
308             layouts,
309             threads: ThreadManager::default(),
310             static_roots: Vec::new(),
311             profiler,
312             string_cache: Default::default(),
313         }
314     }
315 }
316
317 /// A rustc InterpCx for Miri.
318 pub type MiriEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>;
319
320 /// A little trait that's useful to be inherited by extension traits.
321 pub trait MiriEvalContextExt<'mir, 'tcx> {
322     fn eval_context_ref<'a>(&'a self) -> &'a MiriEvalContext<'mir, 'tcx>;
323     fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriEvalContext<'mir, 'tcx>;
324 }
325 impl<'mir, 'tcx> MiriEvalContextExt<'mir, 'tcx> for MiriEvalContext<'mir, 'tcx> {
326     #[inline(always)]
327     fn eval_context_ref(&self) -> &MiriEvalContext<'mir, 'tcx> {
328         self
329     }
330     #[inline(always)]
331     fn eval_context_mut(&mut self) -> &mut MiriEvalContext<'mir, 'tcx> {
332         self
333     }
334 }
335
336 /// Machine hook implementations.
337 impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
338     type MemoryKind = MiriMemoryKind;
339
340     type FrameExtra = FrameData<'tcx>;
341     type MemoryExtra = MemoryExtra;
342     type AllocExtra = AllocExtra;
343     type PointerTag = Tag;
344     type ExtraFnVal = Dlsym;
345
346     type MemoryMap =
347         MonoHashMap<AllocId, (MemoryKind<MiriMemoryKind>, Allocation<Tag, Self::AllocExtra>)>;
348
349     const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
350
351     #[inline(always)]
352     fn enforce_alignment(memory_extra: &MemoryExtra) -> bool {
353         memory_extra.check_alignment != AlignmentCheck::None
354     }
355
356     #[inline(always)]
357     fn force_int_for_alignment_check(memory_extra: &Self::MemoryExtra) -> bool {
358         memory_extra.check_alignment == AlignmentCheck::Int
359     }
360
361     #[inline(always)]
362     fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
363         ecx.machine.validate
364     }
365
366     #[inline(always)]
367     fn find_mir_or_eval_fn(
368         ecx: &mut InterpCx<'mir, 'tcx, Self>,
369         instance: ty::Instance<'tcx>,
370         abi: Abi,
371         args: &[OpTy<'tcx, Tag>],
372         ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
373         unwind: StackPopUnwind,
374     ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
375         ecx.find_mir_or_eval_fn(instance, abi, args, ret, unwind)
376     }
377
378     #[inline(always)]
379     fn call_extra_fn(
380         ecx: &mut InterpCx<'mir, 'tcx, Self>,
381         fn_val: Dlsym,
382         abi: Abi,
383         args: &[OpTy<'tcx, Tag>],
384         ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
385         _unwind: StackPopUnwind,
386     ) -> InterpResult<'tcx> {
387         ecx.call_dlsym(fn_val, abi, args, ret)
388     }
389
390     #[inline(always)]
391     fn call_intrinsic(
392         ecx: &mut rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
393         instance: ty::Instance<'tcx>,
394         args: &[OpTy<'tcx, Tag>],
395         ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
396         unwind: StackPopUnwind,
397     ) -> InterpResult<'tcx> {
398         ecx.call_intrinsic(instance, args, ret, unwind)
399     }
400
401     #[inline(always)]
402     fn assert_panic(
403         ecx: &mut InterpCx<'mir, 'tcx, Self>,
404         msg: &mir::AssertMessage<'tcx>,
405         unwind: Option<mir::BasicBlock>,
406     ) -> InterpResult<'tcx> {
407         ecx.assert_panic(msg, unwind)
408     }
409
410     #[inline(always)]
411     fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: String) -> InterpResult<'tcx, !> {
412         throw_machine_stop!(TerminationInfo::Abort(msg))
413     }
414
415     #[inline(always)]
416     fn binary_ptr_op(
417         ecx: &rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
418         bin_op: mir::BinOp,
419         left: &ImmTy<'tcx, Tag>,
420         right: &ImmTy<'tcx, Tag>,
421     ) -> InterpResult<'tcx, (Scalar<Tag>, bool, ty::Ty<'tcx>)> {
422         ecx.binary_ptr_op(bin_op, left, right)
423     }
424
425     fn box_alloc(
426         ecx: &mut InterpCx<'mir, 'tcx, Self>,
427         dest: &PlaceTy<'tcx, Tag>,
428     ) -> InterpResult<'tcx> {
429         trace!("box_alloc for {:?}", dest.layout.ty);
430         let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;
431         // First argument: `size`.
432         // (`0` is allowed here -- this is expected to be handled by the lang item).
433         let size = Scalar::from_machine_usize(layout.size.bytes(), ecx);
434
435         // Second argument: `align`.
436         let align = Scalar::from_machine_usize(layout.align.abi.bytes(), ecx);
437
438         // Call the `exchange_malloc` lang item.
439         let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
440         let malloc = ty::Instance::mono(ecx.tcx.tcx, malloc);
441         ecx.call_function(
442             malloc,
443             Abi::Rust,
444             &[size.into(), align.into()],
445             Some(dest),
446             // Don't do anything when we are done. The `statement()` function will increment
447             // the old stack frame's stmt counter to the next statement, which means that when
448             // `exchange_malloc` returns, we go on evaluating exactly where we want to be.
449             StackPopCleanup::None { cleanup: true },
450         )?;
451         Ok(())
452     }
453
454     fn thread_local_static_alloc_id(
455         ecx: &mut InterpCx<'mir, 'tcx, Self>,
456         def_id: DefId,
457     ) -> InterpResult<'tcx, AllocId> {
458         ecx.get_or_create_thread_local_alloc_id(def_id)
459     }
460
461     fn extern_static_alloc_id(
462         memory: &Memory<'mir, 'tcx, Self>,
463         def_id: DefId,
464     ) -> InterpResult<'tcx, AllocId> {
465         let attrs = memory.tcx.get_attrs(def_id);
466         let link_name = match memory.tcx.sess.first_attr_value_str_by_name(&attrs, sym::link_name) {
467             Some(name) => name,
468             None => memory.tcx.item_name(def_id),
469         };
470         if let Some(&id) = memory.extra.extern_statics.get(&link_name) {
471             Ok(id)
472         } else {
473             throw_unsup_format!("`extern` static {:?} is not supported by Miri", def_id)
474         }
475     }
476
477     fn init_allocation_extra<'b>(
478         memory_extra: &MemoryExtra,
479         id: AllocId,
480         alloc: Cow<'b, Allocation>,
481         kind: Option<MemoryKind<Self::MemoryKind>>,
482     ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag) {
483         if Some(id) == memory_extra.tracked_alloc_id {
484             register_diagnostic(NonHaltingDiagnostic::CreatedAlloc(id));
485         }
486
487         let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
488         let alloc = alloc.into_owned();
489         let (stacks, base_tag) = if let Some(stacked_borrows) = &memory_extra.stacked_borrows {
490             let (stacks, base_tag) =
491                 Stacks::new_allocation(id, alloc.size(), stacked_borrows, kind);
492             (Some(stacks), base_tag)
493         } else {
494             // No stacks, no tag.
495             (None, Tag::Untagged)
496         };
497         let race_alloc = if let Some(data_race) = &memory_extra.data_race {
498             Some(data_race::AllocExtra::new_allocation(&data_race, alloc.size(), kind))
499         } else {
500             None
501         };
502         let mut stacked_borrows = memory_extra.stacked_borrows.as_ref().map(|sb| sb.borrow_mut());
503         let alloc: Allocation<Tag, Self::AllocExtra> = alloc.with_tags_and_extra(
504             |alloc| {
505                 if let Some(stacked_borrows) = &mut stacked_borrows {
506                     // Only globals may already contain pointers at this point
507                     assert_eq!(kind, MiriMemoryKind::Global.into());
508                     stacked_borrows.global_base_ptr(alloc)
509                 } else {
510                     Tag::Untagged
511                 }
512             },
513             AllocExtra { stacked_borrows: stacks, data_race: race_alloc },
514         );
515         (Cow::Owned(alloc), base_tag)
516     }
517
518     #[inline(always)]
519     fn memory_read(
520         memory_extra: &Self::MemoryExtra,
521         alloc_extra: &AllocExtra,
522         ptr: Pointer<Tag>,
523         size: Size,
524     ) -> InterpResult<'tcx> {
525         if let Some(data_race) = &alloc_extra.data_race {
526             data_race.read(ptr, size, memory_extra.data_race.as_ref().unwrap())?;
527         }
528         if let Some(stacked_borrows) = &alloc_extra.stacked_borrows {
529             stacked_borrows.memory_read(ptr, size, memory_extra.stacked_borrows.as_ref().unwrap())
530         } else {
531             Ok(())
532         }
533     }
534
535     #[inline(always)]
536     fn memory_written(
537         memory_extra: &mut Self::MemoryExtra,
538         alloc_extra: &mut AllocExtra,
539         ptr: Pointer<Tag>,
540         size: Size,
541     ) -> InterpResult<'tcx> {
542         if let Some(data_race) = &mut alloc_extra.data_race {
543             data_race.write(ptr, size, memory_extra.data_race.as_mut().unwrap())?;
544         }
545         if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
546             stacked_borrows.memory_written(
547                 ptr,
548                 size,
549                 memory_extra.stacked_borrows.as_mut().unwrap(),
550             )
551         } else {
552             Ok(())
553         }
554     }
555
556     #[inline(always)]
557     fn memory_deallocated(
558         memory_extra: &mut Self::MemoryExtra,
559         alloc_extra: &mut AllocExtra,
560         ptr: Pointer<Tag>,
561         size: Size,
562     ) -> InterpResult<'tcx> {
563         if Some(ptr.alloc_id) == memory_extra.tracked_alloc_id {
564             register_diagnostic(NonHaltingDiagnostic::FreedAlloc(ptr.alloc_id));
565         }
566         if let Some(data_race) = &mut alloc_extra.data_race {
567             data_race.deallocate(ptr, size, memory_extra.data_race.as_mut().unwrap())?;
568         }
569         if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
570             stacked_borrows.memory_deallocated(
571                 ptr,
572                 size,
573                 memory_extra.stacked_borrows.as_mut().unwrap(),
574             )
575         } else {
576             Ok(())
577         }
578     }
579
580     fn after_static_mem_initialized(
581         ecx: &mut InterpCx<'mir, 'tcx, Self>,
582         ptr: Pointer<Self::PointerTag>,
583         size: Size,
584     ) -> InterpResult<'tcx> {
585         if ecx.memory.extra.data_race.is_some() {
586             ecx.reset_vector_clocks(ptr, size)?;
587         }
588         Ok(())
589     }
590
591     #[inline(always)]
592     fn tag_global_base_pointer(memory_extra: &MemoryExtra, id: AllocId) -> Self::PointerTag {
593         if let Some(stacked_borrows) = &memory_extra.stacked_borrows {
594             stacked_borrows.borrow_mut().global_base_ptr(id)
595         } else {
596             Tag::Untagged
597         }
598     }
599
600     #[inline(always)]
601     fn retag(
602         ecx: &mut InterpCx<'mir, 'tcx, Self>,
603         kind: mir::RetagKind,
604         place: &PlaceTy<'tcx, Tag>,
605     ) -> InterpResult<'tcx> {
606         if ecx.memory.extra.stacked_borrows.is_some() { ecx.retag(kind, place) } else { Ok(()) }
607     }
608
609     #[inline(always)]
610     fn init_frame_extra(
611         ecx: &mut InterpCx<'mir, 'tcx, Self>,
612         frame: Frame<'mir, 'tcx, Tag>,
613     ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> {
614         // Start recording our event before doing anything else
615         let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
616             let fn_name = frame.instance.to_string();
617             let entry = ecx.machine.string_cache.entry(fn_name.clone());
618             let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
619
620             Some(profiler.start_recording_interval_event_detached(
621                 *name,
622                 EventId::from_label(*name),
623                 ecx.get_active_thread().to_u32(),
624             ))
625         } else {
626             None
627         };
628
629         let stacked_borrows = ecx.memory.extra.stacked_borrows.as_ref();
630         let call_id = stacked_borrows.map_or(NonZeroU64::new(1).unwrap(), |stacked_borrows| {
631             stacked_borrows.borrow_mut().new_call()
632         });
633
634         let extra = FrameData { call_id, catch_unwind: None, timing };
635         Ok(frame.with_extra(extra))
636     }
637
638     fn stack<'a>(
639         ecx: &'a InterpCx<'mir, 'tcx, Self>,
640     ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
641         ecx.active_thread_stack()
642     }
643
644     fn stack_mut<'a>(
645         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
646     ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
647         ecx.active_thread_stack_mut()
648     }
649
650     #[inline(always)]
651     fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
652         if ecx.memory.extra.stacked_borrows.is_some() { ecx.retag_return_place() } else { Ok(()) }
653     }
654
655     #[inline(always)]
656     fn after_stack_pop(
657         ecx: &mut InterpCx<'mir, 'tcx, Self>,
658         mut frame: Frame<'mir, 'tcx, Tag, FrameData<'tcx>>,
659         unwinding: bool,
660     ) -> InterpResult<'tcx, StackPopJump> {
661         let timing = frame.extra.timing.take();
662         let res = ecx.handle_stack_pop(frame.extra, unwinding);
663         if let Some(profiler) = ecx.machine.profiler.as_ref() {
664             profiler.finish_recording_interval_event(timing.unwrap());
665         }
666         res
667     }
668
669     #[inline(always)]
670     fn int_to_ptr(
671         memory: &Memory<'mir, 'tcx, Self>,
672         int: u64,
673     ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
674         intptrcast::GlobalState::int_to_ptr(int, memory)
675     }
676
677     #[inline(always)]
678     fn ptr_to_int(
679         memory: &Memory<'mir, 'tcx, Self>,
680         ptr: Pointer<Self::PointerTag>,
681     ) -> InterpResult<'tcx, u64> {
682         intptrcast::GlobalState::ptr_to_int(ptr, memory)
683     }
684 }