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