]> git.lizzy.rs Git - rust.git/blobdiff - src/machine.rs
rename track-raw-pointers flag to tag-raw-pointers
[rust.git] / src / machine.rs
index ebe3c509ad874371afe96af4712ab594dd5fef93..201854e76fa46052bfed004367fb6fc4f7dd47de 100644 (file)
@@ -3,26 +3,27 @@
 
 use std::borrow::Cow;
 use std::cell::RefCell;
+use std::fmt;
 use std::num::NonZeroU64;
-use std::rc::Rc;
 use std::time::Instant;
-use std::fmt;
 
 use log::trace;
 use rand::rngs::StdRng;
+use rand::SeedableRng;
 
 use rustc_data_structures::fx::FxHashMap;
 use rustc_middle::{
     mir,
     ty::{
         self,
-        layout::{LayoutCx, LayoutError, TyAndLayout},
-        TyCtxt,
+        layout::{LayoutCx, LayoutError, LayoutOf, TyAndLayout},
+        Instance, TyCtxt,
     },
 };
-use rustc_span::symbol::{sym, Symbol};
 use rustc_span::def_id::DefId;
-use rustc_target::abi::{LayoutOf, Size};
+use rustc_span::symbol::{sym, Symbol};
+use rustc_target::abi::Size;
+use rustc_target::spec::abi::Abi;
 
 use crate::*;
 
@@ -33,7 +34,6 @@
 pub const NUM_CPUS: u64 = 1;
 
 /// Extra data stored with each stack frame
-#[derive(Debug)]
 pub struct FrameData<'tcx> {
     /// Extra data for Stacked Borrows.
     pub call_id: stacked_borrows::CallId,
@@ -42,6 +42,22 @@ pub struct FrameData<'tcx> {
     /// called by `try`). When this frame is popped during unwinding a panic,
     /// we stop unwinding, use the `CatchUnwindData` to handle catching.
     pub catch_unwind: Option<CatchUnwindData<'tcx>>,
+
+    /// If `measureme` profiling is enabled, holds timing information
+    /// for the start of this frame. When we finish executing this frame,
+    /// we use this to register a completed event with `measureme`.
+    pub timing: Option<measureme::DetachedTiming>,
+}
+
+impl<'tcx> std::fmt::Debug for FrameData<'tcx> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        // Omitting `timing`, it does not support `Debug`.
+        let FrameData { call_id, catch_unwind, timing: _ } = self;
+        f.debug_struct("FrameData")
+            .field("call_id", call_id)
+            .field("catch_unwind", catch_unwind)
+            .finish()
+    }
 }
 
 /// Extra memory kinds
@@ -98,26 +114,63 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
             Env => write!(f, "environment variable"),
             Global => write!(f, "global (static or const)"),
             ExternStatic => write!(f, "extern static"),
-            Tls =>  write!(f, "thread-local static"),
+            Tls => write!(f, "thread-local static"),
         }
     }
 }
 
+/// Pointer provenance (tag).
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct Tag {
+    pub alloc_id: AllocId,
+    /// Stacked Borrows tag.
+    pub sb: SbTag,
+}
+
+impl Provenance for Tag {
+    /// We use absolute addresses in the `offset` of a `Pointer<Tag>`.
+    const OFFSET_IS_ADDR: bool = true;
+
+    /// We cannot err on partial overwrites, it happens too often in practice (due to unions).
+    const ERR_ON_PARTIAL_PTR_OVERWRITE: bool = false;
+
+    fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let (tag, addr) = ptr.into_parts(); // address is absolute
+        write!(f, "0x{:x}", addr.bytes())?;
+        // Forward `alternate` flag to `alloc_id` printing.
+        if f.alternate() {
+            write!(f, "[{:#?}]", tag.alloc_id)?;
+        } else {
+            write!(f, "[{:?}]", tag.alloc_id)?;
+        }
+        // Print Stacked Borrows tag.
+        write!(f, "{:?}", tag.sb)
+    }
+
+    fn get_alloc_id(self) -> AllocId {
+        self.alloc_id
+    }
+}
+
 /// Extra per-allocation data
 #[derive(Debug, Clone)]
 pub struct AllocExtra {
     /// Stacked Borrows state is only added if it is enabled.
     pub stacked_borrows: Option<stacked_borrows::AllocExtra>,
+    /// Data race detection via the use of a vector-clock,
+    ///  this is only added if it is enabled.
+    pub data_race: Option<data_race::AllocExtra>,
 }
 
 /// Extra global memory data
-#[derive(Clone, Debug)]
+#[derive(Debug)]
 pub struct MemoryExtra {
     pub stacked_borrows: Option<stacked_borrows::MemoryExtra>,
+    pub data_race: Option<data_race::MemoryExtra>,
     pub intptrcast: intptrcast::MemoryExtra,
 
-    /// Mapping extern static names to their canonical allocation.
-    extern_statics: FxHashMap<Symbol, AllocId>,
+    /// Mapping extern static names to their base pointer.
+    extern_statics: FxHashMap<Symbol, Pointer<Tag>>,
 
     /// The random number generator used for resolving non-determinism.
     /// Needs to be queried by ptr_to_int, hence needs interior mutability.
@@ -129,67 +182,71 @@ pub struct MemoryExtra {
 
     /// Controls whether alignment of memory accesses is being checked.
     pub(crate) check_alignment: AlignmentCheck,
+
+    /// Failure rate of compare_exchange_weak, between 0.0 and 1.0
+    pub(crate) cmpxchg_weak_failure_rate: f64,
 }
 
 impl MemoryExtra {
-    pub fn new(
-        rng: StdRng,
-        stacked_borrows: bool,
-        tracked_pointer_tag: Option<PtrId>,
-        tracked_call_id: Option<CallId>,
-        tracked_alloc_id: Option<AllocId>,
-        check_alignment: AlignmentCheck,
-    ) -> Self {
-        let stacked_borrows = if stacked_borrows {
-            Some(Rc::new(RefCell::new(stacked_borrows::GlobalState::new(tracked_pointer_tag, tracked_call_id))))
+    pub fn new(config: &MiriConfig) -> Self {
+        let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
+        let stacked_borrows = if config.stacked_borrows {
+            Some(RefCell::new(stacked_borrows::GlobalState::new(
+                config.tracked_pointer_tag,
+                config.tracked_call_id,
+                config.tag_raw,
+            )))
         } else {
             None
         };
+        let data_race =
+            if config.data_race_detector { Some(data_race::GlobalState::new()) } else { None };
         MemoryExtra {
             stacked_borrows,
+            data_race,
             intptrcast: Default::default(),
             extern_statics: FxHashMap::default(),
             rng: RefCell::new(rng),
-            tracked_alloc_id,
-            check_alignment,
+            tracked_alloc_id: config.tracked_alloc_id,
+            check_alignment: config.check_alignment,
+            cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
         }
     }
 
     fn add_extern_static<'tcx, 'mir>(
         this: &mut MiriEvalContext<'mir, 'tcx>,
         name: &str,
-        ptr: Scalar<Tag>,
+        ptr: Pointer<Option<Tag>>,
     ) {
-        let ptr = ptr.assert_ptr();
-        assert_eq!(ptr.offset, Size::ZERO);
-        this.memory
-            .extra
-            .extern_statics
-            .insert(Symbol::intern(name), ptr.alloc_id)
-            .unwrap_none();
+        let ptr = ptr.into_pointer_or_addr().unwrap();
+        this.memory.extra.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
     }
 
     /// Sets up the "extern statics" for this machine.
     pub fn init_extern_statics<'tcx, 'mir>(
         this: &mut MiriEvalContext<'mir, 'tcx>,
     ) -> InterpResult<'tcx> {
-        match this.tcx.sess.target.target.target_os.as_str() {
+        match this.tcx.sess.target.os.as_str() {
             "linux" => {
                 // "__cxa_thread_atexit_impl"
                 // This should be all-zero, pointer-sized.
                 let layout = this.machine.layouts.usize;
-                let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into());
-                this.write_scalar(Scalar::from_machine_usize(0, this), place.into())?;
+                let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into())?;
+                this.write_scalar(Scalar::from_machine_usize(0, this), &place.into())?;
                 Self::add_extern_static(this, "__cxa_thread_atexit_impl", place.ptr);
                 // "environ"
-                Self::add_extern_static(this, "environ", this.machine.env_vars.environ.unwrap().ptr);
+                Self::add_extern_static(
+                    this,
+                    "environ",
+                    this.machine.env_vars.environ.unwrap().ptr,
+                );
             }
             "windows" => {
                 // "_tls_used"
                 // This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
                 let layout = this.machine.layouts.u8;
-                let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into());
-                this.write_scalar(Scalar::from_u8(0), place.into())?;
+                let place = this.allocate(layout, MiriMemoryKind::ExternStatic.into())?;
+                this.write_scalar(Scalar::from_u8(0), &place.into())?;
                 Self::add_extern_static(this, "_tls_used", place.ptr);
             }
             _ => {} // No "extern statics" supported on this target
@@ -232,23 +289,27 @@ pub struct Evaluator<'mir, 'tcx> {
     /// Program arguments (`Option` because we can only initialize them after creating the ecx).
     /// These are *pointers* to argc/argv because macOS.
     /// We also need the full command line as one string because of Windows.
-    pub(crate) argc: Option<Scalar<Tag>>,
-    pub(crate) argv: Option<Scalar<Tag>>,
-    pub(crate) cmd_line: Option<Scalar<Tag>>,
-
-    /// Last OS error location in memory. It is a 32-bit integer.
-    pub(crate) last_error: Option<MPlaceTy<'tcx, Tag>>,
+    pub(crate) argc: Option<MemPlace<Tag>>,
+    pub(crate) argv: Option<MemPlace<Tag>>,
+    pub(crate) cmd_line: Option<MemPlace<Tag>>,
 
     /// TLS state.
     pub(crate) tls: TlsData<'tcx>,
 
-    /// If enabled, the `env_vars` field is populated with the host env vars during initialization
-    /// and random number generation is delegated to the host.
-    pub(crate) communicate: bool,
+    /// What should Miri do when an op requires communicating with the host,
+    /// such as accessing host env vars, random number generation, and
+    /// file system access.
+    pub(crate) isolated_op: IsolatedOp,
 
     /// Whether to enforce the validity invariant.
     pub(crate) validate: bool,
 
+    /// Whether to enforce validity (e.g., initialization) of integers and floats.
+    pub(crate) enforce_number_validity: bool,
+
+    /// Whether to enforce [ABI](Abi) of function calls.
+    pub(crate) enforce_abi: bool,
+
     pub(crate) file_handler: shims::posix::FileHandler,
     pub(crate) dir_handler: shims::posix::DirHandler,
 
@@ -263,16 +324,31 @@ pub struct Evaluator<'mir, 'tcx> {
 
     /// Allocations that are considered roots of static memory (that may leak).
     pub(crate) static_roots: Vec<AllocId>,
+
+    /// The `measureme` profiler used to record timing information about
+    /// the emulated program.
+    profiler: Option<measureme::Profiler>,
+    /// Used with `profiler` to cache the `StringId`s for event names
+    /// uesd with `measureme`.
+    string_cache: FxHashMap<String, measureme::StringId>,
+
+    /// Cache of `Instance` exported under the given `Symbol` name.
+    /// `None` means no `Instance` exported under the given name is found.
+    pub(crate) exported_symbols_cache: FxHashMap<Symbol, Option<Instance<'tcx>>>,
+
+    /// Whether to raise a panic in the context of the evaluated process when unsupported
+    /// functionality is encountered. If `false`, an error is propagated in the Miri application context
+    /// instead (default behavior)
+    pub(crate) panic_on_unsupported: bool,
 }
 
 impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
-    pub(crate) fn new(
-        communicate: bool,
-        validate: bool,
-        layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>,
-    ) -> Self {
-        let layouts = PrimitiveLayouts::new(layout_cx)
-            .expect("Couldn't get layouts of primitive types");
+    pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self {
+        let layouts =
+            PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
+        let profiler = config.measureme_out.as_ref().map(|out| {
+            measureme::Profiler::new(out).expect("Couldn't create `measureme` profiler")
+        });
         Evaluator {
             // `env_vars` could be initialized properly here if `Memory` were available before
             // calling this method.
@@ -280,18 +356,27 @@ pub(crate) fn new(
             argc: None,
             argv: None,
             cmd_line: None,
-            last_error: None,
             tls: TlsData::default(),
-            communicate,
-            validate,
+            isolated_op: config.isolated_op,
+            validate: config.validate,
+            enforce_number_validity: config.check_number_validity,
+            enforce_abi: config.check_abi,
             file_handler: Default::default(),
             dir_handler: Default::default(),
             time_anchor: Instant::now(),
             layouts,
             threads: ThreadManager::default(),
             static_roots: Vec::new(),
+            profiler,
+            string_cache: Default::default(),
+            exported_symbols_cache: FxHashMap::default(),
+            panic_on_unsupported: config.panic_on_unsupported,
         }
     }
+
+    pub(crate) fn communicate(&self) -> bool {
+        self.isolated_op == IsolatedOp::Allow
+    }
 }
 
 /// A rustc InterpCx for Miri.
@@ -328,6 +413,8 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
 
     const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
 
+    const PANIC_ON_ALLOC_FAIL: bool = false;
+
     #[inline(always)]
     fn enforce_alignment(memory_extra: &MemoryExtra) -> bool {
         memory_extra.check_alignment != AlignmentCheck::None
@@ -343,35 +430,47 @@ fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
         ecx.machine.validate
     }
 
+    #[inline(always)]
+    fn enforce_number_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+        ecx.machine.enforce_number_validity
+    }
+
+    #[inline(always)]
+    fn enforce_abi(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+        ecx.machine.enforce_abi
+    }
+
     #[inline(always)]
     fn find_mir_or_eval_fn(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
         instance: ty::Instance<'tcx>,
+        abi: Abi,
         args: &[OpTy<'tcx, Tag>],
-        ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
-        unwind: Option<mir::BasicBlock>,
+        ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
+        unwind: StackPopUnwind,
     ) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>> {
-        ecx.find_mir_or_eval_fn(instance, args, ret, unwind)
+        ecx.find_mir_or_eval_fn(instance, abi, args, ret, unwind)
     }
 
     #[inline(always)]
     fn call_extra_fn(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
         fn_val: Dlsym,
+        abi: Abi,
         args: &[OpTy<'tcx, Tag>],
-        ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
-        _unwind: Option<mir::BasicBlock>,
+        ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
+        _unwind: StackPopUnwind,
     ) -> InterpResult<'tcx> {
-        ecx.call_dlsym(fn_val, args, ret)
+        ecx.call_dlsym(fn_val, abi, args, ret)
     }
 
     #[inline(always)]
     fn call_intrinsic(
-        ecx: &mut rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
+        ecx: &mut rustc_const_eval::interpret::InterpCx<'mir, 'tcx, Self>,
         instance: ty::Instance<'tcx>,
         args: &[OpTy<'tcx, Tag>],
-        ret: Option<(PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
-        unwind: Option<mir::BasicBlock>,
+        ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
+        unwind: StackPopUnwind,
     ) -> InterpResult<'tcx> {
         ecx.call_intrinsic(instance, args, ret, unwind)
     }
@@ -386,23 +485,23 @@ fn assert_panic(
     }
 
     #[inline(always)]
-    fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, !> {
-        throw_machine_stop!(TerminationInfo::Abort(None))
+    fn abort(_ecx: &mut InterpCx<'mir, 'tcx, Self>, msg: String) -> InterpResult<'tcx, !> {
+        throw_machine_stop!(TerminationInfo::Abort(msg))
     }
 
     #[inline(always)]
     fn binary_ptr_op(
-        ecx: &rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
+        ecx: &rustc_const_eval::interpret::InterpCx<'mir, 'tcx, Self>,
         bin_op: mir::BinOp,
-        left: ImmTy<'tcx, Tag>,
-        right: ImmTy<'tcx, Tag>,
+        left: &ImmTy<'tcx, Tag>,
+        right: &ImmTy<'tcx, Tag>,
     ) -> InterpResult<'tcx, (Scalar<Tag>, bool, ty::Ty<'tcx>)> {
         ecx.binary_ptr_op(bin_op, left, right)
     }
 
     fn box_alloc(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        dest: PlaceTy<'tcx, Tag>,
+        dest: &PlaceTy<'tcx, Tag>,
     ) -> InterpResult<'tcx> {
         trace!("box_alloc for {:?}", dest.layout.ty);
         let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;
@@ -418,6 +517,7 @@ fn box_alloc(
         let malloc = ty::Instance::mono(ecx.tcx.tcx, malloc);
         ecx.call_function(
             malloc,
+            Abi::Rust,
             &[size.into(), align.into()],
             Some(dest),
             // Don't do anything when we are done. The `statement()` function will increment
@@ -428,84 +528,156 @@ fn box_alloc(
         Ok(())
     }
 
-    fn thread_local_static_alloc_id(
+    fn thread_local_static_base_pointer(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
         def_id: DefId,
-    ) -> InterpResult<'tcx, AllocId> {
-        ecx.get_or_create_thread_local_alloc_id(def_id)
+    ) -> InterpResult<'tcx, Pointer<Tag>> {
+        ecx.get_or_create_thread_local_alloc(def_id)
     }
 
-    fn extern_static_alloc_id(
+    fn extern_static_base_pointer(
         memory: &Memory<'mir, 'tcx, Self>,
         def_id: DefId,
-    ) -> InterpResult<'tcx, AllocId> {
+    ) -> InterpResult<'tcx, Pointer<Tag>> {
         let attrs = memory.tcx.get_attrs(def_id);
         let link_name = match memory.tcx.sess.first_attr_value_str_by_name(&attrs, sym::link_name) {
             Some(name) => name,
             None => memory.tcx.item_name(def_id),
         };
-        if let Some(&id) = memory.extra.extern_statics.get(&link_name) {
-            Ok(id)
+        if let Some(&ptr) = memory.extra.extern_statics.get(&link_name) {
+            Ok(ptr)
         } else {
             throw_unsup_format!("`extern` static {:?} is not supported by Miri", def_id)
         }
     }
 
     fn init_allocation_extra<'b>(
-        memory_extra: &MemoryExtra,
+        mem: &Memory<'mir, 'tcx, Self>,
         id: AllocId,
         alloc: Cow<'b, Allocation>,
         kind: Option<MemoryKind<Self::MemoryKind>>,
-    ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag) {
-        if Some(id) == memory_extra.tracked_alloc_id {
+    ) -> Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>> {
+        if Some(id) == mem.extra.tracked_alloc_id {
             register_diagnostic(NonHaltingDiagnostic::CreatedAlloc(id));
         }
 
         let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
         let alloc = alloc.into_owned();
-        let (stacks, base_tag) =
-            if let Some(stacked_borrows) = &memory_extra.stacked_borrows {
-                let (stacks, base_tag) =
-                    Stacks::new_allocation(id, alloc.size, Rc::clone(stacked_borrows), kind);
-                (Some(stacks), base_tag)
-            } else {
-                // No stacks, no tag.
-                (None, Tag::Untagged)
-            };
-        let mut stacked_borrows = memory_extra.stacked_borrows.as_ref().map(|sb| sb.borrow_mut());
-        let alloc: Allocation<Tag, Self::AllocExtra> = alloc.with_tags_and_extra(
-            |alloc| {
-                if let Some(stacked_borrows) = &mut stacked_borrows {
-                    // Only globals may already contain pointers at this point
-                    assert_eq!(kind, MiriMemoryKind::Global.into());
-                    stacked_borrows.global_base_ptr(alloc)
-                } else {
-                    Tag::Untagged
-                }
-            },
-            AllocExtra { stacked_borrows: stacks },
+        let stacks = if let Some(stacked_borrows) = &mem.extra.stacked_borrows {
+            Some(Stacks::new_allocation(id, alloc.size(), stacked_borrows, kind))
+        } else {
+            None
+        };
+        let race_alloc = if let Some(data_race) = &mem.extra.data_race {
+            Some(data_race::AllocExtra::new_allocation(&data_race, alloc.size(), kind))
+        } else {
+            None
+        };
+        let alloc: Allocation<Tag, Self::AllocExtra> = alloc.convert_tag_add_extra(
+            &mem.tcx,
+            AllocExtra { stacked_borrows: stacks, data_race: race_alloc },
+            |ptr| Evaluator::tag_alloc_base_pointer(mem, ptr),
         );
-        (Cow::Owned(alloc), base_tag)
+        Cow::Owned(alloc)
+    }
+
+    fn tag_alloc_base_pointer(
+        mem: &Memory<'mir, 'tcx, Self>,
+        ptr: Pointer<AllocId>,
+    ) -> Pointer<Tag> {
+        let absolute_addr = intptrcast::GlobalState::rel_ptr_to_addr(&mem, ptr);
+        let sb_tag = if let Some(stacked_borrows) = &mem.extra.stacked_borrows {
+            stacked_borrows.borrow_mut().base_tag(ptr.provenance)
+        } else {
+            SbTag::Untagged
+        };
+        Pointer::new(Tag { alloc_id: ptr.provenance, sb: sb_tag }, Size::from_bytes(absolute_addr))
     }
 
     #[inline(always)]
-    fn before_deallocation(
-        memory_extra: &mut Self::MemoryExtra,
-        id: AllocId,
+    fn ptr_from_addr(
+        mem: &Memory<'mir, 'tcx, Self>,
+        addr: u64,
+    ) -> Pointer<Option<Self::PointerTag>> {
+        intptrcast::GlobalState::ptr_from_addr(addr, mem)
+    }
+
+    /// Convert a pointer with provenance into an allocation-offset pair,
+    /// or a `None` with an absolute address if that conversion is not possible.
+    fn ptr_get_alloc(
+        mem: &Memory<'mir, 'tcx, Self>,
+        ptr: Pointer<Self::PointerTag>,
+    ) -> (AllocId, Size) {
+        let rel = intptrcast::GlobalState::abs_ptr_to_rel(mem, ptr);
+        (ptr.provenance.alloc_id, rel)
+    }
+
+    #[inline(always)]
+    fn memory_read(
+        memory_extra: &Self::MemoryExtra,
+        alloc_extra: &AllocExtra,
+        tag: Tag,
+        range: AllocRange,
     ) -> InterpResult<'tcx> {
-        if Some(id) == memory_extra.tracked_alloc_id {
-            register_diagnostic(NonHaltingDiagnostic::FreedAlloc(id));
+        if let Some(data_race) = &alloc_extra.data_race {
+            data_race.read(tag.alloc_id, range, memory_extra.data_race.as_ref().unwrap())?;
+        }
+        if let Some(stacked_borrows) = &alloc_extra.stacked_borrows {
+            stacked_borrows.memory_read(
+                tag.alloc_id,
+                tag.sb,
+                range,
+                memory_extra.stacked_borrows.as_ref().unwrap(),
+            )
+        } else {
+            Ok(())
         }
+    }
 
-        Ok(())
+    #[inline(always)]
+    fn memory_written(
+        memory_extra: &mut Self::MemoryExtra,
+        alloc_extra: &mut AllocExtra,
+        tag: Tag,
+        range: AllocRange,
+    ) -> InterpResult<'tcx> {
+        if let Some(data_race) = &mut alloc_extra.data_race {
+            data_race.write(tag.alloc_id, range, memory_extra.data_race.as_mut().unwrap())?;
+        }
+        if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
+            stacked_borrows.memory_written(
+                tag.alloc_id,
+                tag.sb,
+                range,
+                memory_extra.stacked_borrows.as_mut().unwrap(),
+            )
+        } else {
+            Ok(())
+        }
     }
 
     #[inline(always)]
-    fn tag_global_base_pointer(memory_extra: &MemoryExtra, id: AllocId) -> Self::PointerTag {
-        if let Some(stacked_borrows) = &memory_extra.stacked_borrows {
-            stacked_borrows.borrow_mut().global_base_ptr(id)
+    fn memory_deallocated(
+        memory_extra: &mut Self::MemoryExtra,
+        alloc_extra: &mut AllocExtra,
+        tag: Tag,
+        range: AllocRange,
+    ) -> InterpResult<'tcx> {
+        if Some(tag.alloc_id) == memory_extra.tracked_alloc_id {
+            register_diagnostic(NonHaltingDiagnostic::FreedAlloc(tag.alloc_id));
+        }
+        if let Some(data_race) = &mut alloc_extra.data_race {
+            data_race.deallocate(tag.alloc_id, range, memory_extra.data_race.as_mut().unwrap())?;
+        }
+        if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
+            stacked_borrows.memory_deallocated(
+                tag.alloc_id,
+                tag.sb,
+                range,
+                memory_extra.stacked_borrows.as_mut().unwrap(),
+            )
         } else {
-            Tag::Untagged
+            Ok(())
         }
     }
 
@@ -513,13 +685,9 @@ fn tag_global_base_pointer(memory_extra: &MemoryExtra, id: AllocId) -> Self::Poi
     fn retag(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
         kind: mir::RetagKind,
-        place: PlaceTy<'tcx, Tag>,
+        place: &PlaceTy<'tcx, Tag>,
     ) -> InterpResult<'tcx> {
-        if ecx.memory.extra.stacked_borrows.is_some() {
-            ecx.retag(kind, place)
-        } else {
-            Ok(())
-        }
+        if ecx.memory.extra.stacked_borrows.is_some() { ecx.retag(kind, place) } else { Ok(()) }
     }
 
     #[inline(always)]
@@ -527,98 +695,58 @@ fn init_frame_extra(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
         frame: Frame<'mir, 'tcx, Tag>,
     ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> {
+        // Start recording our event before doing anything else
+        let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
+            let fn_name = frame.instance.to_string();
+            let entry = ecx.machine.string_cache.entry(fn_name.clone());
+            let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
+
+            Some(profiler.start_recording_interval_event_detached(
+                *name,
+                measureme::EventId::from_label(*name),
+                ecx.get_active_thread().to_u32(),
+            ))
+        } else {
+            None
+        };
+
         let stacked_borrows = ecx.memory.extra.stacked_borrows.as_ref();
         let call_id = stacked_borrows.map_or(NonZeroU64::new(1).unwrap(), |stacked_borrows| {
             stacked_borrows.borrow_mut().new_call()
         });
-        let extra = FrameData { call_id, catch_unwind: None };
+
+        let extra = FrameData { call_id, catch_unwind: None, timing };
         Ok(frame.with_extra(extra))
     }
 
     fn stack<'a>(
-        ecx: &'a InterpCx<'mir, 'tcx, Self>
+        ecx: &'a InterpCx<'mir, 'tcx, Self>,
     ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
         ecx.active_thread_stack()
     }
 
     fn stack_mut<'a>(
-        ecx: &'a mut InterpCx<'mir, 'tcx, Self>
+        ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
     ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
         ecx.active_thread_stack_mut()
     }
 
     #[inline(always)]
     fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
-        if ecx.memory.extra.stacked_borrows.is_some() {
-            ecx.retag_return_place()
-        } else {
-            Ok(())
-        }
+        if ecx.memory.extra.stacked_borrows.is_some() { ecx.retag_return_place() } else { Ok(()) }
     }
 
     #[inline(always)]
     fn after_stack_pop(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        frame: Frame<'mir, 'tcx, Tag, FrameData<'tcx>>,
+        mut frame: Frame<'mir, 'tcx, Tag, FrameData<'tcx>>,
         unwinding: bool,
     ) -> InterpResult<'tcx, StackPopJump> {
-        ecx.handle_stack_pop(frame.extra, unwinding)
-    }
-
-    #[inline(always)]
-    fn int_to_ptr(
-        memory: &Memory<'mir, 'tcx, Self>,
-        int: u64,
-    ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
-        intptrcast::GlobalState::int_to_ptr(int, memory)
-    }
-
-    #[inline(always)]
-    fn ptr_to_int(
-        memory: &Memory<'mir, 'tcx, Self>,
-        ptr: Pointer<Self::PointerTag>,
-    ) -> InterpResult<'tcx, u64> {
-        intptrcast::GlobalState::ptr_to_int(ptr, memory)
-    }
-}
-
-impl AllocationExtra<Tag> for AllocExtra {
-    #[inline(always)]
-    fn memory_read<'tcx>(
-        alloc: &Allocation<Tag, AllocExtra>,
-        ptr: Pointer<Tag>,
-        size: Size,
-    ) -> InterpResult<'tcx> {
-        if let Some(stacked_borrows) = &alloc.extra.stacked_borrows {
-            stacked_borrows.memory_read(ptr, size)
-        } else {
-            Ok(())
-        }
-    }
-
-    #[inline(always)]
-    fn memory_written<'tcx>(
-        alloc: &mut Allocation<Tag, AllocExtra>,
-        ptr: Pointer<Tag>,
-        size: Size,
-    ) -> InterpResult<'tcx> {
-        if let Some(stacked_borrows) = &mut alloc.extra.stacked_borrows {
-            stacked_borrows.memory_written(ptr, size)
-        } else {
-            Ok(())
-        }
-    }
-
-    #[inline(always)]
-    fn memory_deallocated<'tcx>(
-        alloc: &mut Allocation<Tag, AllocExtra>,
-        ptr: Pointer<Tag>,
-        size: Size,
-    ) -> InterpResult<'tcx> {
-        if let Some(stacked_borrows) = &mut alloc.extra.stacked_borrows {
-            stacked_borrows.memory_deallocated(ptr, size)
-        } else {
-            Ok(())
+        let timing = frame.extra.timing.take();
+        let res = ecx.handle_stack_pop(frame.extra, unwinding);
+        if let Some(profiler) = ecx.machine.profiler.as_ref() {
+            profiler.finish_recording_interval_event(timing.unwrap());
         }
+        res
     }
 }