miri/
machine.rs

1//! Global machine state as well as implementation of the interpreter engine
2//! `Machine` trait.
3
4use std::any::Any;
5use std::borrow::Cow;
6use std::cell::{Cell, RefCell};
7use std::collections::hash_map::Entry;
8use std::path::Path;
9use std::rc::Rc;
10use std::{fmt, process};
11
12use rand::rngs::StdRng;
13use rand::{Rng, SeedableRng};
14use rustc_abi::{Align, ExternAbi, Size};
15use rustc_apfloat::{Float, FloatConvert};
16use rustc_attr_data_structures::InlineAttr;
17use rustc_data_structures::fx::{FxHashMap, FxHashSet};
18#[allow(unused)]
19use rustc_data_structures::static_assert_size;
20use rustc_middle::mir;
21use rustc_middle::query::TyCtxtAt;
22use rustc_middle::ty::layout::{
23    HasTyCtxt, HasTypingEnv, LayoutCx, LayoutError, LayoutOf, TyAndLayout,
24};
25use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
26use rustc_session::config::InliningThreshold;
27use rustc_span::def_id::{CrateNum, DefId};
28use rustc_span::{Span, SpanData, Symbol};
29use rustc_target::callconv::FnAbi;
30
31use crate::alloc_addresses::EvalContextExt;
32use crate::concurrency::cpu_affinity::{self, CpuAffinityMask};
33use crate::concurrency::data_race::{self, NaReadType, NaWriteType};
34use crate::concurrency::{AllocDataRaceHandler, GenmcCtx, GlobalDataRaceHandler, weak_memory};
35use crate::*;
36
37/// First real-time signal.
38/// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35
39/// as typical values.
40pub const SIGRTMIN: i32 = 34;
41
42/// Last real-time signal.
43/// `signal(7)` says it must be between 32 and 64 and specifies
44/// `SIGRTMAX` - `SIGRTMIN` >= 8 (which is the value of `_POSIX_RTSIG_MAX`)
45pub const SIGRTMAX: i32 = 42;
46
47/// Each anonymous global (constant, vtable, function pointer, ...) has multiple addresses, but only
48/// this many. Since const allocations are never deallocated, choosing a new [`AllocId`] and thus
49/// base address for each evaluation would produce unbounded memory usage.
50const ADDRS_PER_ANON_GLOBAL: usize = 32;
51
52/// Extra data stored with each stack frame
53pub struct FrameExtra<'tcx> {
54    /// Extra data for the Borrow Tracker.
55    pub borrow_tracker: Option<borrow_tracker::FrameState>,
56
57    /// If this is Some(), then this is a special "catch unwind" frame (the frame of `try_fn`
58    /// called by `try`). When this frame is popped during unwinding a panic,
59    /// we stop unwinding, use the `CatchUnwindData` to handle catching.
60    pub catch_unwind: Option<CatchUnwindData<'tcx>>,
61
62    /// If `measureme` profiling is enabled, holds timing information
63    /// for the start of this frame. When we finish executing this frame,
64    /// we use this to register a completed event with `measureme`.
65    pub timing: Option<measureme::DetachedTiming>,
66
67    /// Indicates whether a `Frame` is part of a workspace-local crate and is also not
68    /// `#[track_caller]`. We compute this once on creation and store the result, as an
69    /// optimization.
70    /// This is used by `MiriMachine::current_span` and `MiriMachine::caller_span`
71    pub is_user_relevant: bool,
72
73    /// We have a cache for the mapping from [`mir::Const`] to resulting [`AllocId`].
74    /// However, we don't want all frames to always get the same result, so we insert
75    /// an additional bit of "salt" into the cache key. This salt is fixed per-frame
76    /// so that within a call, a const will have a stable address.
77    salt: usize,
78
79    /// Data race detector per-frame data.
80    pub data_race: Option<data_race::FrameState>,
81}
82
83impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        // Omitting `timing`, it does not support `Debug`.
86        let FrameExtra {
87            borrow_tracker,
88            catch_unwind,
89            timing: _,
90            is_user_relevant,
91            salt,
92            data_race,
93        } = self;
94        f.debug_struct("FrameData")
95            .field("borrow_tracker", borrow_tracker)
96            .field("catch_unwind", catch_unwind)
97            .field("is_user_relevant", is_user_relevant)
98            .field("salt", salt)
99            .field("data_race", data_race)
100            .finish()
101    }
102}
103
104impl VisitProvenance for FrameExtra<'_> {
105    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
106        let FrameExtra {
107            catch_unwind,
108            borrow_tracker,
109            timing: _,
110            is_user_relevant: _,
111            salt: _,
112            data_race: _,
113        } = self;
114
115        catch_unwind.visit_provenance(visit);
116        borrow_tracker.visit_provenance(visit);
117    }
118}
119
120/// Extra memory kinds
121#[derive(Debug, Copy, Clone, PartialEq, Eq)]
122pub enum MiriMemoryKind {
123    /// `__rust_alloc` memory.
124    Rust,
125    /// `miri_alloc` memory.
126    Miri,
127    /// `malloc` memory.
128    C,
129    /// Windows `HeapAlloc` memory.
130    WinHeap,
131    /// Windows "local" memory (to be freed with `LocalFree`)
132    WinLocal,
133    /// Memory for args, errno, and other parts of the machine-managed environment.
134    /// This memory may leak.
135    Machine,
136    /// Memory allocated by the runtime (e.g. env vars). Separate from `Machine`
137    /// because we clean it up and leak-check it.
138    Runtime,
139    /// Globals copied from `tcx`.
140    /// This memory may leak.
141    Global,
142    /// Memory for extern statics.
143    /// This memory may leak.
144    ExternStatic,
145    /// Memory for thread-local statics.
146    /// This memory may leak.
147    Tls,
148    /// Memory mapped directly by the program
149    Mmap,
150}
151
152impl From<MiriMemoryKind> for MemoryKind {
153    #[inline(always)]
154    fn from(kind: MiriMemoryKind) -> MemoryKind {
155        MemoryKind::Machine(kind)
156    }
157}
158
159impl MayLeak for MiriMemoryKind {
160    #[inline(always)]
161    fn may_leak(self) -> bool {
162        use self::MiriMemoryKind::*;
163        match self {
164            Rust | Miri | C | WinHeap | WinLocal | Runtime => false,
165            Machine | Global | ExternStatic | Tls | Mmap => true,
166        }
167    }
168}
169
170impl MiriMemoryKind {
171    /// Whether we have a useful allocation span for an allocation of this kind.
172    fn should_save_allocation_span(self) -> bool {
173        use self::MiriMemoryKind::*;
174        match self {
175            // Heap allocations are fine since the `Allocation` is created immediately.
176            Rust | Miri | C | WinHeap | WinLocal | Mmap => true,
177            // Everything else is unclear, let's not show potentially confusing spans.
178            Machine | Global | ExternStatic | Tls | Runtime => false,
179        }
180    }
181}
182
183impl fmt::Display for MiriMemoryKind {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        use self::MiriMemoryKind::*;
186        match self {
187            Rust => write!(f, "Rust heap"),
188            Miri => write!(f, "Miri bare-metal heap"),
189            C => write!(f, "C heap"),
190            WinHeap => write!(f, "Windows heap"),
191            WinLocal => write!(f, "Windows local memory"),
192            Machine => write!(f, "machine-managed memory"),
193            Runtime => write!(f, "language runtime memory"),
194            Global => write!(f, "global (static or const)"),
195            ExternStatic => write!(f, "extern static"),
196            Tls => write!(f, "thread-local static"),
197            Mmap => write!(f, "mmap"),
198        }
199    }
200}
201
202pub type MemoryKind = interpret::MemoryKind<MiriMemoryKind>;
203
204/// Pointer provenance.
205// This needs to be `Eq`+`Hash` because the `Machine` trait needs that because validity checking
206// *might* be recursive and then it has to track which places have already been visited.
207// These implementations are a bit questionable, and it means we may check the same place multiple
208// times with different provenance, but that is in general not wrong.
209#[derive(Clone, Copy, PartialEq, Eq, Hash)]
210pub enum Provenance {
211    /// For pointers with concrete provenance. we exactly know which allocation they are attached to
212    /// and what their borrow tag is.
213    Concrete {
214        alloc_id: AllocId,
215        /// Borrow Tracker tag.
216        tag: BorTag,
217    },
218    /// Pointers with wildcard provenance are created on int-to-ptr casts. According to the
219    /// specification, we should at that point angelically "guess" a provenance that will make all
220    /// future uses of this pointer work, if at all possible. Of course such a semantics cannot be
221    /// actually implemented in Miri. So instead, we approximate this, erroring on the side of
222    /// accepting too much code rather than rejecting correct code: a pointer with wildcard
223    /// provenance "acts like" any previously exposed pointer. Each time it is used, we check
224    /// whether *some* exposed pointer could have done what we want to do, and if the answer is yes
225    /// then we allow the access. This allows too much code in two ways:
226    /// - The same wildcard pointer can "take the role" of multiple different exposed pointers on
227    ///   subsequent memory accesses.
228    /// - In the aliasing model, we don't just have to know the borrow tag of the pointer used for
229    ///   the access, we also have to update the aliasing state -- and that update can be very
230    ///   different depending on which borrow tag we pick! Stacked Borrows has support for this by
231    ///   switching to a stack that is only approximately known, i.e. we over-approximate the effect
232    ///   of using *any* exposed pointer for this access, and only keep information about the borrow
233    ///   stack that would be true with all possible choices.
234    Wildcard,
235}
236
237/// The "extra" information a pointer has over a regular AllocId.
238#[derive(Copy, Clone, PartialEq)]
239pub enum ProvenanceExtra {
240    Concrete(BorTag),
241    Wildcard,
242}
243
244#[cfg(target_pointer_width = "64")]
245static_assert_size!(StrictPointer, 24);
246// FIXME: this would with in 24bytes but layout optimizations are not smart enough
247// #[cfg(target_pointer_width = "64")]
248//static_assert_size!(Pointer, 24);
249#[cfg(target_pointer_width = "64")]
250static_assert_size!(Scalar, 32);
251
252impl fmt::Debug for Provenance {
253    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
254        match self {
255            Provenance::Concrete { alloc_id, tag } => {
256                // Forward `alternate` flag to `alloc_id` printing.
257                if f.alternate() {
258                    write!(f, "[{alloc_id:#?}]")?;
259                } else {
260                    write!(f, "[{alloc_id:?}]")?;
261                }
262                // Print Borrow Tracker tag.
263                write!(f, "{tag:?}")?;
264            }
265            Provenance::Wildcard => {
266                write!(f, "[wildcard]")?;
267            }
268        }
269        Ok(())
270    }
271}
272
273impl interpret::Provenance for Provenance {
274    /// We use absolute addresses in the `offset` of a `StrictPointer`.
275    const OFFSET_IS_ADDR: bool = true;
276
277    /// Miri implements wildcard provenance.
278    const WILDCARD: Option<Self> = Some(Provenance::Wildcard);
279
280    fn get_alloc_id(self) -> Option<AllocId> {
281        match self {
282            Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
283            Provenance::Wildcard => None,
284        }
285    }
286
287    fn fmt(ptr: &interpret::Pointer<Self>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288        let (prov, addr) = ptr.into_parts(); // address is absolute
289        write!(f, "{:#x}", addr.bytes())?;
290        if f.alternate() {
291            write!(f, "{prov:#?}")?;
292        } else {
293            write!(f, "{prov:?}")?;
294        }
295        Ok(())
296    }
297
298    fn join(left: Option<Self>, right: Option<Self>) -> Option<Self> {
299        match (left, right) {
300            // If both are the *same* concrete tag, that is the result.
301            (
302                Some(Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }),
303                Some(Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }),
304            ) if left_alloc == right_alloc && left_tag == right_tag => left,
305            // If one side is a wildcard, the best possible outcome is that it is equal to the other
306            // one, and we use that.
307            (Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o,
308            // Otherwise, fall back to `None`.
309            _ => None,
310        }
311    }
312}
313
314impl fmt::Debug for ProvenanceExtra {
315    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316        match self {
317            ProvenanceExtra::Concrete(pid) => write!(f, "{pid:?}"),
318            ProvenanceExtra::Wildcard => write!(f, "<wildcard>"),
319        }
320    }
321}
322
323impl ProvenanceExtra {
324    pub fn and_then<T>(self, f: impl FnOnce(BorTag) -> Option<T>) -> Option<T> {
325        match self {
326            ProvenanceExtra::Concrete(pid) => f(pid),
327            ProvenanceExtra::Wildcard => None,
328        }
329    }
330}
331
332/// Extra per-allocation data
333#[derive(Debug)]
334pub struct AllocExtra<'tcx> {
335    /// Global state of the borrow tracker, if enabled.
336    pub borrow_tracker: Option<borrow_tracker::AllocState>,
337    /// Extra state for data race detection.
338    ///
339    /// Invariant: The enum variant must match the enum variant in the `data_race` field on `MiriMachine`
340    pub data_race: AllocDataRaceHandler,
341    /// A backtrace to where this allocation was allocated.
342    /// As this is recorded for leak reports, it only exists
343    /// if this allocation is leakable. The backtrace is not
344    /// pruned yet; that should be done before printing it.
345    pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
346    /// Synchronization primitives like to attach extra data to particular addresses. We store that
347    /// inside the relevant allocation, to ensure that everything is removed when the allocation is
348    /// freed.
349    /// This maps offsets to synchronization-primitive-specific data.
350    pub sync: FxHashMap<Size, Box<dyn Any>>,
351}
352
353// We need a `Clone` impl because the machine passes `Allocation` through `Cow`...
354// but that should never end up actually cloning our `AllocExtra`.
355impl<'tcx> Clone for AllocExtra<'tcx> {
356    fn clone(&self) -> Self {
357        panic!("our allocations should never be cloned");
358    }
359}
360
361impl VisitProvenance for AllocExtra<'_> {
362    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
363        let AllocExtra { borrow_tracker, data_race, backtrace: _, sync: _ } = self;
364
365        borrow_tracker.visit_provenance(visit);
366        data_race.visit_provenance(visit);
367    }
368}
369
370/// Precomputed layouts of primitive types
371pub struct PrimitiveLayouts<'tcx> {
372    pub unit: TyAndLayout<'tcx>,
373    pub i8: TyAndLayout<'tcx>,
374    pub i16: TyAndLayout<'tcx>,
375    pub i32: TyAndLayout<'tcx>,
376    pub i64: TyAndLayout<'tcx>,
377    pub i128: TyAndLayout<'tcx>,
378    pub isize: TyAndLayout<'tcx>,
379    pub u8: TyAndLayout<'tcx>,
380    pub u16: TyAndLayout<'tcx>,
381    pub u32: TyAndLayout<'tcx>,
382    pub u64: TyAndLayout<'tcx>,
383    pub u128: TyAndLayout<'tcx>,
384    pub usize: TyAndLayout<'tcx>,
385    pub bool: TyAndLayout<'tcx>,
386    pub mut_raw_ptr: TyAndLayout<'tcx>,   // *mut ()
387    pub const_raw_ptr: TyAndLayout<'tcx>, // *const ()
388}
389
390impl<'tcx> PrimitiveLayouts<'tcx> {
391    fn new(layout_cx: LayoutCx<'tcx>) -> Result<Self, &'tcx LayoutError<'tcx>> {
392        let tcx = layout_cx.tcx();
393        let mut_raw_ptr = Ty::new_mut_ptr(tcx, tcx.types.unit);
394        let const_raw_ptr = Ty::new_imm_ptr(tcx, tcx.types.unit);
395        Ok(Self {
396            unit: layout_cx.layout_of(tcx.types.unit)?,
397            i8: layout_cx.layout_of(tcx.types.i8)?,
398            i16: layout_cx.layout_of(tcx.types.i16)?,
399            i32: layout_cx.layout_of(tcx.types.i32)?,
400            i64: layout_cx.layout_of(tcx.types.i64)?,
401            i128: layout_cx.layout_of(tcx.types.i128)?,
402            isize: layout_cx.layout_of(tcx.types.isize)?,
403            u8: layout_cx.layout_of(tcx.types.u8)?,
404            u16: layout_cx.layout_of(tcx.types.u16)?,
405            u32: layout_cx.layout_of(tcx.types.u32)?,
406            u64: layout_cx.layout_of(tcx.types.u64)?,
407            u128: layout_cx.layout_of(tcx.types.u128)?,
408            usize: layout_cx.layout_of(tcx.types.usize)?,
409            bool: layout_cx.layout_of(tcx.types.bool)?,
410            mut_raw_ptr: layout_cx.layout_of(mut_raw_ptr)?,
411            const_raw_ptr: layout_cx.layout_of(const_raw_ptr)?,
412        })
413    }
414
415    pub fn uint(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
416        match size.bits() {
417            8 => Some(self.u8),
418            16 => Some(self.u16),
419            32 => Some(self.u32),
420            64 => Some(self.u64),
421            128 => Some(self.u128),
422            _ => None,
423        }
424    }
425
426    pub fn int(&self, size: Size) -> Option<TyAndLayout<'tcx>> {
427        match size.bits() {
428            8 => Some(self.i8),
429            16 => Some(self.i16),
430            32 => Some(self.i32),
431            64 => Some(self.i64),
432            128 => Some(self.i128),
433            _ => None,
434        }
435    }
436}
437
438/// The machine itself.
439///
440/// If you add anything here that stores machine values, remember to update
441/// `visit_all_machine_values`!
442pub struct MiriMachine<'tcx> {
443    // We carry a copy of the global `TyCtxt` for convenience, so methods taking just `&Evaluator` have `tcx` access.
444    pub tcx: TyCtxt<'tcx>,
445
446    /// Global data for borrow tracking.
447    pub borrow_tracker: Option<borrow_tracker::GlobalState>,
448
449    /// Depending on settings, this will be `None`,
450    /// global data for a data race detector,
451    /// or the context required for running in GenMC mode.
452    ///
453    /// Invariant: The enum variant must match the enum variant of `AllocDataRaceHandler` in the `data_race` field of all `AllocExtra`.
454    pub data_race: GlobalDataRaceHandler,
455
456    /// Ptr-int-cast module global data.
457    pub alloc_addresses: alloc_addresses::GlobalState,
458
459    /// Environment variables.
460    pub(crate) env_vars: EnvVars<'tcx>,
461
462    /// Return place of the main function.
463    pub(crate) main_fn_ret_place: Option<MPlaceTy<'tcx>>,
464
465    /// Program arguments (`Option` because we can only initialize them after creating the ecx).
466    /// These are *pointers* to argc/argv because macOS.
467    /// We also need the full command line as one string because of Windows.
468    pub(crate) argc: Option<Pointer>,
469    pub(crate) argv: Option<Pointer>,
470    pub(crate) cmd_line: Option<Pointer>,
471
472    /// TLS state.
473    pub(crate) tls: TlsData<'tcx>,
474
475    /// What should Miri do when an op requires communicating with the host,
476    /// such as accessing host env vars, random number generation, and
477    /// file system access.
478    pub(crate) isolated_op: IsolatedOp,
479
480    /// Whether to enforce the validity invariant.
481    pub(crate) validation: ValidationMode,
482
483    /// The table of file descriptors.
484    pub(crate) fds: shims::FdTable,
485    /// The table of directory descriptors.
486    pub(crate) dirs: shims::DirTable,
487
488    /// The list of all EpollEventInterest.
489    pub(crate) epoll_interests: shims::EpollInterestTable,
490
491    /// This machine's monotone clock.
492    pub(crate) monotonic_clock: MonotonicClock,
493
494    /// The set of threads.
495    pub(crate) threads: ThreadManager<'tcx>,
496
497    /// Stores which thread is eligible to run on which CPUs.
498    /// This has no effect at all, it is just tracked to produce the correct result
499    /// in `sched_getaffinity`
500    pub(crate) thread_cpu_affinity: FxHashMap<ThreadId, CpuAffinityMask>,
501
502    /// The state of the primitive synchronization objects.
503    pub(crate) sync: SynchronizationObjects,
504
505    /// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
506    pub(crate) layouts: PrimitiveLayouts<'tcx>,
507
508    /// Allocations that are considered roots of static memory (that may leak).
509    pub(crate) static_roots: Vec<AllocId>,
510
511    /// The `measureme` profiler used to record timing information about
512    /// the emulated program.
513    profiler: Option<measureme::Profiler>,
514    /// Used with `profiler` to cache the `StringId`s for event names
515    /// used with `measureme`.
516    string_cache: FxHashMap<String, measureme::StringId>,
517
518    /// Cache of `Instance` exported under the given `Symbol` name.
519    /// `None` means no `Instance` exported under the given name is found.
520    pub(crate) exported_symbols_cache: FxHashMap<Symbol, Option<Instance<'tcx>>>,
521
522    /// Equivalent setting as RUST_BACKTRACE on encountering an error.
523    pub(crate) backtrace_style: BacktraceStyle,
524
525    /// Crates which are considered local for the purposes of error reporting.
526    pub(crate) local_crates: Vec<CrateNum>,
527
528    /// Mapping extern static names to their pointer.
529    extern_statics: FxHashMap<Symbol, StrictPointer>,
530
531    /// The random number generator used for resolving non-determinism.
532    /// Needs to be queried by ptr_to_int, hence needs interior mutability.
533    pub(crate) rng: RefCell<StdRng>,
534
535    /// The allocator used for the machine's `AllocBytes` in native-libs mode.
536    #[cfg(target_os = "linux")]
537    pub(crate) allocator: Option<Rc<RefCell<crate::alloc::isolated_alloc::IsolatedAlloc>>>,
538
539    /// The allocation IDs to report when they are being allocated
540    /// (helps for debugging memory leaks and use after free bugs).
541    tracked_alloc_ids: FxHashSet<AllocId>,
542    /// For the tracked alloc ids, also report read/write accesses.
543    track_alloc_accesses: bool,
544
545    /// Controls whether alignment of memory accesses is being checked.
546    pub(crate) check_alignment: AlignmentCheck,
547
548    /// Failure rate of compare_exchange_weak, between 0.0 and 1.0
549    pub(crate) cmpxchg_weak_failure_rate: f64,
550
551    /// The probability of the active thread being preempted at the end of each basic block.
552    pub(crate) preemption_rate: f64,
553
554    /// If `Some`, we will report the current stack every N basic blocks.
555    pub(crate) report_progress: Option<u32>,
556    // The total number of blocks that have been executed.
557    pub(crate) basic_block_count: u64,
558
559    /// Handle of the optional shared object file for native functions.
560    #[cfg(unix)]
561    pub native_lib: Vec<(libloading::Library, std::path::PathBuf)>,
562    #[cfg(not(unix))]
563    pub native_lib: Vec<!>,
564
565    /// Run a garbage collector for BorTags every N basic blocks.
566    pub(crate) gc_interval: u32,
567    /// The number of blocks that passed since the last BorTag GC pass.
568    pub(crate) since_gc: u32,
569
570    /// The number of CPUs to be reported by miri.
571    pub(crate) num_cpus: u32,
572
573    /// Determines Miri's page size and associated values
574    pub(crate) page_size: u64,
575    pub(crate) stack_addr: u64,
576    pub(crate) stack_size: u64,
577
578    /// Whether to collect a backtrace when each allocation is created, just in case it leaks.
579    pub(crate) collect_leak_backtraces: bool,
580
581    /// The spans we will use to report where an allocation was created and deallocated in
582    /// diagnostics.
583    pub(crate) allocation_spans: RefCell<FxHashMap<AllocId, (Span, Option<Span>)>>,
584
585    /// Maps MIR consts to their evaluated result. We combine the const with a "salt" (`usize`)
586    /// that is fixed per stack frame; this lets us have sometimes different results for the
587    /// same const while ensuring consistent results within a single call.
588    const_cache: RefCell<FxHashMap<(mir::Const<'tcx>, usize), OpTy<'tcx>>>,
589
590    /// For each allocation, an offset inside that allocation that was deemed aligned even for
591    /// symbolic alignment checks. This cannot be stored in `AllocExtra` since it needs to be
592    /// tracked for vtables and function allocations as well as regular allocations.
593    ///
594    /// Invariant: the promised alignment will never be less than the native alignment of the
595    /// allocation.
596    pub(crate) symbolic_alignment: RefCell<FxHashMap<AllocId, (Size, Align)>>,
597
598    /// A cache of "data range" computations for unions (i.e., the offsets of non-padding bytes).
599    union_data_ranges: FxHashMap<Ty<'tcx>, RangeSet>,
600
601    /// Caches the sanity-checks for various pthread primitives.
602    pub(crate) pthread_mutex_sanity: Cell<bool>,
603    pub(crate) pthread_rwlock_sanity: Cell<bool>,
604    pub(crate) pthread_condvar_sanity: Cell<bool>,
605
606    /// Remembers whether we already warned about an extern type with Stacked Borrows.
607    pub(crate) sb_extern_type_warned: Cell<bool>,
608    /// Remember whether we already warned about sharing memory with a native call.
609    #[cfg(unix)]
610    pub(crate) native_call_mem_warned: Cell<bool>,
611    /// Remembers which shims have already shown the warning about erroring in isolation.
612    pub(crate) reject_in_isolation_warned: RefCell<FxHashSet<String>>,
613    /// Remembers which int2ptr casts we have already warned about.
614    pub(crate) int2ptr_warned: RefCell<FxHashSet<Span>>,
615
616    /// Cache for `mangle_internal_symbol`.
617    pub(crate) mangle_internal_symbol_cache: FxHashMap<&'static str, String>,
618
619    /// Always prefer the intrinsic fallback body over the native Miri implementation.
620    pub force_intrinsic_fallback: bool,
621
622    /// Whether floating-point operations can behave non-deterministically.
623    pub float_nondet: bool,
624}
625
626impl<'tcx> MiriMachine<'tcx> {
627    pub(crate) fn new(
628        config: &MiriConfig,
629        layout_cx: LayoutCx<'tcx>,
630        genmc_ctx: Option<Rc<GenmcCtx>>,
631    ) -> Self {
632        let tcx = layout_cx.tcx();
633        let local_crates = helpers::get_local_crates(tcx);
634        let layouts =
635            PrimitiveLayouts::new(layout_cx).expect("Couldn't get layouts of primitive types");
636        let profiler = config.measureme_out.as_ref().map(|out| {
637            let crate_name =
638                tcx.sess.opts.crate_name.clone().unwrap_or_else(|| "unknown-crate".to_string());
639            let pid = process::id();
640            // We adopt the same naming scheme for the profiler output that rustc uses. In rustc,
641            // the PID is padded so that the nondeterministic value of the PID does not spread
642            // nondeterminism to the allocator. In Miri we are not aiming for such performance
643            // control, we just pad for consistency with rustc.
644            let filename = format!("{crate_name}-{pid:07}");
645            let path = Path::new(out).join(filename);
646            measureme::Profiler::new(path).expect("Couldn't create `measureme` profiler")
647        });
648        let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
649        let borrow_tracker = config.borrow_tracker.map(|bt| bt.instantiate_global_state(config));
650        let data_race = if config.genmc_mode {
651            // `genmc_ctx` persists across executions, so we don't create a new one here.
652            GlobalDataRaceHandler::Genmc(genmc_ctx.unwrap())
653        } else if config.data_race_detector {
654            GlobalDataRaceHandler::Vclocks(Box::new(data_race::GlobalState::new(config)))
655        } else {
656            GlobalDataRaceHandler::None
657        };
658        // Determine page size, stack address, and stack size.
659        // These values are mostly meaningless, but the stack address is also where we start
660        // allocating physical integer addresses for all allocations.
661        let page_size = if let Some(page_size) = config.page_size {
662            page_size
663        } else {
664            let target = &tcx.sess.target;
665            match target.arch.as_ref() {
666                "wasm32" | "wasm64" => 64 * 1024, // https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances
667                "aarch64" => {
668                    if target.options.vendor.as_ref() == "apple" {
669                        // No "definitive" source, but see:
670                        // https://www.wwdcnotes.com/notes/wwdc20/10214/
671                        // https://github.com/ziglang/zig/issues/11308 etc.
672                        16 * 1024
673                    } else {
674                        4 * 1024
675                    }
676                }
677                _ => 4 * 1024,
678            }
679        };
680        // On 16bit targets, 32 pages is more than the entire address space!
681        let stack_addr = if tcx.pointer_size().bits() < 32 { page_size } else { page_size * 32 };
682        let stack_size =
683            if tcx.pointer_size().bits() < 32 { page_size * 4 } else { page_size * 16 };
684        assert!(
685            usize::try_from(config.num_cpus).unwrap() <= cpu_affinity::MAX_CPUS,
686            "miri only supports up to {} CPUs, but {} were configured",
687            cpu_affinity::MAX_CPUS,
688            config.num_cpus
689        );
690        let threads = ThreadManager::new(config);
691        let mut thread_cpu_affinity = FxHashMap::default();
692        if matches!(&*tcx.sess.target.os, "linux" | "freebsd" | "android") {
693            thread_cpu_affinity
694                .insert(threads.active_thread(), CpuAffinityMask::new(&layout_cx, config.num_cpus));
695        }
696        MiriMachine {
697            tcx,
698            borrow_tracker,
699            data_race,
700            alloc_addresses: RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr)),
701            // `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
702            env_vars: EnvVars::default(),
703            main_fn_ret_place: None,
704            argc: None,
705            argv: None,
706            cmd_line: None,
707            tls: TlsData::default(),
708            isolated_op: config.isolated_op,
709            validation: config.validation,
710            fds: shims::FdTable::init(config.mute_stdout_stderr),
711            epoll_interests: shims::EpollInterestTable::new(),
712            dirs: Default::default(),
713            layouts,
714            threads,
715            thread_cpu_affinity,
716            sync: SynchronizationObjects::default(),
717            static_roots: Vec::new(),
718            profiler,
719            string_cache: Default::default(),
720            exported_symbols_cache: FxHashMap::default(),
721            backtrace_style: config.backtrace_style,
722            local_crates,
723            extern_statics: FxHashMap::default(),
724            rng: RefCell::new(rng),
725            #[cfg(target_os = "linux")]
726            allocator: if !config.native_lib.is_empty() {
727                Some(Rc::new(RefCell::new(crate::alloc::isolated_alloc::IsolatedAlloc::new())))
728            } else { None },
729            tracked_alloc_ids: config.tracked_alloc_ids.clone(),
730            track_alloc_accesses: config.track_alloc_accesses,
731            check_alignment: config.check_alignment,
732            cmpxchg_weak_failure_rate: config.cmpxchg_weak_failure_rate,
733            preemption_rate: config.preemption_rate,
734            report_progress: config.report_progress,
735            basic_block_count: 0,
736            monotonic_clock: MonotonicClock::new(config.isolated_op == IsolatedOp::Allow),
737            #[cfg(unix)]
738            native_lib: config.native_lib.iter().map(|lib_file_path| {
739                let host_triple = rustc_session::config::host_tuple();
740                let target_triple = tcx.sess.opts.target_triple.tuple();
741                // Check if host target == the session target.
742                if host_triple != target_triple {
743                    panic!(
744                        "calling native C functions in linked .so file requires host and target to be the same: \
745                        host={host_triple}, target={target_triple}",
746                    );
747                }
748                // Note: it is the user's responsibility to provide a correct SO file.
749                // WATCH OUT: If an invalid/incorrect SO file is specified, this can cause
750                // undefined behaviour in Miri itself!
751                (
752                    unsafe {
753                        libloading::Library::new(lib_file_path)
754                            .expect("failed to read specified extern shared object file")
755                    },
756                    lib_file_path.clone(),
757                )
758            }).collect(),
759            #[cfg(not(unix))]
760            native_lib: config.native_lib.iter().map(|_| {
761                panic!("calling functions from native libraries via FFI is only supported on Unix")
762            }).collect(),
763            gc_interval: config.gc_interval,
764            since_gc: 0,
765            num_cpus: config.num_cpus,
766            page_size,
767            stack_addr,
768            stack_size,
769            collect_leak_backtraces: config.collect_leak_backtraces,
770            allocation_spans: RefCell::new(FxHashMap::default()),
771            const_cache: RefCell::new(FxHashMap::default()),
772            symbolic_alignment: RefCell::new(FxHashMap::default()),
773            union_data_ranges: FxHashMap::default(),
774            pthread_mutex_sanity: Cell::new(false),
775            pthread_rwlock_sanity: Cell::new(false),
776            pthread_condvar_sanity: Cell::new(false),
777            sb_extern_type_warned: Cell::new(false),
778            #[cfg(unix)]
779            native_call_mem_warned: Cell::new(false),
780            reject_in_isolation_warned: Default::default(),
781            int2ptr_warned: Default::default(),
782            mangle_internal_symbol_cache: Default::default(),
783            force_intrinsic_fallback: config.force_intrinsic_fallback,
784            float_nondet: config.float_nondet,
785        }
786    }
787
788    pub(crate) fn late_init(
789        ecx: &mut MiriInterpCx<'tcx>,
790        config: &MiriConfig,
791        on_main_stack_empty: StackEmptyCallback<'tcx>,
792    ) -> InterpResult<'tcx> {
793        EnvVars::init(ecx, config)?;
794        MiriMachine::init_extern_statics(ecx)?;
795        ThreadManager::init(ecx, on_main_stack_empty);
796        interp_ok(())
797    }
798
799    pub(crate) fn add_extern_static(ecx: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) {
800        // This got just allocated, so there definitely is a pointer here.
801        let ptr = ptr.into_pointer_or_addr().unwrap();
802        ecx.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
803    }
804
805    pub(crate) fn communicate(&self) -> bool {
806        self.isolated_op == IsolatedOp::Allow
807    }
808
809    /// Check whether the stack frame that this `FrameInfo` refers to is part of a local crate.
810    pub(crate) fn is_local(&self, frame: &FrameInfo<'_>) -> bool {
811        let def_id = frame.instance.def_id();
812        def_id.is_local() || self.local_crates.contains(&def_id.krate)
813    }
814
815    /// Called when the interpreter is going to shut down abnormally, such as due to a Ctrl-C.
816    pub(crate) fn handle_abnormal_termination(&mut self) {
817        // All strings in the profile data are stored in a single string table which is not
818        // written to disk until the profiler is dropped. If the interpreter exits without dropping
819        // the profiler, it is not possible to interpret the profile data and all measureme tools
820        // will panic when given the file.
821        drop(self.profiler.take());
822    }
823
824    pub(crate) fn page_align(&self) -> Align {
825        Align::from_bytes(self.page_size).unwrap()
826    }
827
828    pub(crate) fn allocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
829        self.allocation_spans
830            .borrow()
831            .get(&alloc_id)
832            .map(|(allocated, _deallocated)| allocated.data())
833    }
834
835    pub(crate) fn deallocated_span(&self, alloc_id: AllocId) -> Option<SpanData> {
836        self.allocation_spans
837            .borrow()
838            .get(&alloc_id)
839            .and_then(|(_allocated, deallocated)| *deallocated)
840            .map(Span::data)
841    }
842
843    fn init_allocation(
844        ecx: &MiriInterpCx<'tcx>,
845        id: AllocId,
846        kind: MemoryKind,
847        size: Size,
848        align: Align,
849    ) -> InterpResult<'tcx, AllocExtra<'tcx>> {
850        if ecx.machine.tracked_alloc_ids.contains(&id) {
851            ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(id, size, align, kind));
852        }
853
854        let borrow_tracker = ecx
855            .machine
856            .borrow_tracker
857            .as_ref()
858            .map(|bt| bt.borrow_mut().new_allocation(id, size, kind, &ecx.machine));
859
860        let data_race = match &ecx.machine.data_race {
861            GlobalDataRaceHandler::None => AllocDataRaceHandler::None,
862            GlobalDataRaceHandler::Vclocks(data_race) =>
863                AllocDataRaceHandler::Vclocks(
864                    data_race::AllocState::new_allocation(
865                        data_race,
866                        &ecx.machine.threads,
867                        size,
868                        kind,
869                        ecx.machine.current_span(),
870                    ),
871                    data_race.weak_memory.then(weak_memory::AllocState::new_allocation),
872                ),
873            GlobalDataRaceHandler::Genmc(_genmc_ctx) => {
874                // GenMC learns about new allocations directly from the alloc_addresses module,
875                // since it has to be able to control the address at which they are placed.
876                AllocDataRaceHandler::Genmc
877            }
878        };
879
880        // If an allocation is leaked, we want to report a backtrace to indicate where it was
881        // allocated. We don't need to record a backtrace for allocations which are allowed to
882        // leak.
883        let backtrace = if kind.may_leak() || !ecx.machine.collect_leak_backtraces {
884            None
885        } else {
886            Some(ecx.generate_stacktrace())
887        };
888
889        if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) {
890            ecx.machine
891                .allocation_spans
892                .borrow_mut()
893                .insert(id, (ecx.machine.current_span(), None));
894        }
895
896        interp_ok(AllocExtra { borrow_tracker, data_race, backtrace, sync: FxHashMap::default() })
897    }
898}
899
900impl VisitProvenance for MiriMachine<'_> {
901    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
902        #[rustfmt::skip]
903        let MiriMachine {
904            threads,
905            thread_cpu_affinity: _,
906            sync: _,
907            tls,
908            env_vars,
909            main_fn_ret_place,
910            argc,
911            argv,
912            cmd_line,
913            extern_statics,
914            dirs,
915            borrow_tracker,
916            data_race,
917            alloc_addresses,
918            fds,
919            epoll_interests:_,
920            tcx: _,
921            isolated_op: _,
922            validation: _,
923            monotonic_clock: _,
924            layouts: _,
925            static_roots: _,
926            profiler: _,
927            string_cache: _,
928            exported_symbols_cache: _,
929            backtrace_style: _,
930            local_crates: _,
931            rng: _,
932            #[cfg(target_os = "linux")]
933            allocator: _,
934            tracked_alloc_ids: _,
935            track_alloc_accesses: _,
936            check_alignment: _,
937            cmpxchg_weak_failure_rate: _,
938            preemption_rate: _,
939            report_progress: _,
940            basic_block_count: _,
941            native_lib: _,
942            gc_interval: _,
943            since_gc: _,
944            num_cpus: _,
945            page_size: _,
946            stack_addr: _,
947            stack_size: _,
948            collect_leak_backtraces: _,
949            allocation_spans: _,
950            const_cache: _,
951            symbolic_alignment: _,
952            union_data_ranges: _,
953            pthread_mutex_sanity: _,
954            pthread_rwlock_sanity: _,
955            pthread_condvar_sanity: _,
956            sb_extern_type_warned: _,
957            #[cfg(unix)]
958            native_call_mem_warned: _,
959            reject_in_isolation_warned: _,
960            int2ptr_warned: _,
961            mangle_internal_symbol_cache: _,
962            force_intrinsic_fallback: _,
963            float_nondet: _,
964        } = self;
965
966        threads.visit_provenance(visit);
967        tls.visit_provenance(visit);
968        env_vars.visit_provenance(visit);
969        dirs.visit_provenance(visit);
970        fds.visit_provenance(visit);
971        data_race.visit_provenance(visit);
972        borrow_tracker.visit_provenance(visit);
973        alloc_addresses.visit_provenance(visit);
974        main_fn_ret_place.visit_provenance(visit);
975        argc.visit_provenance(visit);
976        argv.visit_provenance(visit);
977        cmd_line.visit_provenance(visit);
978        for ptr in extern_statics.values() {
979            ptr.visit_provenance(visit);
980        }
981    }
982}
983
984/// A rustc InterpCx for Miri.
985pub type MiriInterpCx<'tcx> = InterpCx<'tcx, MiriMachine<'tcx>>;
986
987/// A little trait that's useful to be inherited by extension traits.
988pub trait MiriInterpCxExt<'tcx> {
989    fn eval_context_ref<'a>(&'a self) -> &'a MiriInterpCx<'tcx>;
990    fn eval_context_mut<'a>(&'a mut self) -> &'a mut MiriInterpCx<'tcx>;
991}
992impl<'tcx> MiriInterpCxExt<'tcx> for MiriInterpCx<'tcx> {
993    #[inline(always)]
994    fn eval_context_ref(&self) -> &MiriInterpCx<'tcx> {
995        self
996    }
997    #[inline(always)]
998    fn eval_context_mut(&mut self) -> &mut MiriInterpCx<'tcx> {
999        self
1000    }
1001}
1002
1003/// Machine hook implementations.
1004impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
1005    type MemoryKind = MiriMemoryKind;
1006    type ExtraFnVal = DynSym;
1007
1008    type FrameExtra = FrameExtra<'tcx>;
1009    type AllocExtra = AllocExtra<'tcx>;
1010
1011    type Provenance = Provenance;
1012    type ProvenanceExtra = ProvenanceExtra;
1013    type Bytes = MiriAllocBytes;
1014
1015    type MemoryMap =
1016        MonoHashMap<AllocId, (MemoryKind, Allocation<Provenance, Self::AllocExtra, Self::Bytes>)>;
1017
1018    const GLOBAL_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Global);
1019
1020    const PANIC_ON_ALLOC_FAIL: bool = false;
1021
1022    #[inline(always)]
1023    fn enforce_alignment(ecx: &MiriInterpCx<'tcx>) -> bool {
1024        ecx.machine.check_alignment != AlignmentCheck::None
1025    }
1026
1027    #[inline(always)]
1028    fn alignment_check(
1029        ecx: &MiriInterpCx<'tcx>,
1030        alloc_id: AllocId,
1031        alloc_align: Align,
1032        alloc_kind: AllocKind,
1033        offset: Size,
1034        align: Align,
1035    ) -> Option<Misalignment> {
1036        if ecx.machine.check_alignment != AlignmentCheck::Symbolic {
1037            // Just use the built-in check.
1038            return None;
1039        }
1040        if alloc_kind != AllocKind::LiveData {
1041            // Can't have any extra info here.
1042            return None;
1043        }
1044        // Let's see which alignment we have been promised for this allocation.
1045        let (promised_offset, promised_align) = ecx
1046            .machine
1047            .symbolic_alignment
1048            .borrow()
1049            .get(&alloc_id)
1050            .copied()
1051            .unwrap_or((Size::ZERO, alloc_align));
1052        if promised_align < align {
1053            // Definitely not enough.
1054            Some(Misalignment { has: promised_align, required: align })
1055        } else {
1056            // What's the offset between us and the promised alignment?
1057            let distance = offset.bytes().wrapping_sub(promised_offset.bytes());
1058            // That must also be aligned.
1059            if distance % align.bytes() == 0 {
1060                // All looking good!
1061                None
1062            } else {
1063                // The biggest power of two through which `distance` is divisible.
1064                let distance_pow2 = 1 << distance.trailing_zeros();
1065                Some(Misalignment {
1066                    has: Align::from_bytes(distance_pow2).unwrap(),
1067                    required: align,
1068                })
1069            }
1070        }
1071    }
1072
1073    #[inline(always)]
1074    fn enforce_validity(ecx: &MiriInterpCx<'tcx>, _layout: TyAndLayout<'tcx>) -> bool {
1075        ecx.machine.validation != ValidationMode::No
1076    }
1077    #[inline(always)]
1078    fn enforce_validity_recursively(
1079        ecx: &InterpCx<'tcx, Self>,
1080        _layout: TyAndLayout<'tcx>,
1081    ) -> bool {
1082        ecx.machine.validation == ValidationMode::Deep
1083    }
1084
1085    #[inline(always)]
1086    fn ignore_optional_overflow_checks(ecx: &MiriInterpCx<'tcx>) -> bool {
1087        !ecx.tcx.sess.overflow_checks()
1088    }
1089
1090    fn check_fn_target_features(
1091        ecx: &MiriInterpCx<'tcx>,
1092        instance: ty::Instance<'tcx>,
1093    ) -> InterpResult<'tcx> {
1094        let attrs = ecx.tcx.codegen_fn_attrs(instance.def_id());
1095        if attrs
1096            .target_features
1097            .iter()
1098            .any(|feature| !ecx.tcx.sess.target_features.contains(&feature.name))
1099        {
1100            let unavailable = attrs
1101                .target_features
1102                .iter()
1103                .filter(|&feature| {
1104                    !feature.implied && !ecx.tcx.sess.target_features.contains(&feature.name)
1105                })
1106                .fold(String::new(), |mut s, feature| {
1107                    if !s.is_empty() {
1108                        s.push_str(", ");
1109                    }
1110                    s.push_str(feature.name.as_str());
1111                    s
1112                });
1113            let msg = format!(
1114                "calling a function that requires unavailable target features: {unavailable}"
1115            );
1116            // On WASM, this is not UB, but instead gets rejected during validation of the module
1117            // (see #84988).
1118            if ecx.tcx.sess.target.is_like_wasm {
1119                throw_machine_stop!(TerminationInfo::Abort(msg));
1120            } else {
1121                throw_ub_format!("{msg}");
1122            }
1123        }
1124        interp_ok(())
1125    }
1126
1127    #[inline(always)]
1128    fn find_mir_or_eval_fn(
1129        ecx: &mut MiriInterpCx<'tcx>,
1130        instance: ty::Instance<'tcx>,
1131        abi: &FnAbi<'tcx, Ty<'tcx>>,
1132        args: &[FnArg<'tcx, Provenance>],
1133        dest: &PlaceTy<'tcx>,
1134        ret: Option<mir::BasicBlock>,
1135        unwind: mir::UnwindAction,
1136    ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
1137        // For foreign items, try to see if we can emulate them.
1138        if ecx.tcx.is_foreign_item(instance.def_id()) {
1139            // An external function call that does not have a MIR body. We either find MIR elsewhere
1140            // or emulate its effect.
1141            // This will be Ok(None) if we're emulating the intrinsic entirely within Miri (no need
1142            // to run extra MIR), and Ok(Some(body)) if we found MIR to run for the
1143            // foreign function
1144            // Any needed call to `goto_block` will be performed by `emulate_foreign_item`.
1145            let args = ecx.copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit?
1146            let link_name = Symbol::intern(ecx.tcx.symbol_name(instance).name);
1147            return ecx.emulate_foreign_item(link_name, abi, &args, dest, ret, unwind);
1148        }
1149
1150        // Otherwise, load the MIR.
1151        interp_ok(Some((ecx.load_mir(instance.def, None)?, instance)))
1152    }
1153
1154    #[inline(always)]
1155    fn call_extra_fn(
1156        ecx: &mut MiriInterpCx<'tcx>,
1157        fn_val: DynSym,
1158        abi: &FnAbi<'tcx, Ty<'tcx>>,
1159        args: &[FnArg<'tcx, Provenance>],
1160        dest: &PlaceTy<'tcx>,
1161        ret: Option<mir::BasicBlock>,
1162        unwind: mir::UnwindAction,
1163    ) -> InterpResult<'tcx> {
1164        let args = ecx.copy_fn_args(args); // FIXME: Should `InPlace` arguments be reset to uninit?
1165        ecx.emulate_dyn_sym(fn_val, abi, &args, dest, ret, unwind)
1166    }
1167
1168    #[inline(always)]
1169    fn call_intrinsic(
1170        ecx: &mut MiriInterpCx<'tcx>,
1171        instance: ty::Instance<'tcx>,
1172        args: &[OpTy<'tcx>],
1173        dest: &PlaceTy<'tcx>,
1174        ret: Option<mir::BasicBlock>,
1175        unwind: mir::UnwindAction,
1176    ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
1177        ecx.call_intrinsic(instance, args, dest, ret, unwind)
1178    }
1179
1180    #[inline(always)]
1181    fn assert_panic(
1182        ecx: &mut MiriInterpCx<'tcx>,
1183        msg: &mir::AssertMessage<'tcx>,
1184        unwind: mir::UnwindAction,
1185    ) -> InterpResult<'tcx> {
1186        ecx.assert_panic(msg, unwind)
1187    }
1188
1189    fn panic_nounwind(ecx: &mut InterpCx<'tcx, Self>, msg: &str) -> InterpResult<'tcx> {
1190        ecx.start_panic_nounwind(msg)
1191    }
1192
1193    fn unwind_terminate(
1194        ecx: &mut InterpCx<'tcx, Self>,
1195        reason: mir::UnwindTerminateReason,
1196    ) -> InterpResult<'tcx> {
1197        // Call the lang item.
1198        let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap();
1199        let panic = ty::Instance::mono(ecx.tcx.tcx, panic);
1200        ecx.call_function(
1201            panic,
1202            ExternAbi::Rust,
1203            &[],
1204            None,
1205            StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
1206        )?;
1207        interp_ok(())
1208    }
1209
1210    #[inline(always)]
1211    fn binary_ptr_op(
1212        ecx: &MiriInterpCx<'tcx>,
1213        bin_op: mir::BinOp,
1214        left: &ImmTy<'tcx>,
1215        right: &ImmTy<'tcx>,
1216    ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1217        ecx.binary_ptr_op(bin_op, left, right)
1218    }
1219
1220    #[inline(always)]
1221    fn generate_nan<F1: Float + FloatConvert<F2>, F2: Float>(
1222        ecx: &InterpCx<'tcx, Self>,
1223        inputs: &[F1],
1224    ) -> F2 {
1225        ecx.generate_nan(inputs)
1226    }
1227
1228    #[inline(always)]
1229    fn apply_float_nondet(
1230        ecx: &mut InterpCx<'tcx, Self>,
1231        val: ImmTy<'tcx>,
1232    ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1233        crate::math::apply_random_float_error_to_imm(ecx, val, 2 /* log2(4) */)
1234    }
1235
1236    #[inline(always)]
1237    fn equal_float_min_max<F: Float>(ecx: &MiriInterpCx<'tcx>, a: F, b: F) -> F {
1238        ecx.equal_float_min_max(a, b)
1239    }
1240
1241    #[inline(always)]
1242    fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
1243        interp_ok(ecx.tcx.sess.ub_checks())
1244    }
1245
1246    #[inline(always)]
1247    fn contract_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
1248        interp_ok(ecx.tcx.sess.contract_checks())
1249    }
1250
1251    #[inline(always)]
1252    fn thread_local_static_pointer(
1253        ecx: &mut MiriInterpCx<'tcx>,
1254        def_id: DefId,
1255    ) -> InterpResult<'tcx, StrictPointer> {
1256        ecx.get_or_create_thread_local_alloc(def_id)
1257    }
1258
1259    fn extern_static_pointer(
1260        ecx: &MiriInterpCx<'tcx>,
1261        def_id: DefId,
1262    ) -> InterpResult<'tcx, StrictPointer> {
1263        let link_name = Symbol::intern(ecx.tcx.symbol_name(Instance::mono(*ecx.tcx, def_id)).name);
1264        if let Some(&ptr) = ecx.machine.extern_statics.get(&link_name) {
1265            // Various parts of the engine rely on `get_alloc_info` for size and alignment
1266            // information. That uses the type information of this static.
1267            // Make sure it matches the Miri allocation for this.
1268            let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
1269                panic!("extern_statics cannot contain wildcards")
1270            };
1271            let info = ecx.get_alloc_info(alloc_id);
1272            let def_ty = ecx.tcx.type_of(def_id).instantiate_identity();
1273            let extern_decl_layout =
1274                ecx.tcx.layout_of(ecx.typing_env().as_query_input(def_ty)).unwrap();
1275            if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align {
1276                throw_unsup_format!(
1277                    "extern static `{link_name}` has been declared as `{krate}::{name}` \
1278                    with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
1279                    but Miri emulates it via an extern static shim \
1280                    with a size of {shim_size} bytes and alignment of {shim_align} bytes",
1281                    name = ecx.tcx.def_path_str(def_id),
1282                    krate = ecx.tcx.crate_name(def_id.krate),
1283                    decl_size = extern_decl_layout.size.bytes(),
1284                    decl_align = extern_decl_layout.align.abi.bytes(),
1285                    shim_size = info.size.bytes(),
1286                    shim_align = info.align.bytes(),
1287                )
1288            }
1289            interp_ok(ptr)
1290        } else {
1291            throw_unsup_format!("extern static `{link_name}` is not supported by Miri",)
1292        }
1293    }
1294
1295    fn init_local_allocation(
1296        ecx: &MiriInterpCx<'tcx>,
1297        id: AllocId,
1298        kind: MemoryKind,
1299        size: Size,
1300        align: Align,
1301    ) -> InterpResult<'tcx, Self::AllocExtra> {
1302        assert!(kind != MiriMemoryKind::Global.into());
1303        MiriMachine::init_allocation(ecx, id, kind, size, align)
1304    }
1305
1306    fn adjust_alloc_root_pointer(
1307        ecx: &MiriInterpCx<'tcx>,
1308        ptr: interpret::Pointer<CtfeProvenance>,
1309        kind: Option<MemoryKind>,
1310    ) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
1311        let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None");
1312        let alloc_id = ptr.provenance.alloc_id();
1313        if cfg!(debug_assertions) {
1314            // The machine promises to never call us on thread-local or extern statics.
1315            match ecx.tcx.try_get_global_alloc(alloc_id) {
1316                Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_thread_local_static(def_id) => {
1317                    panic!("adjust_alloc_root_pointer called on thread-local static")
1318                }
1319                Some(GlobalAlloc::Static(def_id)) if ecx.tcx.is_foreign_item(def_id) => {
1320                    panic!("adjust_alloc_root_pointer called on extern static")
1321                }
1322                _ => {}
1323            }
1324        }
1325        // FIXME: can we somehow preserve the immutability of `ptr`?
1326        let tag = if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
1327            borrow_tracker.borrow_mut().root_ptr_tag(alloc_id, &ecx.machine)
1328        } else {
1329            // Value does not matter, SB is disabled
1330            BorTag::default()
1331        };
1332        ecx.adjust_alloc_root_pointer(ptr, tag, kind)
1333    }
1334
1335    /// Called on `usize as ptr` casts.
1336    #[inline(always)]
1337    fn ptr_from_addr_cast(ecx: &MiriInterpCx<'tcx>, addr: u64) -> InterpResult<'tcx, Pointer> {
1338        ecx.ptr_from_addr_cast(addr)
1339    }
1340
1341    /// Called on `ptr as usize` casts.
1342    /// (Actually computing the resulting `usize` doesn't need machine help,
1343    /// that's just `Scalar::try_to_int`.)
1344    #[inline(always)]
1345    fn expose_provenance(
1346        ecx: &InterpCx<'tcx, Self>,
1347        provenance: Self::Provenance,
1348    ) -> InterpResult<'tcx> {
1349        ecx.expose_provenance(provenance)
1350    }
1351
1352    /// Convert a pointer with provenance into an allocation-offset pair and extra provenance info.
1353    /// `size` says how many bytes of memory are expected at that pointer. The *sign* of `size` can
1354    /// be used to disambiguate situations where a wildcard pointer sits right in between two
1355    /// allocations.
1356    ///
1357    /// If `ptr.provenance.get_alloc_id()` is `Some(p)`, the returned `AllocId` must be `p`.
1358    /// The resulting `AllocId` will just be used for that one step and the forgotten again
1359    /// (i.e., we'll never turn the data returned here back into a `Pointer` that might be
1360    /// stored in machine state).
1361    ///
1362    /// When this fails, that means the pointer does not point to a live allocation.
1363    fn ptr_get_alloc(
1364        ecx: &MiriInterpCx<'tcx>,
1365        ptr: StrictPointer,
1366        size: i64,
1367    ) -> Option<(AllocId, Size, Self::ProvenanceExtra)> {
1368        let rel = ecx.ptr_get_alloc(ptr, size);
1369
1370        rel.map(|(alloc_id, size)| {
1371            let tag = match ptr.provenance {
1372                Provenance::Concrete { tag, .. } => ProvenanceExtra::Concrete(tag),
1373                Provenance::Wildcard => ProvenanceExtra::Wildcard,
1374            };
1375            (alloc_id, size, tag)
1376        })
1377    }
1378
1379    /// Called to adjust global allocations to the Provenance and AllocExtra of this machine.
1380    ///
1381    /// If `alloc` contains pointers, then they are all pointing to globals.
1382    ///
1383    /// This should avoid copying if no work has to be done! If this returns an owned
1384    /// allocation (because a copy had to be done to adjust things), machine memory will
1385    /// cache the result. (This relies on `AllocMap::get_or` being able to add the
1386    /// owned allocation to the map even when the map is shared.)
1387    fn adjust_global_allocation<'b>(
1388        ecx: &InterpCx<'tcx, Self>,
1389        id: AllocId,
1390        alloc: &'b Allocation,
1391    ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
1392    {
1393        let alloc = alloc.adjust_from_tcx(
1394            &ecx.tcx,
1395            |bytes, align| ecx.get_global_alloc_bytes(id, bytes, align),
1396            |ptr| ecx.global_root_pointer(ptr),
1397        )?;
1398        let kind = MiriMemoryKind::Global.into();
1399        let extra = MiriMachine::init_allocation(ecx, id, kind, alloc.size(), alloc.align)?;
1400        interp_ok(Cow::Owned(alloc.with_extra(extra)))
1401    }
1402
1403    #[inline(always)]
1404    fn before_memory_read(
1405        _tcx: TyCtxtAt<'tcx>,
1406        machine: &Self,
1407        alloc_extra: &AllocExtra<'tcx>,
1408        ptr: Pointer,
1409        (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1410        range: AllocRange,
1411    ) -> InterpResult<'tcx> {
1412        if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1413            machine
1414                .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Read));
1415        }
1416        // The order of checks is deliberate, to prefer reporting a data race over a borrow tracker error.
1417        match &machine.data_race {
1418            GlobalDataRaceHandler::None => {}
1419            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1420                genmc_ctx.memory_load(machine, ptr.addr(), range.size)?,
1421            GlobalDataRaceHandler::Vclocks(_data_race) => {
1422                let AllocDataRaceHandler::Vclocks(data_race, weak_memory) = &alloc_extra.data_race
1423                else {
1424                    unreachable!();
1425                };
1426                data_race.read(alloc_id, range, NaReadType::Read, None, machine)?;
1427                if let Some(weak_memory) = weak_memory {
1428                    weak_memory.memory_accessed(range, machine.data_race.as_vclocks_ref().unwrap());
1429                }
1430            }
1431        }
1432        if let Some(borrow_tracker) = &alloc_extra.borrow_tracker {
1433            borrow_tracker.before_memory_read(alloc_id, prov_extra, range, machine)?;
1434        }
1435        interp_ok(())
1436    }
1437
1438    #[inline(always)]
1439    fn before_memory_write(
1440        _tcx: TyCtxtAt<'tcx>,
1441        machine: &mut Self,
1442        alloc_extra: &mut AllocExtra<'tcx>,
1443        ptr: Pointer,
1444        (alloc_id, prov_extra): (AllocId, Self::ProvenanceExtra),
1445        range: AllocRange,
1446    ) -> InterpResult<'tcx> {
1447        if machine.track_alloc_accesses && machine.tracked_alloc_ids.contains(&alloc_id) {
1448            machine
1449                .emit_diagnostic(NonHaltingDiagnostic::AccessedAlloc(alloc_id, AccessKind::Write));
1450        }
1451        match &machine.data_race {
1452            GlobalDataRaceHandler::None => {}
1453            GlobalDataRaceHandler::Genmc(genmc_ctx) => {
1454                genmc_ctx.memory_store(machine, ptr.addr(), range.size)?;
1455            }
1456            GlobalDataRaceHandler::Vclocks(_global_state) => {
1457                let AllocDataRaceHandler::Vclocks(data_race, weak_memory) =
1458                    &mut alloc_extra.data_race
1459                else {
1460                    unreachable!()
1461                };
1462                data_race.write(alloc_id, range, NaWriteType::Write, None, machine)?;
1463                if let Some(weak_memory) = weak_memory {
1464                    weak_memory.memory_accessed(range, machine.data_race.as_vclocks_ref().unwrap());
1465                }
1466            }
1467        }
1468        if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1469            borrow_tracker.before_memory_write(alloc_id, prov_extra, range, machine)?;
1470        }
1471        interp_ok(())
1472    }
1473
1474    #[inline(always)]
1475    fn before_memory_deallocation(
1476        _tcx: TyCtxtAt<'tcx>,
1477        machine: &mut Self,
1478        alloc_extra: &mut AllocExtra<'tcx>,
1479        ptr: Pointer,
1480        (alloc_id, prove_extra): (AllocId, Self::ProvenanceExtra),
1481        size: Size,
1482        align: Align,
1483        kind: MemoryKind,
1484    ) -> InterpResult<'tcx> {
1485        if machine.tracked_alloc_ids.contains(&alloc_id) {
1486            machine.emit_diagnostic(NonHaltingDiagnostic::FreedAlloc(alloc_id));
1487        }
1488        match &machine.data_race {
1489            GlobalDataRaceHandler::None => {}
1490            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
1491                genmc_ctx.handle_dealloc(machine, ptr.addr(), size, align, kind)?,
1492            GlobalDataRaceHandler::Vclocks(_global_state) => {
1493                let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap();
1494                data_race.write(
1495                    alloc_id,
1496                    alloc_range(Size::ZERO, size),
1497                    NaWriteType::Deallocate,
1498                    None,
1499                    machine,
1500                )?;
1501            }
1502        }
1503        if let Some(borrow_tracker) = &mut alloc_extra.borrow_tracker {
1504            borrow_tracker.before_memory_deallocation(alloc_id, prove_extra, size, machine)?;
1505        }
1506        if let Some((_, deallocated_at)) = machine.allocation_spans.borrow_mut().get_mut(&alloc_id)
1507        {
1508            *deallocated_at = Some(machine.current_span());
1509        }
1510        machine.free_alloc_id(alloc_id, size, align, kind);
1511        interp_ok(())
1512    }
1513
1514    #[inline(always)]
1515    fn retag_ptr_value(
1516        ecx: &mut InterpCx<'tcx, Self>,
1517        kind: mir::RetagKind,
1518        val: &ImmTy<'tcx>,
1519    ) -> InterpResult<'tcx, ImmTy<'tcx>> {
1520        if ecx.machine.borrow_tracker.is_some() {
1521            ecx.retag_ptr_value(kind, val)
1522        } else {
1523            interp_ok(val.clone())
1524        }
1525    }
1526
1527    #[inline(always)]
1528    fn retag_place_contents(
1529        ecx: &mut InterpCx<'tcx, Self>,
1530        kind: mir::RetagKind,
1531        place: &PlaceTy<'tcx>,
1532    ) -> InterpResult<'tcx> {
1533        if ecx.machine.borrow_tracker.is_some() {
1534            ecx.retag_place_contents(kind, place)?;
1535        }
1536        interp_ok(())
1537    }
1538
1539    fn protect_in_place_function_argument(
1540        ecx: &mut InterpCx<'tcx, Self>,
1541        place: &MPlaceTy<'tcx>,
1542    ) -> InterpResult<'tcx> {
1543        // If we have a borrow tracker, we also have it set up protection so that all reads *and
1544        // writes* during this call are insta-UB.
1545        let protected_place = if ecx.machine.borrow_tracker.is_some() {
1546            ecx.protect_place(place)?
1547        } else {
1548            // No borrow tracker.
1549            place.clone()
1550        };
1551        // We do need to write `uninit` so that even after the call ends, the former contents of
1552        // this place cannot be observed any more. We do the write after retagging so that for
1553        // Tree Borrows, this is considered to activate the new tag.
1554        // Conveniently this also ensures that the place actually points to suitable memory.
1555        ecx.write_uninit(&protected_place)?;
1556        // Now we throw away the protected place, ensuring its tag is never used again.
1557        interp_ok(())
1558    }
1559
1560    #[inline(always)]
1561    fn init_frame(
1562        ecx: &mut InterpCx<'tcx, Self>,
1563        frame: Frame<'tcx, Provenance>,
1564    ) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
1565        // Start recording our event before doing anything else
1566        let timing = if let Some(profiler) = ecx.machine.profiler.as_ref() {
1567            let fn_name = frame.instance().to_string();
1568            let entry = ecx.machine.string_cache.entry(fn_name.clone());
1569            let name = entry.or_insert_with(|| profiler.alloc_string(&*fn_name));
1570
1571            Some(profiler.start_recording_interval_event_detached(
1572                *name,
1573                measureme::EventId::from_label(*name),
1574                ecx.active_thread().to_u32(),
1575            ))
1576        } else {
1577            None
1578        };
1579
1580        let borrow_tracker = ecx.machine.borrow_tracker.as_ref();
1581
1582        let extra = FrameExtra {
1583            borrow_tracker: borrow_tracker.map(|bt| bt.borrow_mut().new_frame()),
1584            catch_unwind: None,
1585            timing,
1586            is_user_relevant: ecx.machine.is_user_relevant(&frame),
1587            salt: ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL),
1588            data_race: ecx
1589                .machine
1590                .data_race
1591                .as_vclocks_ref()
1592                .map(|_| data_race::FrameState::default()),
1593        };
1594
1595        interp_ok(frame.with_extra(extra))
1596    }
1597
1598    fn stack<'a>(
1599        ecx: &'a InterpCx<'tcx, Self>,
1600    ) -> &'a [Frame<'tcx, Self::Provenance, Self::FrameExtra>] {
1601        ecx.active_thread_stack()
1602    }
1603
1604    fn stack_mut<'a>(
1605        ecx: &'a mut InterpCx<'tcx, Self>,
1606    ) -> &'a mut Vec<Frame<'tcx, Self::Provenance, Self::FrameExtra>> {
1607        ecx.active_thread_stack_mut()
1608    }
1609
1610    fn before_terminator(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1611        ecx.machine.basic_block_count += 1u64; // a u64 that is only incremented by 1 will "never" overflow
1612        ecx.machine.since_gc += 1;
1613        // Possibly report our progress. This will point at the terminator we are about to execute.
1614        if let Some(report_progress) = ecx.machine.report_progress {
1615            if ecx.machine.basic_block_count % u64::from(report_progress) == 0 {
1616                ecx.emit_diagnostic(NonHaltingDiagnostic::ProgressReport {
1617                    block_count: ecx.machine.basic_block_count,
1618                });
1619            }
1620        }
1621
1622        // Search for BorTags to find all live pointers, then remove all other tags from borrow
1623        // stacks.
1624        // When debug assertions are enabled, run the GC as often as possible so that any cases
1625        // where it mistakenly removes an important tag become visible.
1626        if ecx.machine.gc_interval > 0 && ecx.machine.since_gc >= ecx.machine.gc_interval {
1627            ecx.machine.since_gc = 0;
1628            ecx.run_provenance_gc();
1629        }
1630
1631        // These are our preemption points.
1632        // (This will only take effect after the terminator has been executed.)
1633        ecx.maybe_preempt_active_thread();
1634
1635        // Make sure some time passes.
1636        ecx.machine.monotonic_clock.tick();
1637
1638        interp_ok(())
1639    }
1640
1641    #[inline(always)]
1642    fn after_stack_push(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1643        if ecx.frame().extra.is_user_relevant {
1644            // We just pushed a local frame, so we know that the topmost local frame is the topmost
1645            // frame. If we push a non-local frame, there's no need to do anything.
1646            let stack_len = ecx.active_thread_stack().len();
1647            ecx.active_thread_mut().set_top_user_relevant_frame(stack_len - 1);
1648        }
1649        interp_ok(())
1650    }
1651
1652    fn before_stack_pop(ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
1653        let frame = ecx.frame();
1654        // We want this *before* the return value copy, because the return place itself is protected
1655        // until we do `on_stack_pop` here, and we need to un-protect it to copy the return value.
1656        if ecx.machine.borrow_tracker.is_some() {
1657            ecx.on_stack_pop(frame)?;
1658        }
1659        if frame.extra.is_user_relevant {
1660            // All that we store is whether or not the frame we just removed is local, so now we
1661            // have no idea where the next topmost local frame is. So we recompute it.
1662            // (If this ever becomes a bottleneck, we could have `push` store the previous
1663            // user-relevant frame and restore that here.)
1664            // We have to skip the frame that is just being popped.
1665            ecx.active_thread_mut().recompute_top_user_relevant_frame(/* skip */ 1);
1666        }
1667        // tracing-tree can autoamtically annotate scope changes, but it gets very confused by our
1668        // concurrency and what it prints is just plain wrong. So we print our own information
1669        // instead. (Cc https://github.com/rust-lang/miri/issues/2266)
1670        info!("Leaving {}", ecx.frame().instance());
1671        interp_ok(())
1672    }
1673
1674    #[inline(always)]
1675    fn after_stack_pop(
1676        ecx: &mut InterpCx<'tcx, Self>,
1677        frame: Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1678        unwinding: bool,
1679    ) -> InterpResult<'tcx, ReturnAction> {
1680        let res = {
1681            // Move `frame` into a sub-scope so we control when it will be dropped.
1682            let mut frame = frame;
1683            let timing = frame.extra.timing.take();
1684            let res = ecx.handle_stack_pop_unwind(frame.extra, unwinding);
1685            if let Some(profiler) = ecx.machine.profiler.as_ref() {
1686                profiler.finish_recording_interval_event(timing.unwrap());
1687            }
1688            res
1689        };
1690        // Needs to be done after dropping frame to show up on the right nesting level.
1691        // (Cc https://github.com/rust-lang/miri/issues/2266)
1692        if !ecx.active_thread_stack().is_empty() {
1693            info!("Continuing in {}", ecx.frame().instance());
1694        }
1695        res
1696    }
1697
1698    fn after_local_read(
1699        ecx: &InterpCx<'tcx, Self>,
1700        frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
1701        local: mir::Local,
1702    ) -> InterpResult<'tcx> {
1703        if let Some(data_race) = &frame.extra.data_race {
1704            data_race.local_read(local, &ecx.machine);
1705        }
1706        interp_ok(())
1707    }
1708
1709    fn after_local_write(
1710        ecx: &mut InterpCx<'tcx, Self>,
1711        local: mir::Local,
1712        storage_live: bool,
1713    ) -> InterpResult<'tcx> {
1714        if let Some(data_race) = &ecx.frame().extra.data_race {
1715            data_race.local_write(local, storage_live, &ecx.machine);
1716        }
1717        interp_ok(())
1718    }
1719
1720    fn after_local_moved_to_memory(
1721        ecx: &mut InterpCx<'tcx, Self>,
1722        local: mir::Local,
1723        mplace: &MPlaceTy<'tcx>,
1724    ) -> InterpResult<'tcx> {
1725        let Some(Provenance::Concrete { alloc_id, .. }) = mplace.ptr().provenance else {
1726            panic!("after_local_allocated should only be called on fresh allocations");
1727        };
1728        // Record the span where this was allocated: the declaration of the local.
1729        let local_decl = &ecx.frame().body().local_decls[local];
1730        let span = local_decl.source_info.span;
1731        ecx.machine.allocation_spans.borrow_mut().insert(alloc_id, (span, None));
1732        // The data race system has to fix the clocks used for this write.
1733        let (alloc_info, machine) = ecx.get_alloc_extra_mut(alloc_id)?;
1734        if let Some(data_race) =
1735            &machine.threads.active_thread_stack().last().unwrap().extra.data_race
1736        {
1737            data_race.local_moved_to_memory(
1738                local,
1739                alloc_info.data_race.as_vclocks_mut().unwrap(),
1740                machine,
1741            );
1742        }
1743        interp_ok(())
1744    }
1745
1746    fn eval_mir_constant<F>(
1747        ecx: &InterpCx<'tcx, Self>,
1748        val: mir::Const<'tcx>,
1749        span: Span,
1750        layout: Option<TyAndLayout<'tcx>>,
1751        eval: F,
1752    ) -> InterpResult<'tcx, OpTy<'tcx>>
1753    where
1754        F: Fn(
1755            &InterpCx<'tcx, Self>,
1756            mir::Const<'tcx>,
1757            Span,
1758            Option<TyAndLayout<'tcx>>,
1759        ) -> InterpResult<'tcx, OpTy<'tcx>>,
1760    {
1761        let frame = ecx.active_thread_stack().last().unwrap();
1762        let mut cache = ecx.machine.const_cache.borrow_mut();
1763        match cache.entry((val, frame.extra.salt)) {
1764            Entry::Vacant(ve) => {
1765                let op = eval(ecx, val, span, layout)?;
1766                ve.insert(op.clone());
1767                interp_ok(op)
1768            }
1769            Entry::Occupied(oe) => interp_ok(oe.get().clone()),
1770        }
1771    }
1772
1773    fn get_global_alloc_salt(
1774        ecx: &InterpCx<'tcx, Self>,
1775        instance: Option<ty::Instance<'tcx>>,
1776    ) -> usize {
1777        let unique = if let Some(instance) = instance {
1778            // Functions cannot be identified by pointers, as asm-equal functions can get
1779            // deduplicated by the linker (we set the "unnamed_addr" attribute for LLVM) and
1780            // functions can be duplicated across crates. We thus generate a new `AllocId` for every
1781            // mention of a function. This means that `main as fn() == main as fn()` is false, while
1782            // `let x = main as fn(); x == x` is true. However, as a quality-of-life feature it can
1783            // be useful to identify certain functions uniquely, e.g. for backtraces. So we identify
1784            // whether codegen will actually emit duplicate functions. It does that when they have
1785            // non-lifetime generics, or when they can be inlined. All other functions are given a
1786            // unique address. This is not a stable guarantee! The `inline` attribute is a hint and
1787            // cannot be relied upon for anything. But if we don't do this, the
1788            // `__rust_begin_short_backtrace`/`__rust_end_short_backtrace` logic breaks and panic
1789            // backtraces look terrible.
1790            let is_generic = instance
1791                .args
1792                .into_iter()
1793                .any(|arg| !matches!(arg.kind(), ty::GenericArgKind::Lifetime(_)));
1794            let can_be_inlined = matches!(
1795                ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
1796                InliningThreshold::Always
1797            ) || !matches!(
1798                ecx.tcx.codegen_fn_attrs(instance.def_id()).inline,
1799                InlineAttr::Never
1800            );
1801            !is_generic && !can_be_inlined
1802        } else {
1803            // Non-functions are never unique.
1804            false
1805        };
1806        // Always use the same salt if the allocation is unique.
1807        if unique {
1808            CTFE_ALLOC_SALT
1809        } else {
1810            ecx.machine.rng.borrow_mut().random_range(0..ADDRS_PER_ANON_GLOBAL)
1811        }
1812    }
1813
1814    fn cached_union_data_range<'e>(
1815        ecx: &'e mut InterpCx<'tcx, Self>,
1816        ty: Ty<'tcx>,
1817        compute_range: impl FnOnce() -> RangeSet,
1818    ) -> Cow<'e, RangeSet> {
1819        Cow::Borrowed(ecx.machine.union_data_ranges.entry(ty).or_insert_with(compute_range))
1820    }
1821
1822    fn get_default_alloc_params(&self) -> <Self::Bytes as AllocBytes>::AllocParams {
1823        use crate::alloc::MiriAllocParams;
1824
1825        #[cfg(target_os = "linux")]
1826        match &self.allocator {
1827            Some(alloc) => MiriAllocParams::Isolated(alloc.clone()),
1828            None => MiriAllocParams::Global,
1829        }
1830        #[cfg(not(target_os = "linux"))]
1831        MiriAllocParams::Global
1832    }
1833}
1834
1835/// Trait for callbacks handling asynchronous machine operations.
1836pub trait MachineCallback<'tcx, T>: VisitProvenance {
1837    /// The function to be invoked when the callback is fired.
1838    fn call(
1839        self: Box<Self>,
1840        ecx: &mut InterpCx<'tcx, MiriMachine<'tcx>>,
1841        arg: T,
1842    ) -> InterpResult<'tcx>;
1843}
1844
1845/// Type alias for boxed machine callbacks with generic argument type.
1846pub type DynMachineCallback<'tcx, T> = Box<dyn MachineCallback<'tcx, T> + 'tcx>;
1847
1848/// Creates a `DynMachineCallback`:
1849///
1850/// ```rust
1851/// callback!(
1852///     @capture<'tcx> {
1853///         var1: Ty1,
1854///         var2: Ty2<'tcx>,
1855///     }
1856///     |this, arg: ArgTy| {
1857///         // Implement the callback here.
1858///         todo!()
1859///     }
1860/// )
1861/// ```
1862///
1863/// All the argument types must implement `VisitProvenance`.
1864#[macro_export]
1865macro_rules! callback {
1866    (@capture<$tcx:lifetime $(,)? $($lft:lifetime),*>
1867        { $($name:ident: $type:ty),* $(,)? }
1868     |$this:ident, $arg:ident: $arg_ty:ty| $body:expr $(,)?) => {{
1869        struct Callback<$tcx, $($lft),*> {
1870            $($name: $type,)*
1871            _phantom: std::marker::PhantomData<&$tcx ()>,
1872        }
1873
1874        impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> {
1875            fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
1876                $(
1877                    self.$name.visit_provenance(_visit);
1878                )*
1879            }
1880        }
1881
1882        impl<$tcx, $($lft),*> MachineCallback<$tcx, $arg_ty> for Callback<$tcx, $($lft),*> {
1883            fn call(
1884                self: Box<Self>,
1885                $this: &mut MiriInterpCx<$tcx>,
1886                $arg: $arg_ty
1887            ) -> InterpResult<$tcx> {
1888                #[allow(unused_variables)]
1889                let Callback { $($name,)* _phantom } = *self;
1890                $body
1891            }
1892        }
1893
1894        Box::new(Callback {
1895            $($name,)*
1896            _phantom: std::marker::PhantomData
1897        })
1898    }};
1899}