]> git.lizzy.rs Git - rust.git/blobdiff - src/machine.rs
check for extern static size mismatches
[rust.git] / src / machine.rs
index 0972699e7288666450c444fafc72275f6b372775..67a6c997e99027e71a6e5d67d2d5acd22e7c8305 100644 (file)
@@ -5,7 +5,6 @@
 use std::cell::RefCell;
 use std::collections::HashSet;
 use std::fmt;
-use std::num::NonZeroU64;
 use std::time::Instant;
 
 use rand::rngs::StdRng;
 use rustc_target::abi::Size;
 use rustc_target::spec::abi::Abi;
 
-use crate::{shims::posix::FileHandler, *};
+use crate::{
+    concurrency::{data_race, weak_memory},
+    shims::unix::FileHandler,
+    *,
+};
 
 // Some global facts about the emulated machine.
 pub const PAGE_SIZE: u64 = 4 * 1024; // FIXME: adjust to target architecture
@@ -39,7 +42,7 @@
 /// Extra data stored with each stack frame
 pub struct FrameData<'tcx> {
     /// Extra data for Stacked Borrows.
-    pub call_id: stacked_borrows::CallId,
+    pub stacked_borrows: Option<stacked_borrows::FrameExtra>,
 
     /// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn`
     /// called by `try`). When this frame is popped during unwinding a panic,
@@ -55,9 +58,9 @@ pub struct FrameData<'tcx> {
 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;
+        let FrameData { stacked_borrows, catch_unwind, timing: _ } = self;
         f.debug_struct("FrameData")
-            .field("call_id", call_id)
+            .field("stacked_borrows", stacked_borrows)
             .field("catch_unwind", catch_unwind)
             .finish()
     }
@@ -89,10 +92,10 @@ pub enum MiriMemoryKind {
     Tls,
 }
 
-impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
+impl From<MiriMemoryKind> for MemoryKind<MiriMemoryKind> {
     #[inline(always)]
-    fn into(self) -> MemoryKind<MiriMemoryKind> {
-        MemoryKind::Machine(self)
+    fn from(kind: MiriMemoryKind) -> MemoryKind<MiriMemoryKind> {
+        MemoryKind::Machine(kind)
     }
 }
 
@@ -123,51 +126,56 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     }
 }
 
-/// Pointer provenance (tag).
+/// Pointer provenance.
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum Tag {
-    Concrete(ConcreteTag),
+pub enum Provenance {
+    Concrete {
+        alloc_id: AllocId,
+        /// Stacked Borrows tag.
+        sb: SbTag,
+    },
     Wildcard,
 }
 
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub struct ConcreteTag {
-    pub alloc_id: AllocId,
-    /// Stacked Borrows tag.
-    pub sb: SbTag,
+/// The "extra" information a pointer has over a regular AllocId.
+#[derive(Copy, Clone)]
+pub enum ProvenanceExtra {
+    Concrete(SbTag),
+    Wildcard,
 }
 
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-static_assert_size!(Pointer<Tag>, 24);
+static_assert_size!(Pointer<Provenance>, 24);
+// FIXME: this would with in 24bytes but layout optimizations are not smart enough
 // #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-// static_assert_size!(Pointer<Option<Tag>>, 24);
+//static_assert_size!(Pointer<Option<Provenance>>, 24);
 #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-static_assert_size!(ScalarMaybeUninit<Tag>, 32);
+static_assert_size!(ScalarMaybeUninit<Provenance>, 32);
 
-impl Provenance for Tag {
-    /// We use absolute addresses in the `offset` of a `Pointer<Tag>`.
+impl interpret::Provenance for Provenance {
+    /// We use absolute addresses in the `offset` of a `Pointer<Provenance>`.
     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())?;
+        let (prov, addr) = ptr.into_parts(); // address is absolute
+        write!(f, "{:#x}", addr.bytes())?;
 
-        match tag {
-            Tag::Concrete(tag) => {
+        match prov {
+            Provenance::Concrete { alloc_id, sb } => {
                 // Forward `alternate` flag to `alloc_id` printing.
                 if f.alternate() {
-                    write!(f, "[{:#?}]", tag.alloc_id)?;
+                    write!(f, "[{:#?}]", alloc_id)?;
                 } else {
-                    write!(f, "[{:?}]", tag.alloc_id)?;
+                    write!(f, "[{:?}]", alloc_id)?;
                 }
                 // Print Stacked Borrows tag.
-                write!(f, "{:?}", tag.sb)?;
+                write!(f, "{:?}", sb)?;
             }
-            Tag::Wildcard => {
-                write!(f, "[Wildcard]")?;
+            Provenance::Wildcard => {
+                write!(f, "[wildcard]")?;
             }
         }
 
@@ -176,8 +184,26 @@ fn fmt(ptr: &Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 
     fn get_alloc_id(self) -> Option<AllocId> {
         match self {
-            Tag::Concrete(concrete) => Some(concrete.alloc_id),
-            Tag::Wildcard => None,
+            Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
+            Provenance::Wildcard => None,
+        }
+    }
+}
+
+impl fmt::Debug for ProvenanceExtra {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"),
+            ProvenanceExtra::Wildcard => write!(f, "<wildcard>"),
+        }
+    }
+}
+
+impl ProvenanceExtra {
+    pub fn and_then<T>(self, f: impl FnOnce(SbTag) -> Option<T>) -> Option<T> {
+        match self {
+            ProvenanceExtra::Concrete(pid) => f(pid),
+            ProvenanceExtra::Wildcard => None,
         }
     }
 }
@@ -190,6 +216,9 @@ pub struct 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>,
+    /// Weak memory emulation via the use of store buffers,
+    ///  this is only added if it is enabled.
+    pub weak_memory: Option<weak_memory::AllocExtra>,
 }
 
 /// Precomputed layouts of primitive types
@@ -240,9 +269,9 @@ 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<MemPlace<Tag>>,
-    pub(crate) argv: Option<MemPlace<Tag>>,
-    pub(crate) cmd_line: Option<MemPlace<Tag>>,
+    pub(crate) argc: Option<MemPlace<Provenance>>,
+    pub(crate) argv: Option<MemPlace<Provenance>>,
+    pub(crate) cmd_line: Option<MemPlace<Provenance>>,
 
     /// TLS state.
     pub(crate) tls: TlsData<'tcx>,
@@ -255,14 +284,13 @@ pub struct Evaluator<'mir, 'tcx> {
     /// 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,
+    /// The table of file descriptors.
+    pub(crate) file_handler: shims::unix::FileHandler,
+    /// The table of directory descriptors.
+    pub(crate) dir_handler: shims::unix::DirHandler,
 
     /// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
     pub(crate) time_anchor: Instant,
@@ -299,7 +327,7 @@ pub struct Evaluator<'mir, 'tcx> {
     pub(crate) local_crates: Vec<CrateNum>,
 
     /// Mapping extern static names to their base pointer.
-    extern_statics: FxHashMap<Symbol, Pointer<Tag>>,
+    extern_statics: FxHashMap<Symbol, Pointer<Provenance>>,
 
     /// The random number generator used for resolving non-determinism.
     /// Needs to be queried by ptr_to_int, hence needs interior mutability.
@@ -317,11 +345,22 @@ pub struct Evaluator<'mir, 'tcx> {
 
     /// Corresponds to -Zmiri-mute-stdout-stderr and doesn't write the output but acts as if it succeeded.
     pub(crate) mute_stdout_stderr: bool,
+
+    /// Whether weak memory emulation is enabled
+    pub(crate) weak_memory: bool,
+
+    /// The probability of the active thread being preempted at the end of each basic block.
+    pub(crate) preemption_rate: f64,
+
+    /// If `Some`, we will report the current stack every N basic blocks.
+    pub(crate) report_progress: Option<u32>,
+    /// The number of blocks that passed since the last progress report.
+    pub(crate) since_progress_report: u32,
 }
 
 impl<'mir, 'tcx> Evaluator<'mir, 'tcx> {
     pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Self {
-        let local_crates = helpers::get_local_crates(&layout_cx.tcx);
+        let local_crates = helpers::get_local_crates(layout_cx.tcx);
         let layouts =
             PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
         let profiler = config.measureme_out.as_ref().map(|out| {
@@ -332,7 +371,7 @@ pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>)
             Some(RefCell::new(stacked_borrows::GlobalStateInner::new(
                 config.tracked_pointer_tags.clone(),
                 config.tracked_call_ids.clone(),
-                config.tag_raw,
+                config.retag_fields,
             )))
         } else {
             None
@@ -351,7 +390,6 @@ pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>)
             tls: TlsData::default(),
             isolated_op: config.isolated_op,
             validate: config.validate,
-            enforce_number_validity: config.check_number_validity,
             enforce_abi: config.check_abi,
             file_handler: FileHandler::new(config.mute_stdout_stderr),
             dir_handler: Default::default(),
@@ -371,6 +409,10 @@ pub(crate) fn new(config: &MiriConfig, layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>)
             check_alignment: config.check_alignment,
             cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
             mute_stdout_stderr: config.mute_stdout_stderr,
+            weak_memory: config.weak_memory_emulation,
+            preemption_rate: config.preemption_rate,
+            report_progress: config.report_progress,
+            since_progress_report: 0,
         }
     }
 
@@ -386,7 +428,7 @@ pub(crate) fn late_init(
     fn add_extern_static(
         this: &mut MiriEvalContext<'mir, 'tcx>,
         name: &str,
-        ptr: Pointer<Option<Tag>>,
+        ptr: Pointer<Option<Provenance>>,
     ) {
         // This got just allocated, so there definitely is a pointer here.
         let ptr = ptr.into_pointer_or_addr().unwrap();
@@ -415,6 +457,14 @@ fn init_extern_statics(this: &mut MiriEvalContext<'mir, 'tcx>) -> InterpResult<'
                     Self::add_extern_static(this, name, place.ptr);
                 }
             }
+            "freebsd" => {
+                // "environ"
+                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`.
@@ -466,11 +516,13 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
     type FrameExtra = FrameData<'tcx>;
     type AllocExtra = AllocExtra;
 
-    type PointerTag = Tag;
-    type TagExtra = SbTag;
+    type Provenance = Provenance;
+    type ProvenanceExtra = ProvenanceExtra;
 
-    type MemoryMap =
-        MonoHashMap<AllocId, (MemoryKind<MiriMemoryKind>, Allocation<Tag, Self::AllocExtra>)>;
+    type MemoryMap = MonoHashMap<
+        AllocId,
+        (MemoryKind<MiriMemoryKind>, Allocation<Provenance, Self::AllocExtra>),
+    >;
 
     const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
 
@@ -492,13 +544,13 @@ fn enforce_validity(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool {
     }
 
     #[inline(always)]
-    fn enforce_number_init(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool {
-        ecx.machine.enforce_number_validity
+    fn enforce_number_init(_ecx: &MiriEvalContext<'mir, 'tcx>) -> bool {
+        true
     }
 
     #[inline(always)]
-    fn enforce_number_no_provenance(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool {
-        ecx.machine.enforce_number_validity
+    fn enforce_number_no_provenance(_ecx: &MiriEvalContext<'mir, 'tcx>) -> bool {
+        true
     }
 
     #[inline(always)]
@@ -506,16 +558,22 @@ fn enforce_abi(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool {
         ecx.machine.enforce_abi
     }
 
+    #[inline(always)]
+    fn checked_binop_checks_overflow(ecx: &MiriEvalContext<'mir, 'tcx>) -> bool {
+        ecx.tcx.sess.overflow_checks()
+    }
+
     #[inline(always)]
     fn find_mir_or_eval_fn(
         ecx: &mut MiriEvalContext<'mir, 'tcx>,
         instance: ty::Instance<'tcx>,
         abi: Abi,
-        args: &[OpTy<'tcx, Tag>],
-        ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
+        args: &[OpTy<'tcx, Provenance>],
+        dest: &PlaceTy<'tcx, Provenance>,
+        ret: Option<mir::BasicBlock>,
         unwind: StackPopUnwind,
     ) -> InterpResult<'tcx, Option<(&'mir mir::Body<'tcx>, ty::Instance<'tcx>)>> {
-        ecx.find_mir_or_eval_fn(instance, abi, args, ret, unwind)
+        ecx.find_mir_or_eval_fn(instance, abi, args, dest, ret, unwind)
     }
 
     #[inline(always)]
@@ -523,22 +581,24 @@ fn call_extra_fn(
         ecx: &mut MiriEvalContext<'mir, 'tcx>,
         fn_val: Dlsym,
         abi: Abi,
-        args: &[OpTy<'tcx, Tag>],
-        ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
+        args: &[OpTy<'tcx, Provenance>],
+        dest: &PlaceTy<'tcx, Provenance>,
+        ret: Option<mir::BasicBlock>,
         _unwind: StackPopUnwind,
     ) -> InterpResult<'tcx> {
-        ecx.call_dlsym(fn_val, abi, args, ret)
+        ecx.call_dlsym(fn_val, abi, args, dest, ret)
     }
 
     #[inline(always)]
     fn call_intrinsic(
         ecx: &mut MiriEvalContext<'mir, 'tcx>,
         instance: ty::Instance<'tcx>,
-        args: &[OpTy<'tcx, Tag>],
-        ret: Option<(&PlaceTy<'tcx, Tag>, mir::BasicBlock)>,
+        args: &[OpTy<'tcx, Provenance>],
+        dest: &PlaceTy<'tcx, Provenance>,
+        ret: Option<mir::BasicBlock>,
         unwind: StackPopUnwind,
     ) -> InterpResult<'tcx> {
-        ecx.call_intrinsic(instance, args, ret, unwind)
+        ecx.call_intrinsic(instance, args, dest, ret, unwind)
     }
 
     #[inline(always)]
@@ -559,42 +619,74 @@ fn abort(_ecx: &mut MiriEvalContext<'mir, 'tcx>, msg: String) -> InterpResult<'t
     fn binary_ptr_op(
         ecx: &MiriEvalContext<'mir, 'tcx>,
         bin_op: mir::BinOp,
-        left: &ImmTy<'tcx, Tag>,
-        right: &ImmTy<'tcx, Tag>,
-    ) -> InterpResult<'tcx, (Scalar<Tag>, bool, ty::Ty<'tcx>)> {
+        left: &ImmTy<'tcx, Provenance>,
+        right: &ImmTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, (Scalar<Provenance>, bool, ty::Ty<'tcx>)> {
         ecx.binary_ptr_op(bin_op, left, right)
     }
 
     fn thread_local_static_base_pointer(
         ecx: &mut MiriEvalContext<'mir, 'tcx>,
         def_id: DefId,
-    ) -> InterpResult<'tcx, Pointer<Tag>> {
+    ) -> InterpResult<'tcx, Pointer<Provenance>> {
         ecx.get_or_create_thread_local_alloc(def_id)
     }
 
     fn extern_static_base_pointer(
         ecx: &MiriEvalContext<'mir, 'tcx>,
         def_id: DefId,
-    ) -> InterpResult<'tcx, Pointer<Tag>> {
+    ) -> InterpResult<'tcx, Pointer<Provenance>> {
         let link_name = ecx.item_link_name(def_id);
         if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) {
+            // Various parts of the engine rely on `get_alloc_info` for size and alignment
+            // information. That uses the type information of this static.
+            // Make sure it matches the Miri allocation for this.
+            let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
+                panic!("extern_statics cannot contain wildcards")
+            };
+            let (shim_size, shim_align, _kind) = ecx.get_alloc_info(alloc_id);
+            let extern_decl_layout =
+                ecx.tcx.layout_of(ty::ParamEnv::empty().and(ecx.tcx.type_of(def_id))).unwrap();
+            if extern_decl_layout.size != shim_size || extern_decl_layout.align.abi != shim_align {
+                throw_unsup_format!(
+                    "`extern` static `{name}` from crate `{krate}` has been declared \
+                    with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
+                    but Miri emulates it via an extern static shim \
+                    with a size of {shim_size} bytes and alignment of {shim_align} bytes",
+                    name = ecx.tcx.def_path_str(def_id),
+                    krate = ecx.tcx.crate_name(def_id.krate),
+                    decl_size = extern_decl_layout.size.bytes(),
+                    decl_align = extern_decl_layout.align.abi.bytes(),
+                    shim_size = shim_size.bytes(),
+                    shim_align = shim_align.bytes(),
+                )
+            }
             Ok(ptr)
         } else {
-            throw_unsup_format!("`extern` static {:?} is not supported by Miri", def_id)
+            throw_unsup_format!(
+                "`extern` static `{name}` from crate `{krate}` is not supported by Miri",
+                name = ecx.tcx.def_path_str(def_id),
+                krate = ecx.tcx.crate_name(def_id.krate),
+            )
         }
     }
 
-    fn init_allocation_extra<'b>(
+    fn adjust_allocation<'b>(
         ecx: &MiriEvalContext<'mir, 'tcx>,
         id: AllocId,
         alloc: Cow<'b, Allocation>,
         kind: Option<MemoryKind<Self::MemoryKind>>,
-    ) -> Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>> {
+    ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>> {
+        let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
         if ecx.machine.tracked_alloc_ids.contains(&id) {
-            register_diagnostic(NonHaltingDiagnostic::CreatedAlloc(id));
+            register_diagnostic(NonHaltingDiagnostic::CreatedAlloc(
+                id,
+                alloc.size(),
+                alloc.align,
+                kind,
+            ));
         }
 
-        let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
         let alloc = alloc.into_owned();
         let stacks = if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
             Some(Stacks::new_allocation(
@@ -608,30 +700,58 @@ fn init_allocation_extra<'b>(
             None
         };
         let race_alloc = if let Some(data_race) = &ecx.machine.data_race {
-            Some(data_race::AllocExtra::new_allocation(data_race, alloc.size(), kind))
+            Some(data_race::AllocExtra::new_allocation(
+                data_race,
+                &ecx.machine.threads,
+                alloc.size(),
+                kind,
+            ))
         } else {
             None
         };
-        let alloc: Allocation<Tag, Self::AllocExtra> = alloc.convert_tag_add_extra(
+        let buffer_alloc = if ecx.machine.weak_memory {
+            Some(weak_memory::AllocExtra::new_allocation())
+        } else {
+            None
+        };
+        let alloc: Allocation<Provenance, Self::AllocExtra> = alloc.adjust_from_tcx(
             &ecx.tcx,
-            AllocExtra { stacked_borrows: stacks, data_race: race_alloc },
-            |ptr| Evaluator::tag_alloc_base_pointer(ecx, ptr),
-        );
-        Cow::Owned(alloc)
+            AllocExtra {
+                stacked_borrows: stacks.map(RefCell::new),
+                data_race: race_alloc,
+                weak_memory: buffer_alloc,
+            },
+            |ptr| ecx.global_base_pointer(ptr),
+        )?;
+        Ok(Cow::Owned(alloc))
     }
 
-    fn tag_alloc_base_pointer(
+    fn adjust_alloc_base_pointer(
         ecx: &MiriEvalContext<'mir, 'tcx>,
         ptr: Pointer<AllocId>,
-    ) -> Pointer<Tag> {
+    ) -> Pointer<Provenance> {
+        if cfg!(debug_assertions) {
+            // The machine promises to never call us on thread-local or extern statics.
+            let alloc_id = ptr.provenance;
+            match ecx.tcx.get_global_alloc(alloc_id) {
+                Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
+                    panic!("adjust_alloc_base_pointer called on thread-local static")
+                }
+                Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => {
+                    panic!("adjust_alloc_base_pointer called on extern static")
+                }
+                _ => {}
+            }
+        }
         let absolute_addr = intptrcast::GlobalStateInner::rel_ptr_to_addr(ecx, ptr);
         let sb_tag = if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
-            stacked_borrows.borrow_mut().base_tag(ptr.provenance)
+            stacked_borrows.borrow_mut().base_ptr_tag(ptr.provenance)
         } else {
-            SbTag::Untagged
+            // Value does not matter, SB is disabled
+            SbTag::default()
         };
         Pointer::new(
-            Tag::Concrete(ConcreteTag { alloc_id: ptr.provenance, sb: sb_tag }),
+            Provenance::Concrete { alloc_id: ptr.provenance, sb: sb_tag },
             Size::from_bytes(absolute_addr),
         )
     }
@@ -640,7 +760,7 @@ fn tag_alloc_base_pointer(
     fn ptr_from_addr_cast(
         ecx: &MiriEvalContext<'mir, 'tcx>,
         addr: u64,
-    ) -> Pointer<Option<Self::PointerTag>> {
+    ) -> InterpResult<'tcx, Pointer<Option<Self::Provenance>>> {
         intptrcast::GlobalStateInner::ptr_from_addr_cast(ecx, addr)
     }
 
@@ -648,37 +768,37 @@ fn ptr_from_addr_cast(
     fn ptr_from_addr_transmute(
         ecx: &MiriEvalContext<'mir, 'tcx>,
         addr: u64,
-    ) -> Pointer<Option<Self::PointerTag>> {
+    ) -> Pointer<Option<Self::Provenance>> {
         intptrcast::GlobalStateInner::ptr_from_addr_transmute(ecx, addr)
     }
 
     fn expose_ptr(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        ptr: Pointer<Self::PointerTag>,
+        ptr: Pointer<Self::Provenance>,
     ) -> InterpResult<'tcx> {
         match ptr.provenance {
-            Tag::Concrete(concrete) =>
-                intptrcast::GlobalStateInner::expose_addr(ecx, concrete.alloc_id),
-            Tag::Wildcard => {
+            Provenance::Concrete { alloc_id, sb } =>
+                intptrcast::GlobalStateInner::expose_ptr(ecx, alloc_id, sb),
+            Provenance::Wildcard => {
                 // No need to do anything for wildcard pointers as
                 // their provenances have already been previously exposed.
+                Ok(())
             }
         }
-        Ok(())
     }
 
     /// 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(
         ecx: &MiriEvalContext<'mir, 'tcx>,
-        ptr: Pointer<Self::PointerTag>,
-    ) -> Option<(AllocId, Size, Self::TagExtra)> {
+        ptr: Pointer<Self::Provenance>,
+    ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
         let rel = intptrcast::GlobalStateInner::abs_ptr_to_rel(ecx, ptr);
 
         rel.map(|(alloc_id, size)| {
             let sb = match ptr.provenance {
-                Tag::Concrete(ConcreteTag { sb, .. }) => sb,
-                Tag::Wildcard => SbTag::Untagged,
+                Provenance::Concrete { sb, .. } => ProvenanceExtra::Concrete(sb),
+                Provenance::Wildcard => ProvenanceExtra::Wildcard,
             };
             (alloc_id, size, sb)
         })
@@ -689,23 +809,31 @@ fn memory_read(
         _tcx: TyCtxt<'tcx>,
         machine: &Self,
         alloc_extra: &AllocExtra,
-        (alloc_id, tag): (AllocId, Self::TagExtra),
+        (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
         range: AllocRange,
     ) -> InterpResult<'tcx> {
         if let Some(data_race) = &alloc_extra.data_race {
-            data_race.read(alloc_id, range, machine.data_race.as_ref().unwrap())?;
+            data_race.read(
+                alloc_id,
+                range,
+                machine.data_race.as_ref().unwrap(),
+                &machine.threads,
+            )?;
         }
         if let Some(stacked_borrows) = &alloc_extra.stacked_borrows {
-            stacked_borrows.memory_read(
+            stacked_borrows.borrow_mut().memory_read(
                 alloc_id,
-                tag,
+                prov_extra,
                 range,
                 machine.stacked_borrows.as_ref().unwrap(),
                 machine.current_span(),
-            )
-        } else {
-            Ok(())
+                &machine.threads,
+            )?;
+        }
+        if let Some(weak_memory) = &alloc_extra.weak_memory {
+            weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
         }
+        Ok(())
     }
 
     #[inline(always)]
@@ -713,23 +841,31 @@ fn memory_written(
         _tcx: TyCtxt<'tcx>,
         machine: &mut Self,
         alloc_extra: &mut AllocExtra,
-        (alloc_id, tag): (AllocId, Self::TagExtra),
+        (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
         range: AllocRange,
     ) -> InterpResult<'tcx> {
         if let Some(data_race) = &mut alloc_extra.data_race {
-            data_race.write(alloc_id, range, machine.data_race.as_mut().unwrap())?;
+            data_race.write(
+                alloc_id,
+                range,
+                machine.data_race.as_mut().unwrap(),
+                &machine.threads,
+            )?;
         }
         if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
-            stacked_borrows.memory_written(
+            stacked_borrows.get_mut().memory_written(
                 alloc_id,
-                tag,
+                prov_extra,
                 range,
                 machine.stacked_borrows.as_ref().unwrap(),
                 machine.current_span(),
-            )
-        } else {
-            Ok(())
+                &machine.threads,
+            )?;
+        }
+        if let Some(weak_memory) = &alloc_extra.weak_memory {
+            weak_memory.memory_accessed(range, machine.data_race.as_ref().unwrap());
         }
+        Ok(())
     }
 
     #[inline(always)]
@@ -737,21 +873,27 @@ fn memory_deallocated(
         _tcx: TyCtxt<'tcx>,
         machine: &mut Self,
         alloc_extra: &mut AllocExtra,
-        (alloc_id, tag): (AllocId, Self::TagExtra),
+        (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
         range: AllocRange,
     ) -> InterpResult<'tcx> {
         if machine.tracked_alloc_ids.contains(&alloc_id) {
             register_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
         }
         if let Some(data_race) = &mut alloc_extra.data_race {
-            data_race.deallocate(alloc_id, range, machine.data_race.as_mut().unwrap())?;
+            data_race.deallocate(
+                alloc_id,
+                range,
+                machine.data_race.as_mut().unwrap(),
+                &machine.threads,
+            )?;
         }
         if let Some(stacked_borrows) = &mut alloc_extra.stacked_borrows {
-            stacked_borrows.memory_deallocated(
+            stacked_borrows.get_mut().memory_deallocated(
                 alloc_id,
-                tag,
+                prove_extra,
                 range,
                 machine.stacked_borrows.as_ref().unwrap(),
+                &machine.threads,
             )
         } else {
             Ok(())
@@ -762,7 +904,7 @@ fn memory_deallocated(
     fn retag(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
         kind: mir::RetagKind,
-        place: &PlaceTy<'tcx, Tag>,
+        place: &PlaceTy<'tcx, Provenance>,
     ) -> InterpResult<'tcx> {
         if ecx.machine.stacked_borrows.is_some() { ecx.retag(kind, place) } else { Ok(()) }
     }
@@ -770,8 +912,8 @@ fn retag(
     #[inline(always)]
     fn init_frame_extra(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        frame: Frame<'mir, 'tcx, Tag>,
-    ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Tag, FrameData<'tcx>>> {
+        frame: Frame<'mir, 'tcx, Provenance>,
+    ) -> InterpResult<'tcx, Frame<'mir, 'tcx, Provenance, 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();
@@ -788,26 +930,42 @@ fn init_frame_extra(
         };
 
         let stacked_borrows = ecx.machine.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, timing };
+        let extra = FrameData {
+            stacked_borrows: stacked_borrows.map(|sb| sb.borrow_mut().new_frame()),
+            catch_unwind: None,
+            timing,
+        };
         Ok(frame.with_extra(extra))
     }
 
     fn stack<'a>(
         ecx: &'a InterpCx<'mir, 'tcx, Self>,
-    ) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
+    ) -> &'a [Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>] {
         ecx.active_thread_stack()
     }
 
     fn stack_mut<'a>(
         ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
-    ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
+    ) -> &'a mut Vec<Frame<'mir, 'tcx, Self::Provenance, Self::FrameExtra>> {
         ecx.active_thread_stack_mut()
     }
 
+    fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
+        // Possibly report our progress.
+        if let Some(report_progress) = ecx.machine.report_progress {
+            if ecx.machine.since_progress_report >= report_progress {
+                register_diagnostic(NonHaltingDiagnostic::ProgressReport);
+                ecx.machine.since_progress_report = 0;
+            }
+            // Cannot overflow, since it is strictly less than `report_progress`.
+            ecx.machine.since_progress_report += 1;
+        }
+        // These are our preemption points.
+        ecx.maybe_preempt_active_thread();
+        Ok(())
+    }
+
     #[inline(always)]
     fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
         if ecx.machine.stacked_borrows.is_some() { ecx.retag_return_place() } else { Ok(()) }
@@ -816,11 +974,14 @@ fn after_stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>
     #[inline(always)]
     fn after_stack_pop(
         ecx: &mut InterpCx<'mir, 'tcx, Self>,
-        mut frame: Frame<'mir, 'tcx, Tag, FrameData<'tcx>>,
+        mut frame: Frame<'mir, 'tcx, Provenance, FrameData<'tcx>>,
         unwinding: bool,
     ) -> InterpResult<'tcx, StackPopJump> {
         let timing = frame.extra.timing.take();
-        let res = ecx.handle_stack_pop(frame.extra, unwinding);
+        if let Some(stacked_borrows) = &ecx.machine.stacked_borrows {
+            stacked_borrows.borrow_mut().end_call(&frame.extra);
+        }
+        let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
         if let Some(profiler) = ecx.machine.profiler.as_ref() {
             profiler.finish_recording_interval_event(timing.unwrap());
         }