about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs9
-rw-r--r--compiler/rustc_trait_selection/src/traits/effects.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/specialize/mod.rs191
-rw-r--r--compiler/rustc_trait_selection/src/traits/util.rs35
-rw-r--r--library/core/src/array/mod.rs34
-rw-r--r--library/std/src/lib.rs2
-rw-r--r--library/std/src/os/fd/owned.rs21
-rw-r--r--library/std/src/sys/random/arc4random.rs2
-rw-r--r--library/std/src/thread/scoped.rs2
-rw-r--r--src/tools/miri/.cargo/config.toml9
-rw-r--r--src/tools/miri/README.md1
-rwxr-xr-xsrc/tools/miri/miri8
-rw-r--r--src/tools/miri/miri-script/src/util.rs4
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/concurrency/sync.rs118
-rw-r--r--src/tools/miri/src/concurrency/thread.rs9
-rw-r--r--src/tools/miri/src/diagnostics.rs2
-rw-r--r--src/tools/miri/src/eval.rs8
-rw-r--r--src/tools/miri/src/helpers.rs7
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs42
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs39
-rw-r--r--src/tools/miri/src/lib.rs3
-rw-r--r--src/tools/miri/src/machine.rs12
-rw-r--r--src/tools/miri/src/math.rs164
-rw-r--r--src/tools/miri/src/provenance_gc.rs20
-rw-r--r--src/tools/miri/src/shims/extern_static.rs65
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs26
-rw-r--r--src/tools/miri/src/shims/unix/android/thread.rs26
-rw-r--r--src/tools/miri/src/shims/unix/linux/eventfd.rs201
-rw-r--r--src/tools/miri/src/shims/unix/linux/sync.rs72
-rw-r--r--src/tools/miri/src/shims/unix/linux/syscall.rs28
-rw-r--r--src/tools/miri/src/shims/unix/macos/sync.rs60
-rw-r--r--src/tools/miri/src/shims/unix/sync.rs90
-rw-r--r--src/tools/miri/src/shims/windows/foreign_items.rs23
-rw-r--r--src/tools/miri/src/shims/windows/handle.rs51
-rw-r--r--src/tools/miri/src/shims/windows/sync.rs9
-rw-r--r--src/tools/miri/src/shims/windows/thread.rs10
-rw-r--r--src/tools/miri/src/shims/x86/aesni.rs19
-rw-r--r--src/tools/miri/src/shims/x86/gfni.rs18
-rw-r--r--src/tools/miri/src/shims/x86/mod.rs477
-rw-r--r--src/tools/miri/src/shims/x86/sha.rs12
-rw-r--r--src/tools/miri/src/shims/x86/sse42.rs70
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs65
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr41
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs71
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr41
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs96
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr41
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs11
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr14
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs21
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr14
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs4
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs119
-rw-r--r--src/tools/miri/tests/pass/float.rs14
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/2phase.rs23
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs13
-rw-r--r--tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.stderr12
-rw-r--r--tests/ui/traits/const-traits/effects/dont-prefer-param-env-for-infer-self-ty.rs16
59 files changed, 1701 insertions, 921 deletions
diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index 34effd199f1..246643d8074 100644
--- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -70,6 +70,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, LocalDefId};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_infer::infer::outlives::env::OutlivesEnvironment;
+use rustc_infer::traits::ObligationCause;
 use rustc_infer::traits::specialization_graph::Node;
 use rustc_middle::ty::trait_def::TraitSpecializationKind;
 use rustc_middle::ty::{
@@ -210,13 +211,7 @@ fn get_impl_args(
         impl1_def_id.to_def_id(),
         impl1_args,
         impl2_node,
-        |_, span| {
-            traits::ObligationCause::new(
-                impl1_span,
-                impl1_def_id,
-                traits::ObligationCauseCode::WhereClause(impl2_node.def_id(), span),
-            )
-        },
+        &ObligationCause::misc(impl1_span, impl1_def_id),
     );
 
     let errors = ocx.select_all_or_error();
diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs
index cb36f1a62db..27d3ec160ca 100644
--- a/compiler/rustc_trait_selection/src/traits/effects.rs
+++ b/compiler/rustc_trait_selection/src/traits/effects.rs
@@ -27,6 +27,11 @@ pub fn evaluate_host_effect_obligation<'tcx>(
         );
     }
 
+    // Force ambiguity for infer self ty.
+    if obligation.predicate.self_ty().is_ty_var() {
+        return Err(EvaluationFailure::Ambiguous);
+    }
+
     match evaluate_host_effect_from_bounds(selcx, obligation) {
         Ok(result) => return Ok(result),
         Err(EvaluationFailure::Ambiguous) => return Err(EvaluationFailure::Ambiguous),
diff --git a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
index 5bf3dbcbc32..a9cd705465e 100644
--- a/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/specialize/mod.rs
@@ -15,24 +15,24 @@ use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::codes::*;
 use rustc_errors::{Diag, EmissionGuarantee};
 use rustc_hir::def_id::{DefId, LocalDefId};
-use rustc_infer::infer::DefineOpaqueTypes;
 use rustc_middle::bug;
 use rustc_middle::query::LocalCrate;
 use rustc_middle::ty::print::PrintTraitRefExt as _;
-use rustc_middle::ty::{
-    self, GenericArgsRef, ImplSubject, Ty, TyCtxt, TypeVisitableExt, TypingMode,
-};
+use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, TypingMode};
 use rustc_session::lint::builtin::{COHERENCE_LEAK_CHECK, ORDER_DEPENDENT_TRAIT_OBJECTS};
 use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span, sym};
+use rustc_type_ir::solve::NoSolution;
 use specialization_graph::GraphExt;
 use tracing::{debug, instrument};
 
-use super::{SelectionContext, util};
 use crate::error_reporting::traits::to_pretty_impl_header;
 use crate::errors::NegativePositiveConflict;
-use crate::infer::{InferCtxt, InferOk, TyCtxtInferExt};
+use crate::infer::{InferCtxt, TyCtxtInferExt};
 use crate::traits::select::IntercrateAmbiguityCause;
-use crate::traits::{FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt, coherence};
+use crate::traits::{
+    FutureCompatOverlapErrorKind, ObligationCause, ObligationCtxt, coherence,
+    predicates_for_generics,
+};
 
 /// Information pertinent to an overlapping impl error.
 #[derive(Debug)]
@@ -87,9 +87,14 @@ pub fn translate_args<'tcx>(
     source_args: GenericArgsRef<'tcx>,
     target_node: specialization_graph::Node,
 ) -> GenericArgsRef<'tcx> {
-    translate_args_with_cause(infcx, param_env, source_impl, source_args, target_node, |_, _| {
-        ObligationCause::dummy()
-    })
+    translate_args_with_cause(
+        infcx,
+        param_env,
+        source_impl,
+        source_args,
+        target_node,
+        &ObligationCause::dummy(),
+    )
 }
 
 /// Like [translate_args], but obligations from the parent implementation
@@ -104,7 +109,7 @@ pub fn translate_args_with_cause<'tcx>(
     source_impl: DefId,
     source_args: GenericArgsRef<'tcx>,
     target_node: specialization_graph::Node,
-    cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
+    cause: &ObligationCause<'tcx>,
 ) -> GenericArgsRef<'tcx> {
     debug!(
         "translate_args({:?}, {:?}, {:?}, {:?})",
@@ -123,7 +128,7 @@ pub fn translate_args_with_cause<'tcx>(
             }
 
             fulfill_implication(infcx, param_env, source_trait_ref, source_impl, target_impl, cause)
-                .unwrap_or_else(|()| {
+                .unwrap_or_else(|_| {
                     bug!(
                         "When translating generic parameters from {source_impl:?} to \
                         {target_impl:?}, the expected specialization failed to hold"
@@ -137,6 +142,84 @@ pub fn translate_args_with_cause<'tcx>(
     source_args.rebase_onto(infcx.tcx, source_impl, target_args)
 }
 
+/// Attempt to fulfill all obligations of `target_impl` after unification with
+/// `source_trait_ref`. If successful, returns the generic parameters for *all* the
+/// generics of `target_impl`, including both those needed to unify with
+/// `source_trait_ref` and those whose identity is determined via a where
+/// clause in the impl.
+fn fulfill_implication<'tcx>(
+    infcx: &InferCtxt<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    source_trait_ref: ty::TraitRef<'tcx>,
+    source_impl: DefId,
+    target_impl: DefId,
+    cause: &ObligationCause<'tcx>,
+) -> Result<GenericArgsRef<'tcx>, NoSolution> {
+    debug!(
+        "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
+        param_env, source_trait_ref, target_impl
+    );
+
+    let ocx = ObligationCtxt::new(infcx);
+    let source_trait_ref = ocx.normalize(cause, param_env, source_trait_ref);
+
+    if !ocx.select_all_or_error().is_empty() {
+        infcx.dcx().span_delayed_bug(
+            infcx.tcx.def_span(source_impl),
+            format!("failed to fully normalize {source_trait_ref}"),
+        );
+        return Err(NoSolution);
+    }
+
+    let target_args = infcx.fresh_args_for_item(DUMMY_SP, target_impl);
+    let target_trait_ref = ocx.normalize(
+        cause,
+        param_env,
+        infcx
+            .tcx
+            .impl_trait_ref(target_impl)
+            .expect("expected source impl to be a trait impl")
+            .instantiate(infcx.tcx, target_args),
+    );
+
+    // do the impls unify? If not, no specialization.
+    ocx.eq(cause, param_env, source_trait_ref, target_trait_ref)?;
+
+    // Now check that the source trait ref satisfies all the where clauses of the target impl.
+    // This is not just for correctness; we also need this to constrain any params that may
+    // only be referenced via projection predicates.
+    let predicates = ocx.normalize(
+        cause,
+        param_env,
+        infcx.tcx.predicates_of(target_impl).instantiate(infcx.tcx, target_args),
+    );
+    let obligations = predicates_for_generics(|_, _| cause.clone(), param_env, predicates);
+    ocx.register_obligations(obligations);
+
+    let errors = ocx.select_all_or_error();
+    if !errors.is_empty() {
+        // no dice!
+        debug!(
+            "fulfill_implication: for impls on {:?} and {:?}, \
+                 could not fulfill: {:?} given {:?}",
+            source_trait_ref,
+            target_trait_ref,
+            errors,
+            param_env.caller_bounds()
+        );
+        return Err(NoSolution);
+    }
+
+    debug!(
+        "fulfill_implication: an impl for {:?} specializes {:?}",
+        source_trait_ref, target_trait_ref
+    );
+
+    // Now resolve the *generic parameters* we built for the target earlier, replacing
+    // the inference variables inside with whatever we got from fulfillment.
+    Ok(infcx.resolve_vars_if_possible(target_args))
+}
+
 pub(super) fn specialization_enabled_in(tcx: TyCtxt<'_>, _: LocalCrate) -> bool {
     tcx.features().specialization() || tcx.features().min_specialization()
 }
@@ -182,8 +265,9 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
         return false;
     }
 
-    // create a parameter environment corresponding to a (placeholder) instantiation of impl1
-    let penv = tcx.param_env(impl1_def_id);
+    // create a parameter environment corresponding to an identity instantiation of impl1,
+    // i.e. the most generic instantiation of impl1.
+    let param_env = tcx.param_env(impl1_def_id);
 
     // Create an infcx, taking the predicates of impl1 as assumptions:
     let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis());
@@ -191,90 +275,15 @@ pub(super) fn specializes(tcx: TyCtxt<'_>, (impl1_def_id, impl2_def_id): (DefId,
     // Attempt to prove that impl2 applies, given all of the above.
     fulfill_implication(
         &infcx,
-        penv,
+        param_env,
         impl1_trait_header.trait_ref.instantiate_identity(),
         impl1_def_id,
         impl2_def_id,
-        |_, _| ObligationCause::dummy(),
+        &ObligationCause::dummy(),
     )
     .is_ok()
 }
 
-/// Attempt to fulfill all obligations of `target_impl` after unification with
-/// `source_trait_ref`. If successful, returns the generic parameters for *all* the
-/// generics of `target_impl`, including both those needed to unify with
-/// `source_trait_ref` and those whose identity is determined via a where
-/// clause in the impl.
-fn fulfill_implication<'tcx>(
-    infcx: &InferCtxt<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    source_trait_ref: ty::TraitRef<'tcx>,
-    source_impl: DefId,
-    target_impl: DefId,
-    error_cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
-) -> Result<GenericArgsRef<'tcx>, ()> {
-    debug!(
-        "fulfill_implication({:?}, trait_ref={:?} |- {:?} applies)",
-        param_env, source_trait_ref, target_impl
-    );
-
-    let ocx = ObligationCtxt::new(infcx);
-    let source_trait_ref = ocx.normalize(&ObligationCause::dummy(), param_env, source_trait_ref);
-
-    if !ocx.select_all_or_error().is_empty() {
-        infcx.dcx().span_delayed_bug(
-            infcx.tcx.def_span(source_impl),
-            format!("failed to fully normalize {source_trait_ref}"),
-        );
-    }
-
-    let source_trait_ref = infcx.resolve_vars_if_possible(source_trait_ref);
-    let source_trait = ImplSubject::Trait(source_trait_ref);
-
-    let selcx = SelectionContext::new(infcx);
-    let target_args = infcx.fresh_args_for_item(DUMMY_SP, target_impl);
-    let (target_trait, obligations) =
-        util::impl_subject_and_oblig(&selcx, param_env, target_impl, target_args, error_cause);
-
-    // do the impls unify? If not, no specialization.
-    let Ok(InferOk { obligations: more_obligations, .. }) = infcx
-        .at(&ObligationCause::dummy(), param_env)
-        // Ok to use `Yes`, as all the generic params are already replaced by inference variables,
-        // which will match the opaque type no matter if it is defining or not.
-        // Any concrete type that would match the opaque would already be handled by coherence rules,
-        // and thus either be ok to match here and already have errored, or it won't match, in which
-        // case there is no issue anyway.
-        .eq(DefineOpaqueTypes::Yes, source_trait, target_trait)
-    else {
-        debug!("fulfill_implication: {:?} does not unify with {:?}", source_trait, target_trait);
-        return Err(());
-    };
-
-    // attempt to prove all of the predicates for impl2 given those for impl1
-    // (which are packed up in penv)
-    ocx.register_obligations(obligations.chain(more_obligations));
-
-    let errors = ocx.select_all_or_error();
-    if !errors.is_empty() {
-        // no dice!
-        debug!(
-            "fulfill_implication: for impls on {:?} and {:?}, \
-                 could not fulfill: {:?} given {:?}",
-            source_trait,
-            target_trait,
-            errors,
-            param_env.caller_bounds()
-        );
-        return Err(());
-    }
-
-    debug!("fulfill_implication: an impl for {:?} specializes {:?}", source_trait, target_trait);
-
-    // Now resolve the *generic parameters* we built for the target earlier, replacing
-    // the inference variables inside with whatever we got from fulfillment.
-    Ok(infcx.resolve_vars_if_possible(target_args))
-}
-
 /// Query provider for `specialization_graph_of`.
 pub(super) fn specialization_graph_provider(
     tcx: TyCtxt<'_>,
diff --git a/compiler/rustc_trait_selection/src/traits/util.rs b/compiler/rustc_trait_selection/src/traits/util.rs
index b7a2f20b769..da1045b664a 100644
--- a/compiler/rustc_trait_selection/src/traits/util.rs
+++ b/compiler/rustc_trait_selection/src/traits/util.rs
@@ -3,19 +3,16 @@ use std::collections::BTreeMap;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Diag;
 use rustc_hir::def_id::DefId;
-use rustc_infer::infer::{InferCtxt, InferOk};
+use rustc_infer::infer::InferCtxt;
 pub use rustc_infer::traits::util::*;
 use rustc_middle::bug;
 use rustc_middle::ty::{
-    self, GenericArgsRef, ImplSubject, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable,
-    TypeVisitableExt, Upcast,
+    self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, Upcast,
 };
 use rustc_span::Span;
 use smallvec::{SmallVec, smallvec};
 use tracing::debug;
 
-use super::{NormalizeExt, ObligationCause, PredicateObligation, SelectionContext};
-
 ///////////////////////////////////////////////////////////////////////////
 // `TraitAliasExpander` iterator
 ///////////////////////////////////////////////////////////////////////////
@@ -166,34 +163,6 @@ impl<'tcx> Iterator for TraitAliasExpander<'tcx> {
 // Other
 ///////////////////////////////////////////////////////////////////////////
 
-/// Instantiate all bound parameters of the impl subject with the given args,
-/// returning the resulting subject and all obligations that arise.
-/// The obligations are closed under normalization.
-pub(crate) fn impl_subject_and_oblig<'a, 'tcx>(
-    selcx: &SelectionContext<'a, 'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    impl_def_id: DefId,
-    impl_args: GenericArgsRef<'tcx>,
-    cause: impl Fn(usize, Span) -> ObligationCause<'tcx>,
-) -> (ImplSubject<'tcx>, impl Iterator<Item = PredicateObligation<'tcx>>) {
-    let subject = selcx.tcx().impl_subject(impl_def_id);
-    let subject = subject.instantiate(selcx.tcx(), impl_args);
-
-    let InferOk { value: subject, obligations: normalization_obligations1 } =
-        selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(subject);
-
-    let predicates = selcx.tcx().predicates_of(impl_def_id);
-    let predicates = predicates.instantiate(selcx.tcx(), impl_args);
-    let InferOk { value: predicates, obligations: normalization_obligations2 } =
-        selcx.infcx.at(&ObligationCause::dummy(), param_env).normalize(predicates);
-    let impl_obligations = super::predicates_for_generics(cause, param_env, predicates);
-
-    let impl_obligations =
-        impl_obligations.chain(normalization_obligations1).chain(normalization_obligations2);
-
-    (subject, impl_obligations)
-}
-
 /// Casts a trait reference into a reference to one of its super
 /// traits; returns `None` if `target_trait_def_id` is not a
 /// supertrait.
diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs
index 4764d7f0b0f..71dcab3423c 100644
--- a/library/core/src/array/mod.rs
+++ b/library/core/src/array/mod.rs
@@ -10,11 +10,13 @@ use crate::convert::Infallible;
 use crate::error::Error;
 use crate::fmt;
 use crate::hash::{self, Hash};
+use crate::intrinsics::transmute_unchecked;
 use crate::iter::{UncheckedIterator, repeat_n};
 use crate::mem::{self, MaybeUninit};
 use crate::ops::{
     ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try,
 };
+use crate::ptr::{null, null_mut};
 use crate::slice::{Iter, IterMut};
 
 mod ascii;
@@ -606,8 +608,20 @@ impl<T, const N: usize> [T; N] {
     /// assert_eq!(strings.len(), 3);
     /// ```
     #[stable(feature = "array_methods", since = "1.77.0")]
-    pub fn each_ref(&self) -> [&T; N] {
-        from_trusted_iterator(self.iter())
+    #[rustc_const_unstable(feature = "const_array_each_ref", issue = "133289")]
+    pub const fn each_ref(&self) -> [&T; N] {
+        let mut buf = [null::<T>(); N];
+
+        // FIXME(const-hack): We would like to simply use iterators for this (as in the original implementation), but this is not allowed in constant expressions.
+        let mut i = 0;
+        while i < N {
+            buf[i] = &raw const self[i];
+
+            i += 1;
+        }
+
+        // SAFETY: `*const T` has the same layout as `&T`, and we've also initialised each pointer as a valid reference.
+        unsafe { transmute_unchecked(buf) }
     }
 
     /// Borrows each element mutably and returns an array of mutable references
@@ -625,8 +639,20 @@ impl<T, const N: usize> [T; N] {
     /// assert_eq!(floats, [0.0, 2.7, -1.0]);
     /// ```
     #[stable(feature = "array_methods", since = "1.77.0")]
-    pub fn each_mut(&mut self) -> [&mut T; N] {
-        from_trusted_iterator(self.iter_mut())
+    #[rustc_const_unstable(feature = "const_array_each_ref", issue = "133289")]
+    pub const fn each_mut(&mut self) -> [&mut T; N] {
+        let mut buf = [null_mut::<T>(); N];
+
+        // FIXME(const-hack): We would like to simply use iterators for this (as in the original implementation), but this is not allowed in constant expressions.
+        let mut i = 0;
+        while i < N {
+            buf[i] = &raw mut self[i];
+
+            i += 1;
+        }
+
+        // SAFETY: `*mut T` has the same layout as `&mut T`, and we've also initialised each pointer as a valid reference.
+        unsafe { transmute_unchecked(buf) }
     }
 
     /// Divides one array reference into two at an index.
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index 5b94f036248..9c4e6f296fe 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -658,6 +658,8 @@ pub mod arch {
     pub use std_detect::is_aarch64_feature_detected;
     #[unstable(feature = "stdarch_arm_feature_detection", issue = "111190")]
     pub use std_detect::is_arm_feature_detected;
+    #[unstable(feature = "is_loongarch_feature_detected", issue = "117425")]
+    pub use std_detect::is_loongarch_feature_detected;
     #[unstable(feature = "is_riscv_feature_detected", issue = "111192")]
     pub use std_detect::is_riscv_feature_detected;
     #[stable(feature = "simd_x86", since = "1.27.0")]
diff --git a/library/std/src/os/fd/owned.rs b/library/std/src/os/fd/owned.rs
index 2d087c03b04..388b8a88a1a 100644
--- a/library/std/src/os/fd/owned.rs
+++ b/library/std/src/os/fd/owned.rs
@@ -173,16 +173,17 @@ impl Drop for OwnedFd {
     #[inline]
     fn drop(&mut self) {
         unsafe {
-            // Note that errors are ignored when closing a file descriptor. The
-            // reason for this is that if an error occurs we don't actually know if
-            // the file descriptor was closed or not, and if we retried (for
-            // something like EINTR), we might close another valid file descriptor
-            // opened after we closed ours.
-            // However, this is usually justified, as some of the major Unices
-            // do make sure to always close the FD, even when `close()` is interrupted,
-            // and the scenario is rare to begin with.
-            // Helpful link to an epic discussion by POSIX workgroup:
-            // http://austingroupbugs.net/view.php?id=529
+            // Note that errors are ignored when closing a file descriptor. According to POSIX 2024,
+            // we can and indeed should retry `close` on `EINTR`
+            // (https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/close.html),
+            // but it is not clear yet how well widely-used implementations are conforming with this
+            // mandate since older versions of POSIX left the state of the FD after an `EINTR`
+            // unspecified. Ignoring errors is "fine" because some of the major Unices (in
+            // particular, Linux) do make sure to always close the FD, even when `close()` is
+            // interrupted, and the scenario is rare to begin with. If we retried on a
+            // not-POSIX-compliant implementation, the consequences could be really bad since we may
+            // close the wrong FD. Helpful link to an epic discussion by POSIX workgroup that led to
+            // the latest POSIX wording: http://austingroupbugs.net/view.php?id=529
             #[cfg(not(target_os = "hermit"))]
             {
                 #[cfg(unix)]
diff --git a/library/std/src/sys/random/arc4random.rs b/library/std/src/sys/random/arc4random.rs
index ffabaafbee8..32467e9ebaa 100644
--- a/library/std/src/sys/random/arc4random.rs
+++ b/library/std/src/sys/random/arc4random.rs
@@ -12,7 +12,6 @@
 #[cfg(not(any(
     target_os = "haiku",
     target_os = "illumos",
-    target_os = "rtems",
     target_os = "solaris",
     target_os = "vita",
 )))]
@@ -22,7 +21,6 @@ use libc::arc4random_buf;
 #[cfg(any(
     target_os = "haiku", // See https://git.haiku-os.org/haiku/tree/headers/compatibility/bsd/stdlib.h
     target_os = "illumos", // See https://www.illumos.org/man/3C/arc4random
-    target_os = "rtems", // See https://docs.rtems.org/branches/master/bsp-howto/getentropy.html
     target_os = "solaris", // See https://docs.oracle.com/cd/E88353_01/html/E37843/arc4random-3c.html
     target_os = "vita", // See https://github.com/vitasdk/newlib/blob/b89e5bc183b516945f9ee07eef483ecb916e45ff/newlib/libc/include/stdlib.h#L74
 ))]
diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs
index b2305b1eda7..a14446b3515 100644
--- a/library/std/src/thread/scoped.rs
+++ b/library/std/src/thread/scoped.rs
@@ -176,7 +176,7 @@ impl<'scope, 'env> Scope<'scope, 'env> {
     /// thread. If the spawned thread panics, [`join`] will return an [`Err`] containing
     /// the panic payload.
     ///
-    /// If the join handle is dropped, the spawned thread will implicitly joined at the
+    /// If the join handle is dropped, the spawned thread will be implicitly joined at the
     /// end of the scope. In that case, if the spawned thread panics, [`scope`] will
     /// panic after all threads are joined.
     ///
diff --git a/src/tools/miri/.cargo/config.toml b/src/tools/miri/.cargo/config.toml
deleted file mode 100644
index 42e7c2c4818..00000000000
--- a/src/tools/miri/.cargo/config.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-[unstable]
-profile-rustflags = true
-
-# Add back the containing directory of the packages we have to refer to using --manifest-path.
-# Per-package profiles avoid adding this to build dependencies.
-[profile.dev.package."cargo-miri"]
-rustflags = ["--remap-path-prefix", "=cargo-miri"]
-[profile.dev.package."miri-script"]
-rustflags = ["--remap-path-prefix", "=miri-script"]
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index a73fefaaf34..9a683ae68fd 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -548,6 +548,7 @@ Definite bugs found:
 * [Incorrect offset computation for highly-aligned types in `portable-atomic-util`](https://github.com/taiki-e/portable-atomic/pull/138)
 * [Occasional memory leak in `std::mpsc` channels](https://github.com/rust-lang/rust/issues/121582) (original code in [crossbeam](https://github.com/crossbeam-rs/crossbeam/pull/1084))
 * [Weak-memory-induced memory leak in Windows thread-local storage](https://github.com/rust-lang/rust/pull/124281)
+* [A bug in the new `RwLock::downgrade` implementation](https://rust-lang.zulipchat.com/#narrow/channel/269128-miri/topic/Miri.20error.20library.20test) (caught by Miri before it landed in the Rust repo)
 
 Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment):
 
diff --git a/src/tools/miri/miri b/src/tools/miri/miri
index 5d07ad9e249..ac1a7211c4e 100755
--- a/src/tools/miri/miri
+++ b/src/tools/miri/miri
@@ -1,13 +1,15 @@
 #!/usr/bin/env bash
 set -e
 # We want to call the binary directly, so we need to know where it ends up.
-MIRI_SCRIPT_TARGET_DIR="$(dirname "$0")"/miri-script/target
+ROOT_DIR="$(dirname "$0")"
+MIRI_SCRIPT_TARGET_DIR="$ROOT_DIR"/miri-script/target
 # If stdout is not a terminal and we are not on CI, assume that we are being invoked by RA, and use JSON output.
 if ! [ -t 1 ] && [ -z "$CI" ]; then
   MESSAGE_FORMAT="--message-format=json"
 fi
-# We need a nightly toolchain, for the `profile-rustflags` cargo feature.
-cargo +nightly build $CARGO_EXTRA_FLAGS --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml \
+# We need a nightly toolchain, for `-Zroot-dir`.
+cargo +nightly build $CARGO_EXTRA_FLAGS --manifest-path "$ROOT_DIR"/miri-script/Cargo.toml \
+  -Zroot-dir="$ROOT_DIR" \
   -q --target-dir "$MIRI_SCRIPT_TARGET_DIR" $MESSAGE_FORMAT || \
   ( echo "Failed to build miri-script. Is the 'nightly' toolchain installed?"; exit 1 )
 # Instead of doing just `cargo run --manifest-path .. $@`, we invoke miri-script binary directly. Invoking `cargo run` goes through
diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs
index e6e85747d4d..cf6529d8373 100644
--- a/src/tools/miri/miri-script/src/util.rs
+++ b/src/tools/miri/miri-script/src/util.rs
@@ -105,7 +105,7 @@ impl MiriEnv {
 
         // Get extra flags for cargo.
         let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default();
-        let cargo_extra_flags = flagsplit(&cargo_extra_flags);
+        let mut cargo_extra_flags = flagsplit(&cargo_extra_flags);
         if cargo_extra_flags.iter().any(|a| a == "--release" || a.starts_with("--profile")) {
             // This makes binaries end up in different paths, let's not do that.
             eprintln!(
@@ -113,6 +113,8 @@ impl MiriEnv {
             );
             std::process::exit(1);
         }
+        // Also set `-Zroot-dir` for cargo, to print diagnostics relative to the miri dir.
+        cargo_extra_flags.push(format!("-Zroot-dir={}", miri_dir.display()));
 
         Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags, libdir })
     }
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index bec28af6257..effed0cd180 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-668959740f97e7a22ae340742886d330ab63950f
+2d0ea7956c45de6e421fd579e2ded27be405dec6
diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs
index 02e8261a6ed..ef4034cc0c1 100644
--- a/src/tools/miri/src/concurrency/sync.rs
+++ b/src/tools/miri/src/concurrency/sync.rs
@@ -1,6 +1,7 @@
 use std::cell::RefCell;
 use std::collections::VecDeque;
 use std::collections::hash_map::Entry;
+use std::default::Default;
 use std::ops::Not;
 use std::rc::Rc;
 use std::time::Duration;
@@ -46,8 +47,6 @@ macro_rules! declare_id {
 }
 pub(super) use declare_id;
 
-declare_id!(MutexId);
-
 /// The mutex state.
 #[derive(Default, Debug)]
 struct Mutex {
@@ -61,6 +60,21 @@ struct Mutex {
     clock: VClock,
 }
 
+#[derive(Default, Clone, Debug)]
+pub struct MutexRef(Rc<RefCell<Mutex>>);
+
+impl MutexRef {
+    fn new() -> Self {
+        MutexRef(Rc::new(RefCell::new(Mutex::default())))
+    }
+}
+
+impl VisitProvenance for MutexRef {
+    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
+        // Mutex contains no provenance.
+    }
+}
+
 declare_id!(RwLockId);
 
 /// The read-write lock state.
@@ -144,7 +158,6 @@ struct FutexWaiter {
 /// The state of all synchronization objects.
 #[derive(Default, Debug)]
 pub struct SynchronizationObjects {
-    mutexes: IndexVec<MutexId, Mutex>,
     rwlocks: IndexVec<RwLockId, RwLock>,
     condvars: IndexVec<CondvarId, Condvar>,
     pub(super) init_onces: IndexVec<InitOnceId, InitOnce>,
@@ -155,17 +168,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn condvar_reacquire_mutex(
         &mut self,
-        mutex: MutexId,
+        mutex_ref: &MutexRef,
         retval: Scalar,
         dest: MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
-        if this.mutex_is_locked(mutex) {
-            assert_ne!(this.mutex_get_owner(mutex), this.active_thread());
-            this.mutex_enqueue_and_block(mutex, Some((retval, dest)));
+        if this.mutex_is_locked(mutex_ref) {
+            assert_ne!(this.mutex_get_owner(mutex_ref), this.active_thread());
+            this.mutex_enqueue_and_block(mutex_ref, Some((retval, dest)));
         } else {
             // We can have it right now!
-            this.mutex_lock(mutex);
+            this.mutex_lock(mutex_ref);
             // Don't forget to write the return value.
             this.write_scalar(retval, &dest)?;
         }
@@ -174,10 +187,9 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 }
 
 impl SynchronizationObjects {
-    pub fn mutex_create(&mut self) -> MutexId {
-        self.mutexes.push(Default::default())
+    pub fn mutex_create(&mut self) -> MutexRef {
+        MutexRef::new()
     }
-
     pub fn rwlock_create(&mut self) -> RwLockId {
         self.rwlocks.push(Default::default())
     }
@@ -209,12 +221,16 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// Helper for lazily initialized `alloc_extra.sync` data:
     /// this forces an immediate init.
-    fn lazy_sync_init<T: 'static + Copy>(
-        &mut self,
+    /// Return a reference to the data in the machine state.
+    fn lazy_sync_init<'a, T: 'static>(
+        &'a mut self,
         primitive: &MPlaceTy<'tcx>,
         init_offset: Size,
         data: T,
-    ) -> InterpResult<'tcx> {
+    ) -> InterpResult<'tcx, &'a T>
+    where
+        'tcx: 'a,
+    {
         let this = self.eval_context_mut();
 
         let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?;
@@ -227,7 +243,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             &init_field,
             AtomicWriteOrd::Relaxed,
         )?;
-        interp_ok(())
+        interp_ok(this.get_alloc_extra(alloc)?.get_sync::<T>(offset).unwrap())
     }
 
     /// Helper for lazily initialized `alloc_extra.sync` data:
@@ -235,13 +251,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails
     ///   and stores that in `alloc_extra.sync`.
     /// - Otherwise, calls `new_data` to initialize the primitive.
-    fn lazy_sync_get_data<T: 'static + Copy>(
-        &mut self,
+    ///
+    /// Return a reference to the data in the machine state.
+    fn lazy_sync_get_data<'a, T: 'static>(
+        &'a mut self,
         primitive: &MPlaceTy<'tcx>,
         init_offset: Size,
         missing_data: impl FnOnce() -> InterpResult<'tcx, T>,
         new_data: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
-    ) -> InterpResult<'tcx, T> {
+    ) -> InterpResult<'tcx, &'a T>
+    where
+        'tcx: 'a,
+    {
         let this = self.eval_context_mut();
 
         // Check if this is already initialized. Needs to be atomic because we can race with another
@@ -265,17 +286,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // or else it has been moved illegally.
             let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?;
             let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc)?;
-            if let Some(data) = alloc_extra.get_sync::<T>(offset) {
-                interp_ok(*data)
-            } else {
+            // Due to borrow checker reasons, we have to do the lookup twice.
+            if alloc_extra.get_sync::<T>(offset).is_none() {
                 let data = missing_data()?;
                 alloc_extra.sync.insert(offset, Box::new(data));
-                interp_ok(data)
             }
+            interp_ok(alloc_extra.get_sync::<T>(offset).unwrap())
         } else {
             let data = new_data(this)?;
-            this.lazy_sync_init(primitive, init_offset, data)?;
-            interp_ok(data)
+            this.lazy_sync_init(primitive, init_offset, data)
         }
     }
 
@@ -311,23 +330,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     #[inline]
     /// Get the id of the thread that currently owns this lock.
-    fn mutex_get_owner(&mut self, id: MutexId) -> ThreadId {
-        let this = self.eval_context_ref();
-        this.machine.sync.mutexes[id].owner.unwrap()
+    fn mutex_get_owner(&self, mutex_ref: &MutexRef) -> ThreadId {
+        mutex_ref.0.borrow().owner.unwrap()
     }
 
     #[inline]
     /// Check if locked.
-    fn mutex_is_locked(&self, id: MutexId) -> bool {
-        let this = self.eval_context_ref();
-        this.machine.sync.mutexes[id].owner.is_some()
+    fn mutex_is_locked(&self, mutex_ref: &MutexRef) -> bool {
+        mutex_ref.0.borrow().owner.is_some()
     }
 
     /// Lock by setting the mutex owner and increasing the lock count.
-    fn mutex_lock(&mut self, id: MutexId) {
+    fn mutex_lock(&mut self, mutex_ref: &MutexRef) {
         let this = self.eval_context_mut();
         let thread = this.active_thread();
-        let mutex = &mut this.machine.sync.mutexes[id];
+        let mut mutex = mutex_ref.0.borrow_mut();
         if let Some(current_owner) = mutex.owner {
             assert_eq!(thread, current_owner, "mutex already locked by another thread");
             assert!(
@@ -347,9 +364,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// count. If the lock count reaches 0, release the lock and potentially
     /// give to a new owner. If the lock was not locked by the current thread,
     /// return `None`.
-    fn mutex_unlock(&mut self, id: MutexId) -> InterpResult<'tcx, Option<usize>> {
+    fn mutex_unlock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx, Option<usize>> {
         let this = self.eval_context_mut();
-        let mutex = &mut this.machine.sync.mutexes[id];
+        let mut mutex = mutex_ref.0.borrow_mut();
         interp_ok(if let Some(current_owner) = mutex.owner {
             // Mutex is locked.
             if current_owner != this.machine.threads.active_thread() {
@@ -367,8 +384,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         mutex.clock.clone_from(clock)
                     });
                 }
-                if let Some(thread) = this.machine.sync.mutexes[id].queue.pop_front() {
-                    this.unblock_thread(thread, BlockReason::Mutex(id))?;
+                let thread_id = mutex.queue.pop_front();
+                // We need to drop our mutex borrow before unblock_thread
+                // because it will be borrowed again in the unblock callback.
+                drop(mutex);
+                if thread_id.is_some() {
+                    this.unblock_thread(thread_id.unwrap(), BlockReason::Mutex)?;
                 }
             }
             Some(old_lock_count)
@@ -385,24 +406,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     #[inline]
     fn mutex_enqueue_and_block(
         &mut self,
-        id: MutexId,
+        mutex_ref: &MutexRef,
         retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
     ) {
         let this = self.eval_context_mut();
-        assert!(this.mutex_is_locked(id), "queing on unlocked mutex");
+        assert!(this.mutex_is_locked(mutex_ref), "queuing on unlocked mutex");
         let thread = this.active_thread();
-        this.machine.sync.mutexes[id].queue.push_back(thread);
+        mutex_ref.0.borrow_mut().queue.push_back(thread);
+        let mutex_ref = mutex_ref.clone();
         this.block_thread(
-            BlockReason::Mutex(id),
+            BlockReason::Mutex,
             None,
             callback!(
                 @capture<'tcx> {
-                    id: MutexId,
+                    mutex_ref: MutexRef,
                     retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
                 }
                 @unblock = |this| {
-                    assert!(!this.mutex_is_locked(id));
-                    this.mutex_lock(id);
+                    assert!(!this.mutex_is_locked(&mutex_ref));
+                    this.mutex_lock(&mutex_ref);
 
                     if let Some((retval, dest)) = retval_dest {
                         this.write_scalar(retval, &dest)?;
@@ -623,14 +645,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn condvar_wait(
         &mut self,
         condvar: CondvarId,
-        mutex: MutexId,
+        mutex_ref: MutexRef,
         timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
         retval_succ: Scalar,
         retval_timeout: Scalar,
         dest: MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
-        if let Some(old_locked_count) = this.mutex_unlock(mutex)? {
+        if let Some(old_locked_count) = this.mutex_unlock(&mutex_ref)? {
             if old_locked_count != 1 {
                 throw_unsup_format!(
                     "awaiting a condvar on a mutex acquired multiple times is not supported"
@@ -650,7 +672,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             callback!(
                 @capture<'tcx> {
                     condvar: CondvarId,
-                    mutex: MutexId,
+                    mutex_ref: MutexRef,
                     retval_succ: Scalar,
                     retval_timeout: Scalar,
                     dest: MPlaceTy<'tcx>,
@@ -665,7 +687,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     }
                     // Try to acquire the mutex.
                     // The timeout only applies to the first wait (until the signal), not for mutex acquisition.
-                    this.condvar_reacquire_mutex(mutex, retval_succ, dest)
+                    this.condvar_reacquire_mutex(&mutex_ref, retval_succ, dest)
                 }
                 @timeout = |this| {
                     // We have to remove the waiter from the queue again.
@@ -673,7 +695,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     let waiters = &mut this.machine.sync.condvars[condvar].waiters;
                     waiters.retain(|waiter| *waiter != thread);
                     // Now get back the lock.
-                    this.condvar_reacquire_mutex(mutex, retval_timeout, dest)
+                    this.condvar_reacquire_mutex(&mutex_ref, retval_timeout, dest)
                 }
             ),
         );
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index e6a3ae897c2..59e2fdd4285 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -113,6 +113,11 @@ impl ThreadId {
         self.0
     }
 
+    /// Create a new thread id from a `u32` without checking if this thread exists.
+    pub fn new_unchecked(id: u32) -> Self {
+        Self(id)
+    }
+
     pub const MAIN_THREAD: ThreadId = ThreadId(0);
 }
 
@@ -141,7 +146,7 @@ pub enum BlockReason {
     /// Waiting for time to pass.
     Sleep,
     /// Blocked on a mutex.
-    Mutex(MutexId),
+    Mutex,
     /// Blocked on a condition variable.
     Condvar(CondvarId),
     /// Blocked on a reader-writer lock.
@@ -152,6 +157,8 @@ pub enum BlockReason {
     InitOnce(InitOnceId),
     /// Blocked on epoll.
     Epoll,
+    /// Blocked on eventfd.
+    Eventfd,
 }
 
 /// The state of a thread.
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index 63591474120..41b7be37c37 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -195,7 +195,7 @@ pub fn prune_stacktrace<'tcx>(
                 // This len check ensures that we don't somehow remove every frame, as doing so breaks
                 // the primary error message.
                 while stacktrace.len() > 1
-                    && stacktrace.last().map_or(false, |frame| !machine.is_local(frame))
+                    && stacktrace.last().is_some_and(|frame| !machine.is_local(frame))
                 {
                     stacktrace.pop();
                 }
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 85591a0b370..1df1d08802a 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -270,12 +270,8 @@ pub fn create_ecx<'tcx>(
 ) -> InterpResult<'tcx, InterpCx<'tcx, MiriMachine<'tcx>>> {
     let typing_env = ty::TypingEnv::fully_monomorphized();
     let layout_cx = LayoutCx::new(tcx, typing_env);
-    let mut ecx = InterpCx::new(
-        tcx,
-        rustc_span::DUMMY_SP,
-        typing_env,
-        MiriMachine::new(config, layout_cx)
-    );
+    let mut ecx =
+        InterpCx::new(tcx, rustc_span::DUMMY_SP, typing_env, MiriMachine::new(config, layout_cx));
 
     // Some parts of initialization require a full `InterpCx`.
     MiriMachine::late_init(&mut ecx, config, {
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 4b34f1686a0..b57ce4e070c 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -116,8 +116,7 @@ pub fn resolve_path<'tcx>(
 /// Gets the layout of a type at a path.
 #[track_caller]
 pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
-    let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS)
-        .ty(cx.tcx(), cx.typing_env());
+    let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), cx.typing_env());
     cx.layout_of(ty).to_result().ok().unwrap()
 }
 
@@ -1009,7 +1008,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_ref();
 
         fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>(
-            this: &MiriInterpCx<'tcx>,
+            ecx: &MiriInterpCx<'tcx>,
             src: F,
             cast_to: TyAndLayout<'tcx>,
             round: rustc_apfloat::Round,
@@ -1029,7 +1028,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // Nothing else
                 _ =>
                     span_bug!(
-                        this.cur_span(),
+                        ecx.cur_span(),
                         "attempted float-to-int conversion with non-int output type {}",
                         cast_to.ty,
                     ),
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index 272dca1594e..9eebbc5d363 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -218,20 +218,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f32()?;
-                // Using host floats (but it's fine, these operations do not have guaranteed precision).
-                let f_host = f.to_host();
+                // Using host floats except for sqrt (but it's fine, these operations do not have
+                // guaranteed precision).
                 let res = match intrinsic_name {
-                    "sinf32" => f_host.sin(),
-                    "cosf32" => f_host.cos(),
-                    "sqrtf32" => f_host.sqrt(), // FIXME Using host floats, this should use full-precision soft-floats
-                    "expf32" => f_host.exp(),
-                    "exp2f32" => f_host.exp2(),
-                    "logf32" => f_host.ln(),
-                    "log10f32" => f_host.log10(),
-                    "log2f32" => f_host.log2(),
+                    "sinf32" => f.to_host().sin().to_soft(),
+                    "cosf32" => f.to_host().cos().to_soft(),
+                    "sqrtf32" => math::sqrt(f),
+                    "expf32" => f.to_host().exp().to_soft(),
+                    "exp2f32" => f.to_host().exp2().to_soft(),
+                    "logf32" => f.to_host().ln().to_soft(),
+                    "log10f32" => f.to_host().log10().to_soft(),
+                    "log2f32" => f.to_host().log2().to_soft(),
                     _ => bug!(),
                 };
-                let res = res.to_soft();
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
@@ -247,20 +246,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f64()?;
-                // Using host floats (but it's fine, these operations do not have guaranteed precision).
-                let f_host = f.to_host();
+                // Using host floats except for sqrt (but it's fine, these operations do not have
+                // guaranteed precision).
                 let res = match intrinsic_name {
-                    "sinf64" => f_host.sin(),
-                    "cosf64" => f_host.cos(),
-                    "sqrtf64" => f_host.sqrt(), // FIXME Using host floats, this should use full-precision soft-floats
-                    "expf64" => f_host.exp(),
-                    "exp2f64" => f_host.exp2(),
-                    "logf64" => f_host.ln(),
-                    "log10f64" => f_host.log10(),
-                    "log2f64" => f_host.log2(),
+                    "sinf64" => f.to_host().sin().to_soft(),
+                    "cosf64" => f.to_host().cos().to_soft(),
+                    "sqrtf64" => math::sqrt(f),
+                    "expf64" => f.to_host().exp().to_soft(),
+                    "exp2f64" => f.to_host().exp2().to_soft(),
+                    "logf64" => f.to_host().ln().to_soft(),
+                    "log10f64" => f.to_host().log10().to_soft(),
+                    "log2f64" => f.to_host().log2().to_soft(),
                     _ => bug!(),
                 };
-                let res = res.to_soft();
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index d5c417e7231..075b6f35e0e 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -104,42 +104,39 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                             let ty::Float(float_ty) = op.layout.ty.kind() else {
                                 span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name)
                             };
-                            // Using host floats (but it's fine, these operations do not have guaranteed precision).
+                            // Using host floats except for sqrt (but it's fine, these operations do not
+                            // have guaranteed precision).
                             match float_ty {
                                 FloatTy::F16 => unimplemented!("f16_f128"),
                                 FloatTy::F32 => {
                                     let f = op.to_scalar().to_f32()?;
-                                    let f_host = f.to_host();
                                     let res = match host_op {
-                                        "fsqrt" => f_host.sqrt(), // FIXME Using host floats, this should use full-precision soft-floats
-                                        "fsin" => f_host.sin(),
-                                        "fcos" => f_host.cos(),
-                                        "fexp" => f_host.exp(),
-                                        "fexp2" => f_host.exp2(),
-                                        "flog" => f_host.ln(),
-                                        "flog2" => f_host.log2(),
-                                        "flog10" => f_host.log10(),
+                                        "fsqrt" => math::sqrt(f),
+                                        "fsin" => f.to_host().sin().to_soft(),
+                                        "fcos" => f.to_host().cos().to_soft(),
+                                        "fexp" => f.to_host().exp().to_soft(),
+                                        "fexp2" => f.to_host().exp2().to_soft(),
+                                        "flog" => f.to_host().ln().to_soft(),
+                                        "flog2" => f.to_host().log2().to_soft(),
+                                        "flog10" => f.to_host().log10().to_soft(),
                                         _ => bug!(),
                                     };
-                                    let res = res.to_soft();
                                     let res = this.adjust_nan(res, &[f]);
                                     Scalar::from(res)
                                 }
                                 FloatTy::F64 => {
                                     let f = op.to_scalar().to_f64()?;
-                                    let f_host = f.to_host();
                                     let res = match host_op {
-                                        "fsqrt" => f_host.sqrt(),
-                                        "fsin" => f_host.sin(),
-                                        "fcos" => f_host.cos(),
-                                        "fexp" => f_host.exp(),
-                                        "fexp2" => f_host.exp2(),
-                                        "flog" => f_host.ln(),
-                                        "flog2" => f_host.log2(),
-                                        "flog10" => f_host.log10(),
+                                        "fsqrt" => math::sqrt(f),
+                                        "fsin" => f.to_host().sin().to_soft(),
+                                        "fcos" => f.to_host().cos().to_soft(),
+                                        "fexp" => f.to_host().exp().to_soft(),
+                                        "fexp2" => f.to_host().exp2().to_soft(),
+                                        "flog" => f.to_host().ln().to_soft(),
+                                        "flog2" => f.to_host().log2().to_soft(),
+                                        "flog10" => f.to_host().log10().to_soft(),
                                         _ => bug!(),
                                     };
-                                    let res = res.to_soft();
                                     let res = this.adjust_nan(res, &[f]);
                                     Scalar::from(res)
                                 }
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index f903ccbc25a..85c896563da 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -83,6 +83,7 @@ mod eval;
 mod helpers;
 mod intrinsics;
 mod machine;
+mod math;
 mod mono_hash_map;
 mod operator;
 mod provenance_gc;
@@ -122,7 +123,7 @@ pub use crate::concurrency::data_race::{
 };
 pub use crate::concurrency::init_once::{EvalContextExt as _, InitOnceId};
 pub use crate::concurrency::sync::{
-    CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects,
+    CondvarId, EvalContextExt as _, MutexRef, RwLockId, SynchronizationObjects,
 };
 pub use crate::concurrency::thread::{
     BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, TimeoutAnchor,
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 7b03aed4763..814dc6d2b01 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -732,20 +732,20 @@ impl<'tcx> MiriMachine<'tcx> {
     }
 
     pub(crate) fn late_init(
-        this: &mut MiriInterpCx<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
         config: &MiriConfig,
         on_main_stack_empty: StackEmptyCallback<'tcx>,
     ) -> InterpResult<'tcx> {
-        EnvVars::init(this, config)?;
-        MiriMachine::init_extern_statics(this)?;
-        ThreadManager::init(this, on_main_stack_empty);
+        EnvVars::init(ecx, config)?;
+        MiriMachine::init_extern_statics(ecx)?;
+        ThreadManager::init(ecx, on_main_stack_empty);
         interp_ok(())
     }
 
-    pub(crate) fn add_extern_static(this: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) {
+    pub(crate) fn add_extern_static(ecx: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) {
         // This got just allocated, so there definitely is a pointer here.
         let ptr = ptr.into_pointer_or_addr().unwrap();
-        this.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
+        ecx.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
     }
 
     pub(crate) fn communicate(&self) -> bool {
diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs
new file mode 100644
index 00000000000..ed3d2d55678
--- /dev/null
+++ b/src/tools/miri/src/math.rs
@@ -0,0 +1,164 @@
+use rand::Rng as _;
+use rand::distributions::Distribution as _;
+use rustc_apfloat::Float as _;
+use rustc_apfloat::ieee::IeeeFloat;
+
+/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
+pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
+    ecx: &mut crate::MiriInterpCx<'_>,
+    val: F,
+    err_scale: i32,
+) -> F {
+    let rng = ecx.machine.rng.get_mut();
+    // Generate a random integer in the range [0, 2^PREC).
+    let dist = rand::distributions::Uniform::new(0, 1 << F::PRECISION);
+    let err = F::from_u128(dist.sample(rng))
+        .value
+        .scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap()));
+    // give it a random sign
+    let err = if rng.gen::<bool>() { -err } else { err };
+    // multiple the value with (1+err)
+    (val * (F::from_u128(1).value + err).value).value
+}
+
+pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFloat<S> {
+    match x.category() {
+        // preserve zero sign
+        rustc_apfloat::Category::Zero => x,
+        // propagate NaN
+        rustc_apfloat::Category::NaN => x,
+        // sqrt of negative number is NaN
+        _ if x.is_negative() => IeeeFloat::NAN,
+        // sqrt(∞) = ∞
+        rustc_apfloat::Category::Infinity => IeeeFloat::INFINITY,
+        rustc_apfloat::Category::Normal => {
+            // Floating point precision, excluding the integer bit
+            let prec = i32::try_from(S::PRECISION).unwrap() - 1;
+
+            // x = 2^(exp - prec) * mant
+            // where mant is an integer with prec+1 bits
+            // mant is a u128, which should be large enough for the largest prec (112 for f128)
+            let mut exp = x.ilogb();
+            let mut mant = x.scalbn(prec - exp).to_u128(128).value;
+
+            if exp % 2 != 0 {
+                // Make exponent even, so it can be divided by 2
+                exp -= 1;
+                mant <<= 1;
+            }
+
+            // Bit-by-bit (base-2 digit-by-digit) sqrt of mant.
+            // mant is treated here as a fixed point number with prec fractional bits.
+            // mant will be shifted left by one bit to have an extra fractional bit, which
+            // will be used to determine the rounding direction.
+
+            // res is the truncated sqrt of mant, where one bit is added at each iteration.
+            let mut res = 0u128;
+            // rem is the remainder with the current res
+            // rem_i = 2^i * ((mant<<1) - res_i^2)
+            // starting with res = 0, rem = mant<<1
+            let mut rem = mant << 1;
+            // s_i = 2*res_i
+            let mut s = 0u128;
+            // d is used to iterate over bits, from high to low (d_i = 2^(-i))
+            let mut d = 1u128 << (prec + 1);
+
+            // For iteration j=i+1, we need to find largest b_j = 0 or 1 such that
+            //  (res_i + b_j * 2^(-j))^2 <= mant<<1
+            // Expanding (a + b)^2 = a^2 + b^2 + 2*a*b:
+            //  res_i^2 + (b_j * 2^(-j))^2 + 2 * res_i * b_j * 2^(-j) <= mant<<1
+            // And rearranging the terms:
+            //  b_j^2 * 2^(-j) + 2 * res_i * b_j <= 2^j * (mant<<1 - res_i^2)
+            //  b_j^2 * 2^(-j) + 2 * res_i * b_j <= rem_i
+
+            while d != 0 {
+                // Probe b_j^2 * 2^(-j) + 2 * res_i * b_j <= rem_i with b_j = 1:
+                // t = 2*res_i + 2^(-j)
+                let t = s + d;
+                if rem >= t {
+                    // b_j should be 1, so make res_j = res_i + 2^(-j) and adjust rem
+                    res += d;
+                    s += d + d;
+                    rem -= t;
+                }
+                // Adjust rem for next iteration
+                rem <<= 1;
+                // Shift iterator
+                d >>= 1;
+            }
+
+            // Remove extra fractional bit from result, rounding to nearest.
+            // If the last bit is 0, then the nearest neighbor is definitely the lower one.
+            // If the last bit is 1, it sounds like this may either be a tie (if there's
+            // infinitely many 0s after this 1), or the nearest neighbor is the upper one.
+            // However, since square roots are either exact or irrational, and an exact root
+            // would lead to the last "extra" bit being 0, we can exclude a tie in this case.
+            // We therefore always round up if the last bit is 1. When the last bit is 0,
+            // adding 1 will not do anything since the shift will discard it.
+            res = (res + 1) >> 1;
+
+            // Build resulting value with res as mantissa and exp/2 as exponent
+            IeeeFloat::from_u128(res).value.scalbn(exp / 2 - prec)
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS};
+
+    use super::sqrt;
+
+    #[test]
+    fn test_sqrt() {
+        #[track_caller]
+        fn test<S: rustc_apfloat::ieee::Semantics>(x: &str, expected: &str) {
+            let x: IeeeFloat<S> = x.parse().unwrap();
+            let expected: IeeeFloat<S> = expected.parse().unwrap();
+            let result = sqrt(x);
+            assert_eq!(result, expected);
+        }
+
+        fn exact_tests<S: rustc_apfloat::ieee::Semantics>() {
+            test::<S>("0", "0");
+            test::<S>("1", "1");
+            test::<S>("1.5625", "1.25");
+            test::<S>("2.25", "1.5");
+            test::<S>("4", "2");
+            test::<S>("5.0625", "2.25");
+            test::<S>("9", "3");
+            test::<S>("16", "4");
+            test::<S>("25", "5");
+            test::<S>("36", "6");
+            test::<S>("49", "7");
+            test::<S>("64", "8");
+            test::<S>("81", "9");
+            test::<S>("100", "10");
+
+            test::<S>("0.5625", "0.75");
+            test::<S>("0.25", "0.5");
+            test::<S>("0.0625", "0.25");
+            test::<S>("0.00390625", "0.0625");
+        }
+
+        exact_tests::<HalfS>();
+        exact_tests::<SingleS>();
+        exact_tests::<DoubleS>();
+        exact_tests::<QuadS>();
+
+        test::<SingleS>("2", "1.4142135");
+        test::<DoubleS>("2", "1.4142135623730951");
+
+        test::<SingleS>("1.1", "1.0488088");
+        test::<DoubleS>("1.1", "1.0488088481701516");
+
+        test::<SingleS>("2.2", "1.4832398");
+        test::<DoubleS>("2.2", "1.4832396974191326");
+
+        test::<SingleS>("1.22101e-40", "1.10499205e-20");
+        test::<DoubleS>("1.22101e-310", "1.1049932126488395e-155");
+
+        test::<SingleS>("3.4028235e38", "1.8446743e19");
+        test::<DoubleS>("1.7976931348623157e308", "1.3407807929942596e154");
+    }
+}
diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs
index 6042a9eb2eb..b3d715db9cd 100644
--- a/src/tools/miri/src/provenance_gc.rs
+++ b/src/tools/miri/src/provenance_gc.rs
@@ -195,10 +195,10 @@ impl LiveAllocs<'_, '_> {
     }
 }
 
-fn remove_unreachable_tags<'tcx>(this: &mut MiriInterpCx<'tcx>, tags: FxHashSet<BorTag>) {
+fn remove_unreachable_tags<'tcx>(ecx: &mut MiriInterpCx<'tcx>, tags: FxHashSet<BorTag>) {
     // Avoid iterating all allocations if there's no borrow tracker anyway.
-    if this.machine.borrow_tracker.is_some() {
-        this.memory.alloc_map().iter(|it| {
+    if ecx.machine.borrow_tracker.is_some() {
+        ecx.memory.alloc_map().iter(|it| {
             for (_id, (_kind, alloc)) in it {
                 alloc.extra.borrow_tracker.as_ref().unwrap().remove_unreachable_tags(&tags);
             }
@@ -206,16 +206,16 @@ fn remove_unreachable_tags<'tcx>(this: &mut MiriInterpCx<'tcx>, tags: FxHashSet<
     }
 }
 
-fn remove_unreachable_allocs<'tcx>(this: &mut MiriInterpCx<'tcx>, allocs: FxHashSet<AllocId>) {
-    let allocs = LiveAllocs { ecx: this, collected: allocs };
-    this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
-    this.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
-    this.machine.alloc_addresses.borrow_mut().remove_unreachable_allocs(&allocs);
-    if let Some(borrow_tracker) = &this.machine.borrow_tracker {
+fn remove_unreachable_allocs<'tcx>(ecx: &mut MiriInterpCx<'tcx>, allocs: FxHashSet<AllocId>) {
+    let allocs = LiveAllocs { ecx, collected: allocs };
+    ecx.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
+    ecx.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
+    ecx.machine.alloc_addresses.borrow_mut().remove_unreachable_allocs(&allocs);
+    if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
         borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);
     }
     // Clean up core (non-Miri-specific) state.
-    this.remove_unreachable_allocs(&allocs.collected);
+    ecx.remove_unreachable_allocs(&allocs.collected);
 }
 
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs
index 5559ea2750b..f0aebfe1693 100644
--- a/src/tools/miri/src/shims/extern_static.rs
+++ b/src/tools/miri/src/shims/extern_static.rs
@@ -4,13 +4,13 @@ use crate::*;
 
 impl<'tcx> MiriMachine<'tcx> {
     fn alloc_extern_static(
-        this: &mut MiriInterpCx<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
         name: &str,
         val: ImmTy<'tcx>,
     ) -> InterpResult<'tcx> {
-        let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
-        this.write_immediate(*val, &place)?;
-        Self::add_extern_static(this, name, place.ptr());
+        let place = ecx.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
+        ecx.write_immediate(*val, &place)?;
+        Self::add_extern_static(ecx, name, place.ptr());
         interp_ok(())
     }
 
@@ -18,72 +18,69 @@ impl<'tcx> MiriMachine<'tcx> {
     /// Most of them are for weak symbols, which we all set to null (indicating that the
     /// symbol is not supported, and triggering fallback code which ends up calling
     /// some other shim that we do support).
-    fn null_ptr_extern_statics(
-        this: &mut MiriInterpCx<'tcx>,
-        names: &[&str],
-    ) -> InterpResult<'tcx> {
+    fn null_ptr_extern_statics(ecx: &mut MiriInterpCx<'tcx>, names: &[&str]) -> InterpResult<'tcx> {
         for name in names {
-            let val = ImmTy::from_int(0, this.machine.layouts.usize);
-            Self::alloc_extern_static(this, name, val)?;
+            let val = ImmTy::from_int(0, ecx.machine.layouts.usize);
+            Self::alloc_extern_static(ecx, name, val)?;
         }
         interp_ok(())
     }
 
     /// Extern statics that are initialized with function pointers to the symbols of the same name.
     fn weak_symbol_extern_statics(
-        this: &mut MiriInterpCx<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
         names: &[&str],
     ) -> InterpResult<'tcx> {
         for name in names {
-            assert!(this.is_dyn_sym(name), "{name} is not a dynamic symbol");
-            let layout = this.machine.layouts.const_raw_ptr;
-            let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name)));
-            let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout);
-            Self::alloc_extern_static(this, name, val)?;
+            assert!(ecx.is_dyn_sym(name), "{name} is not a dynamic symbol");
+            let layout = ecx.machine.layouts.const_raw_ptr;
+            let ptr = ecx.fn_ptr(FnVal::Other(DynSym::from_str(name)));
+            let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, ecx), layout);
+            Self::alloc_extern_static(ecx, name, val)?;
         }
         interp_ok(())
     }
 
     /// Sets up the "extern statics" for this machine.
-    pub fn init_extern_statics(this: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx> {
+    pub fn init_extern_statics(ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx> {
         // "__rust_no_alloc_shim_is_unstable"
-        let val = ImmTy::from_int(0, this.machine.layouts.u8); // always 0, value does not matter
-        Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?;
+        let val = ImmTy::from_int(0, ecx.machine.layouts.u8); // always 0, value does not matter
+        Self::alloc_extern_static(ecx, "__rust_no_alloc_shim_is_unstable", val)?;
 
         // "__rust_alloc_error_handler_should_panic"
-        let val = this.tcx.sess.opts.unstable_opts.oom.should_panic();
-        let val = ImmTy::from_int(val, this.machine.layouts.u8);
-        Self::alloc_extern_static(this, "__rust_alloc_error_handler_should_panic", val)?;
+        let val = ecx.tcx.sess.opts.unstable_opts.oom.should_panic();
+        let val = ImmTy::from_int(val, ecx.machine.layouts.u8);
+        Self::alloc_extern_static(ecx, "__rust_alloc_error_handler_should_panic", val)?;
 
-        if this.target_os_is_unix() {
+        if ecx.target_os_is_unix() {
             // "environ" is mandated by POSIX.
-            let environ = this.machine.env_vars.unix().environ();
-            Self::add_extern_static(this, "environ", environ);
+            let environ = ecx.machine.env_vars.unix().environ();
+            Self::add_extern_static(ecx, "environ", environ);
         }
 
-        match this.tcx.sess.target.os.as_ref() {
+        match ecx.tcx.sess.target.os.as_ref() {
             "linux" => {
-                Self::null_ptr_extern_statics(this, &[
+                Self::null_ptr_extern_statics(ecx, &[
                     "__cxa_thread_atexit_impl",
                     "__clock_gettime64",
                 ])?;
-                Self::weak_symbol_extern_statics(this, &["getrandom", "statx"])?;
+                Self::weak_symbol_extern_statics(ecx, &["getrandom", "statx"])?;
             }
             "freebsd" => {
-                Self::null_ptr_extern_statics(this, &["__cxa_thread_atexit_impl"])?;
+                Self::null_ptr_extern_statics(ecx, &["__cxa_thread_atexit_impl"])?;
             }
             "android" => {
-                Self::null_ptr_extern_statics(this, &["bsd_signal"])?;
-                Self::weak_symbol_extern_statics(this, &["signal", "getrandom"])?;
+                Self::null_ptr_extern_statics(ecx, &["bsd_signal"])?;
+                Self::weak_symbol_extern_statics(ecx, &["signal", "getrandom"])?;
             }
             "windows" => {
                 // "_tls_used"
                 // This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
-                let val = ImmTy::from_int(0, this.machine.layouts.u8);
-                Self::alloc_extern_static(this, "_tls_used", val)?;
+                let val = ImmTy::from_int(0, ecx.machine.layouts.u8);
+                Self::alloc_extern_static(ecx, "_tls_used", val)?;
             }
             "illumos" | "solaris" => {
-                Self::weak_symbol_extern_statics(this, &["pthread_setname_np"])?;
+                Self::weak_symbol_extern_statics(ecx, &["pthread_setname_np"])?;
             }
             _ => {} // No "extern statics" supported on this target
         }
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index b74491a2f8e..4dc857ef30b 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -496,14 +496,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Rust allocation
             "__rust_alloc" | "miri_alloc" => {
-                let default = |this: &mut MiriInterpCx<'tcx>| {
+                let default = |ecx: &mut MiriInterpCx<'tcx>| {
                     // Only call `check_shim` when `#[global_allocator]` isn't used. When that
                     // macro is used, we act like no shim exists, so that the exported function can run.
-                    let [size, align] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
-                    let size = this.read_target_usize(size)?;
-                    let align = this.read_target_usize(align)?;
+                    let [size, align] = ecx.check_shim(abi, ExternAbi::Rust, link_name, args)?;
+                    let size = ecx.read_target_usize(size)?;
+                    let align = ecx.read_target_usize(align)?;
 
-                    this.check_rustc_alloc_request(size, align)?;
+                    ecx.check_rustc_alloc_request(size, align)?;
 
                     let memory_kind = match link_name.as_str() {
                         "__rust_alloc" => MiriMemoryKind::Rust,
@@ -511,13 +511,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         _ => unreachable!(),
                     };
 
-                    let ptr = this.allocate_ptr(
+                    let ptr = ecx.allocate_ptr(
                         Size::from_bytes(size),
                         Align::from_bytes(align).unwrap(),
                         memory_kind.into(),
                     )?;
 
-                    this.write_pointer(ptr, dest)
+                    ecx.write_pointer(ptr, dest)
                 };
 
                 match link_name.as_str() {
@@ -555,14 +555,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 });
             }
             "__rust_dealloc" | "miri_dealloc" => {
-                let default = |this: &mut MiriInterpCx<'tcx>| {
+                let default = |ecx: &mut MiriInterpCx<'tcx>| {
                     // See the comment for `__rust_alloc` why `check_shim` is only called in the
                     // default case.
                     let [ptr, old_size, align] =
-                        this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
-                    let ptr = this.read_pointer(ptr)?;
-                    let old_size = this.read_target_usize(old_size)?;
-                    let align = this.read_target_usize(align)?;
+                        ecx.check_shim(abi, ExternAbi::Rust, link_name, args)?;
+                    let ptr = ecx.read_pointer(ptr)?;
+                    let old_size = ecx.read_target_usize(old_size)?;
+                    let align = ecx.read_target_usize(align)?;
 
                     let memory_kind = match link_name.as_str() {
                         "__rust_dealloc" => MiriMemoryKind::Rust,
@@ -571,7 +571,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     };
 
                     // No need to check old_size/align; we anyway check that they match the allocation.
-                    this.deallocate_ptr(
+                    ecx.deallocate_ptr(
                         ptr,
                         Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
                         memory_kind.into(),
diff --git a/src/tools/miri/src/shims/unix/android/thread.rs b/src/tools/miri/src/shims/unix/android/thread.rs
index 093b7405ccd..f8a0b3a85a2 100644
--- a/src/tools/miri/src/shims/unix/android/thread.rs
+++ b/src/tools/miri/src/shims/unix/android/thread.rs
@@ -8,7 +8,7 @@ use crate::*;
 const TASK_COMM_LEN: usize = 16;
 
 pub fn prctl<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     link_name: Symbol,
     abi: ExternAbi,
     args: &[OpTy<'tcx>],
@@ -16,41 +16,41 @@ pub fn prctl<'tcx>(
 ) -> InterpResult<'tcx> {
     // We do not use `check_shim` here because `prctl` is variadic. The argument
     // count is checked bellow.
-    this.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;
+    ecx.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;
 
     // FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch.
     let pr_set_name = 15;
     let pr_get_name = 16;
 
     let [op] = check_min_arg_count("prctl", args)?;
-    let res = match this.read_scalar(op)?.to_i32()? {
+    let res = match ecx.read_scalar(op)?.to_i32()? {
         op if op == pr_set_name => {
             let [_, name] = check_min_arg_count("prctl(PR_SET_NAME, ...)", args)?;
-            let name = this.read_scalar(name)?;
-            let thread = this.pthread_self()?;
+            let name = ecx.read_scalar(name)?;
+            let thread = ecx.pthread_self()?;
             // The Linux kernel silently truncates long names.
             // https://www.man7.org/linux/man-pages/man2/PR_SET_NAME.2const.html
             let res =
-                this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
+                ecx.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
             assert_eq!(res, ThreadNameResult::Ok);
             Scalar::from_u32(0)
         }
         op if op == pr_get_name => {
             let [_, name] = check_min_arg_count("prctl(PR_GET_NAME, ...)", args)?;
-            let name = this.read_scalar(name)?;
-            let thread = this.pthread_self()?;
-            let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, this);
-            this.check_ptr_access(
-                name.to_pointer(this)?,
+            let name = ecx.read_scalar(name)?;
+            let thread = ecx.pthread_self()?;
+            let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, ecx);
+            ecx.check_ptr_access(
+                name.to_pointer(ecx)?,
                 Size::from_bytes(TASK_COMM_LEN),
                 CheckInAllocMsg::MemoryAccessTest,
             )?;
-            let res = this.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
+            let res = ecx.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
             assert_eq!(res, ThreadNameResult::Ok);
             Scalar::from_u32(0)
         }
         op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op),
     };
-    this.write_scalar(res, dest)?;
+    ecx.write_scalar(res, dest)?;
     interp_ok(())
 }
diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs
index 35bc933885c..63b7d37b13e 100644
--- a/src/tools/miri/src/shims/unix/linux/eventfd.rs
+++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs
@@ -4,7 +4,7 @@ use std::io;
 use std::io::ErrorKind;
 
 use crate::concurrency::VClock;
-use crate::shims::unix::fd::FileDescriptionRef;
+use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef};
 use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
 use crate::shims::unix::*;
 use crate::*;
@@ -26,6 +26,10 @@ struct Event {
     counter: Cell<u64>,
     is_nonblock: bool,
     clock: RefCell<VClock>,
+    /// A list of thread ids blocked on eventfd::read.
+    blocked_read_tid: RefCell<Vec<ThreadId>>,
+    /// A list of thread ids blocked on eventfd::write.
+    blocked_write_tid: RefCell<Vec<ThreadId>>,
 }
 
 impl FileDescription for Event {
@@ -72,31 +76,8 @@ impl FileDescription for Event {
         // eventfd read at the size of u64.
         let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
 
-        // Block when counter == 0.
-        let counter = self.counter.get();
-        if counter == 0 {
-            if self.is_nonblock {
-                return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
-            }
-
-            throw_unsup_format!("eventfd: blocking is unsupported");
-        } else {
-            // Synchronize with all prior `write` calls to this FD.
-            ecx.acquire_clock(&self.clock.borrow());
-
-            // Give old counter value to userspace, and set counter value to 0.
-            ecx.write_int(counter, &buf_place)?;
-            self.counter.set(0);
-
-            // When any of the event happened, we check and update the status of all supported event
-            // types for current file description.
-            ecx.check_and_update_readiness(self_ref)?;
-
-            // Tell userspace how many bytes we wrote.
-            ecx.write_int(buf_place.layout.size.bytes(), dest)?;
-        }
-
-        interp_ok(())
+        let weak_eventfd = self_ref.downgrade();
+        eventfd_read(buf_place, dest, weak_eventfd, ecx)
     }
 
     /// A write call adds the 8-byte integer value supplied in
@@ -127,7 +108,7 @@ impl FileDescription for Event {
             return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest);
         }
 
-        // Read the user supplied value from the pointer.
+        // Read the user-supplied value from the pointer.
         let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
         let num = ecx.read_scalar(&buf_place)?.to_u64()?;
 
@@ -137,27 +118,8 @@ impl FileDescription for Event {
         }
         // If the addition does not let the counter to exceed the maximum value, update the counter.
         // Else, block.
-        match self.counter.get().checked_add(num) {
-            Some(new_count @ 0..=MAX_COUNTER) => {
-                // Future `read` calls will synchronize with this write, so update the FD clock.
-                ecx.release_clock(|clock| {
-                    self.clock.borrow_mut().join(clock);
-                });
-                self.counter.set(new_count);
-            }
-            None | Some(u64::MAX) =>
-                if self.is_nonblock {
-                    return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
-                } else {
-                    throw_unsup_format!("eventfd: blocking is unsupported");
-                },
-        };
-        // When any of the event happened, we check and update the status of all supported event
-        // types for current file description.
-        ecx.check_and_update_readiness(self_ref)?;
-
-        // Return how many bytes we read.
-        ecx.write_int(buf_place.layout.size.bytes(), dest)
+        let weak_eventfd = self_ref.downgrade();
+        eventfd_write(num, buf_place, dest, weak_eventfd, ecx)
     }
 }
 
@@ -217,8 +179,151 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             counter: Cell::new(val.into()),
             is_nonblock,
             clock: RefCell::new(VClock::default()),
+            blocked_read_tid: RefCell::new(Vec::new()),
+            blocked_write_tid: RefCell::new(Vec::new()),
         });
 
         interp_ok(Scalar::from_i32(fd_value))
     }
 }
+
+/// Block thread if the value addition will exceed u64::MAX -1,
+/// else just add the user-supplied value to current counter.
+fn eventfd_write<'tcx>(
+    num: u64,
+    buf_place: MPlaceTy<'tcx>,
+    dest: &MPlaceTy<'tcx>,
+    weak_eventfd: WeakFileDescriptionRef,
+    ecx: &mut MiriInterpCx<'tcx>,
+) -> InterpResult<'tcx> {
+    let Some(eventfd_ref) = weak_eventfd.upgrade() else {
+        throw_unsup_format!("eventfd FD got closed while blocking.")
+    };
+
+    // Since we pass the weak file description ref, it is guaranteed to be
+    // an eventfd file description.
+    let eventfd = eventfd_ref.downcast::<Event>().unwrap();
+
+    match eventfd.counter.get().checked_add(num) {
+        Some(new_count @ 0..=MAX_COUNTER) => {
+            // Future `read` calls will synchronize with this write, so update the FD clock.
+            ecx.release_clock(|clock| {
+                eventfd.clock.borrow_mut().join(clock);
+            });
+
+            // When this function is called, the addition is guaranteed to not exceed u64::MAX - 1.
+            eventfd.counter.set(new_count);
+
+            // When any of the event happened, we check and update the status of all supported event
+            // types for current file description.
+            ecx.check_and_update_readiness(&eventfd_ref)?;
+
+            // Unblock *all* threads previously blocked on `read`.
+            // We need to take out the blocked thread ids and unblock them together,
+            // because `unblock_threads` may block them again and end up re-adding the
+            // thread to the blocked list.
+            let waiting_threads = std::mem::take(&mut *eventfd.blocked_read_tid.borrow_mut());
+            // FIXME: We can randomize the order of unblocking.
+            for thread_id in waiting_threads {
+                ecx.unblock_thread(thread_id, BlockReason::Eventfd)?;
+            }
+
+            // Return how many bytes we wrote.
+            return ecx.write_int(buf_place.layout.size.bytes(), dest);
+        }
+        None | Some(u64::MAX) => {
+            if eventfd.is_nonblock {
+                return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
+            }
+
+            let dest = dest.clone();
+
+            eventfd.blocked_write_tid.borrow_mut().push(ecx.active_thread());
+
+            ecx.block_thread(
+                BlockReason::Eventfd,
+                None,
+                callback!(
+                    @capture<'tcx> {
+                        num: u64,
+                        buf_place: MPlaceTy<'tcx>,
+                        dest: MPlaceTy<'tcx>,
+                        weak_eventfd: WeakFileDescriptionRef,
+                    }
+                    @unblock = |this| {
+                        eventfd_write(num, buf_place, &dest, weak_eventfd, this)
+                    }
+                ),
+            );
+        }
+    };
+    interp_ok(())
+}
+
+/// Block thread if the current counter is 0,
+/// else just return the current counter value to the caller and set the counter to 0.
+fn eventfd_read<'tcx>(
+    buf_place: MPlaceTy<'tcx>,
+    dest: &MPlaceTy<'tcx>,
+    weak_eventfd: WeakFileDescriptionRef,
+    ecx: &mut MiriInterpCx<'tcx>,
+) -> InterpResult<'tcx> {
+    let Some(eventfd_ref) = weak_eventfd.upgrade() else {
+        throw_unsup_format!("eventfd FD got closed while blocking.")
+    };
+
+    // Since we pass the weak file description ref to the callback function, it is guaranteed to be
+    // an eventfd file description.
+    let eventfd = eventfd_ref.downcast::<Event>().unwrap();
+
+    // Block when counter == 0.
+    let counter = eventfd.counter.replace(0);
+
+    if counter == 0 {
+        if eventfd.is_nonblock {
+            return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
+        }
+        let dest = dest.clone();
+
+        eventfd.blocked_read_tid.borrow_mut().push(ecx.active_thread());
+
+        ecx.block_thread(
+            BlockReason::Eventfd,
+            None,
+            callback!(
+                @capture<'tcx> {
+                    buf_place: MPlaceTy<'tcx>,
+                    dest: MPlaceTy<'tcx>,
+                    weak_eventfd: WeakFileDescriptionRef,
+                }
+                @unblock = |this| {
+                    eventfd_read(buf_place, &dest, weak_eventfd, this)
+                }
+            ),
+        );
+    } else {
+        // Synchronize with all prior `write` calls to this FD.
+        ecx.acquire_clock(&eventfd.clock.borrow());
+
+        // Give old counter value to userspace, and set counter value to 0.
+        ecx.write_int(counter, &buf_place)?;
+
+        // When any of the events happened, we check and update the status of all supported event
+        // types for current file description.
+        ecx.check_and_update_readiness(&eventfd_ref)?;
+
+        // Unblock *all* threads previously blocked on `write`.
+        // We need to take out the blocked thread ids and unblock them together,
+        // because `unblock_threads` may block them again and end up re-adding the
+        // thread to the blocked list.
+        let waiting_threads = std::mem::take(&mut *eventfd.blocked_write_tid.borrow_mut());
+        // FIXME: We can randomize the order of unblocking.
+        for thread_id in waiting_threads {
+            ecx.unblock_thread(thread_id, BlockReason::Eventfd)?;
+        }
+
+        // Tell userspace how many bytes we read.
+        return ecx.write_int(buf_place.layout.size.bytes(), dest);
+    }
+    interp_ok(())
+}
diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs
index 01b011d3504..51124fb2a00 100644
--- a/src/tools/miri/src/shims/unix/linux/sync.rs
+++ b/src/tools/miri/src/shims/unix/linux/sync.rs
@@ -9,7 +9,7 @@ struct LinuxFutex {
 /// Implementation of the SYS_futex syscall.
 /// `args` is the arguments *including* the syscall number.
 pub fn futex<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     args: &[OpTy<'tcx>],
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx> {
@@ -26,19 +26,19 @@ pub fn futex<'tcx>(
     // The first three arguments (after the syscall number itself) are the same to all futex operations:
     //     (int *addr, int op, int val).
     // We checked above that these definitely exist.
-    let addr = this.read_pointer(addr)?;
-    let op = this.read_scalar(op)?.to_i32()?;
-    let val = this.read_scalar(val)?.to_i32()?;
+    let addr = ecx.read_pointer(addr)?;
+    let op = ecx.read_scalar(op)?.to_i32()?;
+    let val = ecx.read_scalar(val)?.to_i32()?;
 
     // This is a vararg function so we have to bring our own type for this pointer.
-    let addr = this.ptr_to_mplace(addr, this.machine.layouts.i32);
+    let addr = ecx.ptr_to_mplace(addr, ecx.machine.layouts.i32);
 
-    let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG");
-    let futex_wait = this.eval_libc_i32("FUTEX_WAIT");
-    let futex_wait_bitset = this.eval_libc_i32("FUTEX_WAIT_BITSET");
-    let futex_wake = this.eval_libc_i32("FUTEX_WAKE");
-    let futex_wake_bitset = this.eval_libc_i32("FUTEX_WAKE_BITSET");
-    let futex_realtime = this.eval_libc_i32("FUTEX_CLOCK_REALTIME");
+    let futex_private = ecx.eval_libc_i32("FUTEX_PRIVATE_FLAG");
+    let futex_wait = ecx.eval_libc_i32("FUTEX_WAIT");
+    let futex_wait_bitset = ecx.eval_libc_i32("FUTEX_WAIT_BITSET");
+    let futex_wake = ecx.eval_libc_i32("FUTEX_WAKE");
+    let futex_wake_bitset = ecx.eval_libc_i32("FUTEX_WAKE_BITSET");
+    let futex_realtime = ecx.eval_libc_i32("FUTEX_CLOCK_REALTIME");
 
     // FUTEX_PRIVATE enables an optimization that stops it from working across processes.
     // Miri doesn't support that anyway, so we ignore that flag.
@@ -57,9 +57,9 @@ pub fn futex<'tcx>(
             let (timeout, bitset) = if wait_bitset {
                 let [_, _, _, _, timeout, uaddr2, bitset] =
                     check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT_BITSET, ...)`", args)?;
-                let _timeout = this.read_pointer(timeout)?;
-                let _uaddr2 = this.read_pointer(uaddr2)?;
-                (timeout, this.read_scalar(bitset)?.to_u32()?)
+                let _timeout = ecx.read_pointer(timeout)?;
+                let _uaddr2 = ecx.read_pointer(uaddr2)?;
+                (timeout, ecx.read_scalar(bitset)?.to_u32()?)
             } else {
                 let [_, _, _, _, timeout] =
                     check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT, ...)`", args)?;
@@ -67,21 +67,21 @@ pub fn futex<'tcx>(
             };
 
             if bitset == 0 {
-                return this.set_last_error_and_return(LibcError("EINVAL"), dest);
+                return ecx.set_last_error_and_return(LibcError("EINVAL"), dest);
             }
 
-            let timeout = this.deref_pointer_as(timeout, this.libc_ty_layout("timespec"))?;
-            let timeout = if this.ptr_is_null(timeout.ptr())? {
+            let timeout = ecx.deref_pointer_as(timeout, ecx.libc_ty_layout("timespec"))?;
+            let timeout = if ecx.ptr_is_null(timeout.ptr())? {
                 None
             } else {
-                let duration = match this.read_timespec(&timeout)? {
+                let duration = match ecx.read_timespec(&timeout)? {
                     Some(duration) => duration,
                     None => {
-                        return this.set_last_error_and_return(LibcError("EINVAL"), dest);
+                        return ecx.set_last_error_and_return(LibcError("EINVAL"), dest);
                     }
                 };
                 let timeout_clock = if op & futex_realtime == futex_realtime {
-                    this.check_no_isolation(
+                    ecx.check_no_isolation(
                         "`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`",
                     )?;
                     TimeoutClock::RealTime
@@ -139,36 +139,36 @@ pub fn futex<'tcx>(
             //
             // Thankfully, preemptions cannot happen inside a Miri shim, so we do not need to
             // do anything special to guarantee fence-load-comparison atomicity.
-            this.atomic_fence(AtomicFenceOrd::SeqCst)?;
+            ecx.atomic_fence(AtomicFenceOrd::SeqCst)?;
             // Read an `i32` through the pointer, regardless of any wrapper types.
             // It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`.
             // We do an acquire read -- it only seems reasonable that if we observe a value here, we
             // actually establish an ordering with that value.
-            let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Acquire)?.to_i32()?;
+            let futex_val = ecx.read_scalar_atomic(&addr, AtomicReadOrd::Acquire)?.to_i32()?;
             if val == futex_val {
                 // The value still matches, so we block the thread and make it wait for FUTEX_WAKE.
 
                 // This cannot fail since we already did an atomic acquire read on that pointer.
                 // Acquire reads are only allowed on mutable memory.
-                let futex_ref = this
+                let futex_ref = ecx
                     .get_sync_or_init(addr.ptr(), |_| LinuxFutex { futex: Default::default() })
                     .unwrap()
                     .futex
                     .clone();
 
-                this.futex_wait(
+                ecx.futex_wait(
                     futex_ref,
                     bitset,
                     timeout,
-                    Scalar::from_target_isize(0, this), // retval_succ
-                    Scalar::from_target_isize(-1, this), // retval_timeout
+                    Scalar::from_target_isize(0, ecx), // retval_succ
+                    Scalar::from_target_isize(-1, ecx), // retval_timeout
                     dest.clone(),
                     LibcError("ETIMEDOUT"), // errno_timeout
                 );
             } else {
                 // The futex value doesn't match the expected value, so we return failure
                 // right away without sleeping: -1 and errno set to EAGAIN.
-                return this.set_last_error_and_return(LibcError("EAGAIN"), dest);
+                return ecx.set_last_error_and_return(LibcError("EAGAIN"), dest);
             }
         }
         // FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val)
@@ -179,42 +179,42 @@ pub fn futex<'tcx>(
         // Same as FUTEX_WAKE, but allows you to specify a bitset to select which threads to wake up.
         op if op == futex_wake || op == futex_wake_bitset => {
             let Some(futex_ref) =
-                this.get_sync_or_init(addr.ptr(), |_| LinuxFutex { futex: Default::default() })
+                ecx.get_sync_or_init(addr.ptr(), |_| LinuxFutex { futex: Default::default() })
             else {
                 // No AllocId, or no live allocation at that AllocId.
                 // Return an error code. (That seems nicer than silently doing something non-intuitive.)
                 // This means that if an address gets reused by a new allocation,
                 // we'll use an independent futex queue for this... that seems acceptable.
-                return this.set_last_error_and_return(LibcError("EFAULT"), dest);
+                return ecx.set_last_error_and_return(LibcError("EFAULT"), dest);
             };
             let futex_ref = futex_ref.futex.clone();
 
             let bitset = if op == futex_wake_bitset {
                 let [_, _, _, _, timeout, uaddr2, bitset] =
                     check_min_arg_count("`syscall(SYS_futex, FUTEX_WAKE_BITSET, ...)`", args)?;
-                let _timeout = this.read_pointer(timeout)?;
-                let _uaddr2 = this.read_pointer(uaddr2)?;
-                this.read_scalar(bitset)?.to_u32()?
+                let _timeout = ecx.read_pointer(timeout)?;
+                let _uaddr2 = ecx.read_pointer(uaddr2)?;
+                ecx.read_scalar(bitset)?.to_u32()?
             } else {
                 u32::MAX
             };
             if bitset == 0 {
-                return this.set_last_error_and_return(LibcError("EINVAL"), dest);
+                return ecx.set_last_error_and_return(LibcError("EINVAL"), dest);
             }
             // Together with the SeqCst fence in futex_wait, this makes sure that futex_wait
             // will see the latest value on addr which could be changed by our caller
             // before doing the syscall.
-            this.atomic_fence(AtomicFenceOrd::SeqCst)?;
+            ecx.atomic_fence(AtomicFenceOrd::SeqCst)?;
             let mut n = 0;
             #[expect(clippy::arithmetic_side_effects)]
             for _ in 0..val {
-                if this.futex_wake(&futex_ref, bitset)? {
+                if ecx.futex_wake(&futex_ref, bitset)? {
                     n += 1;
                 } else {
                     break;
                 }
             }
-            this.write_scalar(Scalar::from_target_isize(n, this), dest)?;
+            ecx.write_scalar(Scalar::from_target_isize(n, ecx), dest)?;
         }
         op => throw_unsup_format!("Miri does not support `futex` syscall with op={}", op),
     }
diff --git a/src/tools/miri/src/shims/unix/linux/syscall.rs b/src/tools/miri/src/shims/unix/linux/syscall.rs
index 0d7032adab4..9f6935f096b 100644
--- a/src/tools/miri/src/shims/unix/linux/syscall.rs
+++ b/src/tools/miri/src/shims/unix/linux/syscall.rs
@@ -7,7 +7,7 @@ use crate::shims::unix::linux::sync::futex;
 use crate::*;
 
 pub fn syscall<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     link_name: Symbol,
     abi: ExternAbi,
     args: &[OpTy<'tcx>],
@@ -15,18 +15,18 @@ pub fn syscall<'tcx>(
 ) -> InterpResult<'tcx> {
     // We do not use `check_shim` here because `syscall` is variadic. The argument
     // count is checked bellow.
-    this.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;
+    ecx.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;
     // The syscall variadic function is legal to call with more arguments than needed,
     // extra arguments are simply ignored. The important check is that when we use an
     // argument, we have to also check all arguments *before* it to ensure that they
     // have the right type.
 
-    let sys_getrandom = this.eval_libc("SYS_getrandom").to_target_usize(this)?;
-    let sys_futex = this.eval_libc("SYS_futex").to_target_usize(this)?;
-    let sys_eventfd2 = this.eval_libc("SYS_eventfd2").to_target_usize(this)?;
+    let sys_getrandom = ecx.eval_libc("SYS_getrandom").to_target_usize(ecx)?;
+    let sys_futex = ecx.eval_libc("SYS_futex").to_target_usize(ecx)?;
+    let sys_eventfd2 = ecx.eval_libc("SYS_eventfd2").to_target_usize(ecx)?;
 
     let [op] = check_min_arg_count("syscall", args)?;
-    match this.read_target_usize(op)? {
+    match ecx.read_target_usize(op)? {
         // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
         // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
         num if num == sys_getrandom => {
@@ -34,25 +34,25 @@ pub fn syscall<'tcx>(
             // The first argument is the syscall id, so skip over it.
             let [_, ptr, len, flags] = check_min_arg_count("syscall(SYS_getrandom, ...)", args)?;
 
-            let ptr = this.read_pointer(ptr)?;
-            let len = this.read_target_usize(len)?;
+            let ptr = ecx.read_pointer(ptr)?;
+            let len = ecx.read_target_usize(len)?;
             // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
             // neither of which have any effect on our current PRNG.
             // See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
-            let _flags = this.read_scalar(flags)?.to_i32()?;
+            let _flags = ecx.read_scalar(flags)?.to_i32()?;
 
-            this.gen_random(ptr, len)?;
-            this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
+            ecx.gen_random(ptr, len)?;
+            ecx.write_scalar(Scalar::from_target_usize(len, ecx), dest)?;
         }
         // `futex` is used by some synchronization primitives.
         num if num == sys_futex => {
-            futex(this, args, dest)?;
+            futex(ecx, args, dest)?;
         }
         num if num == sys_eventfd2 => {
             let [_, initval, flags] = check_min_arg_count("syscall(SYS_evetfd2, ...)", args)?;
 
-            let result = this.eventfd(initval, flags)?;
-            this.write_int(result.to_i32()?, dest)?;
+            let result = ecx.eventfd(initval, flags)?;
+            ecx.write_int(result.to_i32()?, dest)?;
         }
         num => {
             throw_unsup_format!("syscall: unsupported syscall number {num}");
diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs
index ffc8c222738..f66a57ae706 100644
--- a/src/tools/miri/src/shims/unix/macos/sync.rs
+++ b/src/tools/miri/src/shims/unix/macos/sync.rs
@@ -14,18 +14,21 @@ use rustc_abi::Size;
 
 use crate::*;
 
-#[derive(Copy, Clone)]
+#[derive(Clone)]
 enum MacOsUnfairLock {
     Poisoned,
-    Active { id: MutexId },
+    Active { mutex_ref: MutexRef },
 }
 
 impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
 trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
-    fn os_unfair_lock_get_data(
-        &mut self,
+    fn os_unfair_lock_get_data<'a>(
+        &'a mut self,
         lock_ptr: &OpTy<'tcx>,
-    ) -> InterpResult<'tcx, MacOsUnfairLock> {
+    ) -> InterpResult<'tcx, &'a MacOsUnfairLock>
+    where
+        'tcx: 'a,
+    {
         let this = self.eval_context_mut();
         let lock = this.deref_pointer(lock_ptr)?;
         this.lazy_sync_get_data(
@@ -42,8 +45,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 interp_ok(MacOsUnfairLock::Poisoned)
             },
             |ecx| {
-                let id = ecx.machine.sync.mutex_create();
-                interp_ok(MacOsUnfairLock::Active { id })
+                let mutex_ref = ecx.machine.sync.mutex_create();
+                interp_ok(MacOsUnfairLock::Active { mutex_ref })
             },
         )
     }
@@ -54,7 +57,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn os_unfair_lock_lock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
+        let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
             // Trying to get a poisoned lock. Just block forever...
             this.block_thread(
                 BlockReason::Sleep,
@@ -68,18 +71,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             );
             return interp_ok(());
         };
+        let mutex_ref = mutex_ref.clone();
 
-        if this.mutex_is_locked(id) {
-            if this.mutex_get_owner(id) == this.active_thread() {
+        if this.mutex_is_locked(&mutex_ref) {
+            if this.mutex_get_owner(&mutex_ref) == this.active_thread() {
                 // Matching the current macOS implementation: abort on reentrant locking.
                 throw_machine_stop!(TerminationInfo::Abort(
                     "attempted to lock an os_unfair_lock that is already locked by the current thread".to_owned()
                 ));
             }
 
-            this.mutex_enqueue_and_block(id, None);
+            this.mutex_enqueue_and_block(&mutex_ref, None);
         } else {
-            this.mutex_lock(id);
+            this.mutex_lock(&mutex_ref);
         }
 
         interp_ok(())
@@ -92,18 +96,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
+        let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
             // Trying to get a poisoned lock. That never works.
             this.write_scalar(Scalar::from_bool(false), dest)?;
             return interp_ok(());
         };
+        let mutex_ref = mutex_ref.clone();
 
-        if this.mutex_is_locked(id) {
+        if this.mutex_is_locked(&mutex_ref) {
             // Contrary to the blocking lock function, this does not check for
             // reentrancy.
             this.write_scalar(Scalar::from_bool(false), dest)?;
         } else {
-            this.mutex_lock(id);
+            this.mutex_lock(&mutex_ref);
             this.write_scalar(Scalar::from_bool(true), dest)?;
         }
 
@@ -113,15 +118,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn os_unfair_lock_unlock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
+        let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
             // The lock is poisoned, who knows who owns it... we'll pretend: someone else.
             throw_machine_stop!(TerminationInfo::Abort(
                 "attempted to unlock an os_unfair_lock not owned by the current thread".to_owned()
             ));
         };
+        let mutex_ref = mutex_ref.clone();
 
         // Now, unlock.
-        if this.mutex_unlock(id)?.is_none() {
+        if this.mutex_unlock(&mutex_ref)?.is_none() {
             // Matching the current macOS implementation: abort.
             throw_machine_stop!(TerminationInfo::Abort(
                 "attempted to unlock an os_unfair_lock not owned by the current thread".to_owned()
@@ -130,7 +136,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // If the lock is not locked by anyone now, it went quer.
         // Reset to zero so that it can be moved and initialized again for the next phase.
-        if !this.mutex_is_locked(id) {
+        if !this.mutex_is_locked(&mutex_ref) {
             let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?;
             this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?;
         }
@@ -141,13 +147,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn os_unfair_lock_assert_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
+        let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
             // The lock is poisoned, who knows who owns it... we'll pretend: someone else.
             throw_machine_stop!(TerminationInfo::Abort(
                 "called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned()
             ));
         };
-        if !this.mutex_is_locked(id) || this.mutex_get_owner(id) != this.active_thread() {
+        let mutex_ref = mutex_ref.clone();
+
+        if !this.mutex_is_locked(&mutex_ref)
+            || this.mutex_get_owner(&mutex_ref) != this.active_thread()
+        {
             throw_machine_stop!(TerminationInfo::Abort(
                 "called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned()
             ));
@@ -161,11 +171,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn os_unfair_lock_assert_not_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
+        let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
             // The lock is poisoned, who knows who owns it... we'll pretend: someone else.
             return interp_ok(());
         };
-        if this.mutex_is_locked(id) && this.mutex_get_owner(id) == this.active_thread() {
+        let mutex_ref = mutex_ref.clone();
+
+        if this.mutex_is_locked(&mutex_ref)
+            && this.mutex_get_owner(&mutex_ref) == this.active_thread()
+        {
             throw_machine_stop!(TerminationInfo::Abort(
                 "called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread".to_owned()
             ));
@@ -173,7 +187,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // If the lock is not locked by anyone now, it went quer.
         // Reset to zero so that it can be moved and initialized again for the next phase.
-        if !this.mutex_is_locked(id) {
+        if !this.mutex_is_locked(&mutex_ref) {
             let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?;
             this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?;
         }
diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs
index 850626d89ac..416cf020dcc 100644
--- a/src/tools/miri/src/shims/unix/sync.rs
+++ b/src/tools/miri/src/shims/unix/sync.rs
@@ -116,9 +116,9 @@ enum MutexKind {
     ErrorCheck,
 }
 
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
 struct PthreadMutex {
-    id: MutexId,
+    mutex_ref: MutexRef,
     kind: MutexKind,
 }
 
@@ -175,19 +175,20 @@ fn mutex_create<'tcx>(
 ) -> InterpResult<'tcx, PthreadMutex> {
     let mutex = ecx.deref_pointer(mutex_ptr)?;
     let id = ecx.machine.sync.mutex_create();
-    let data = PthreadMutex { id, kind };
-    ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data)?;
+    let data = PthreadMutex { mutex_ref: id, kind };
+    ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data.clone())?;
     interp_ok(data)
 }
 
-/// Returns the `MutexId` of the mutex stored at `mutex_op`.
-///
-/// `mutex_get_id` will also check if the mutex has been moved since its first use and
-/// return an error if it has.
+/// Returns the mutex data stored at the address that `mutex_ptr` points to.
+/// Will raise an error if the mutex has been moved since its first use.
 fn mutex_get_data<'tcx, 'a>(
     ecx: &'a mut MiriInterpCx<'tcx>,
     mutex_ptr: &OpTy<'tcx>,
-) -> InterpResult<'tcx, PthreadMutex> {
+) -> InterpResult<'tcx, &'a PthreadMutex>
+where
+    'tcx: 'a,
+{
     let mutex = ecx.deref_pointer(mutex_ptr)?;
     ecx.lazy_sync_get_data(
         &mutex,
@@ -196,7 +197,7 @@ fn mutex_get_data<'tcx, 'a>(
         |ecx| {
             let kind = mutex_kind_from_static_initializer(ecx, &mutex)?;
             let id = ecx.machine.sync.mutex_create();
-            interp_ok(PthreadMutex { id, kind })
+            interp_ok(PthreadMutex { mutex_ref: id, kind })
         },
     )
 }
@@ -261,10 +262,13 @@ fn rwlock_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size
     interp_ok(offset)
 }
 
-fn rwlock_get_data<'tcx>(
-    ecx: &mut MiriInterpCx<'tcx>,
+fn rwlock_get_data<'tcx, 'a>(
+    ecx: &'a mut MiriInterpCx<'tcx>,
     rwlock_ptr: &OpTy<'tcx>,
-) -> InterpResult<'tcx, PthreadRwLock> {
+) -> InterpResult<'tcx, &'a PthreadRwLock>
+where
+    'tcx: 'a,
+{
     let rwlock = ecx.deref_pointer(rwlock_ptr)?;
     ecx.lazy_sync_get_data(
         &rwlock,
@@ -391,10 +395,13 @@ fn cond_create<'tcx>(
     interp_ok(data)
 }
 
-fn cond_get_data<'tcx>(
-    ecx: &mut MiriInterpCx<'tcx>,
+fn cond_get_data<'tcx, 'a>(
+    ecx: &'a mut MiriInterpCx<'tcx>,
     cond_ptr: &OpTy<'tcx>,
-) -> InterpResult<'tcx, PthreadCondvar> {
+) -> InterpResult<'tcx, &'a PthreadCondvar>
+where
+    'tcx: 'a,
+{
     let cond = ecx.deref_pointer(cond_ptr)?;
     ecx.lazy_sync_get_data(
         &cond,
@@ -500,12 +507,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let mutex = mutex_get_data(this, mutex_op)?;
+        let mutex = mutex_get_data(this, mutex_op)?.clone();
 
-        let ret = if this.mutex_is_locked(mutex.id) {
-            let owner_thread = this.mutex_get_owner(mutex.id);
+        let ret = if this.mutex_is_locked(&mutex.mutex_ref) {
+            let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
             if owner_thread != this.active_thread() {
-                this.mutex_enqueue_and_block(mutex.id, Some((Scalar::from_i32(0), dest.clone())));
+                this.mutex_enqueue_and_block(
+                    &mutex.mutex_ref,
+                    Some((Scalar::from_i32(0), dest.clone())),
+                );
                 return interp_ok(());
             } else {
                 // Trying to acquire the same mutex again.
@@ -517,14 +527,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock),
                     MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
                     MutexKind::Recursive => {
-                        this.mutex_lock(mutex.id);
+                        this.mutex_lock(&mutex.mutex_ref);
                         0
                     }
                 }
             }
         } else {
             // The mutex is unlocked. Let's lock it.
-            this.mutex_lock(mutex.id);
+            this.mutex_lock(&mutex.mutex_ref);
             0
         };
         this.write_scalar(Scalar::from_i32(ret), dest)?;
@@ -534,10 +544,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        let mutex = mutex_get_data(this, mutex_op)?;
+        let mutex = mutex_get_data(this, mutex_op)?.clone();
 
-        interp_ok(Scalar::from_i32(if this.mutex_is_locked(mutex.id) {
-            let owner_thread = this.mutex_get_owner(mutex.id);
+        interp_ok(Scalar::from_i32(if this.mutex_is_locked(&mutex.mutex_ref) {
+            let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
             if owner_thread != this.active_thread() {
                 this.eval_libc_i32("EBUSY")
             } else {
@@ -545,14 +555,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
                         this.eval_libc_i32("EBUSY"),
                     MutexKind::Recursive => {
-                        this.mutex_lock(mutex.id);
+                        this.mutex_lock(&mutex.mutex_ref);
                         0
                     }
                 }
             }
         } else {
             // The mutex is unlocked. Let's lock it.
-            this.mutex_lock(mutex.id);
+            this.mutex_lock(&mutex.mutex_ref);
             0
         }))
     }
@@ -560,9 +570,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        let mutex = mutex_get_data(this, mutex_op)?;
+        let mutex = mutex_get_data(this, mutex_op)?.clone();
 
-        if let Some(_old_locked_count) = this.mutex_unlock(mutex.id)? {
+        if let Some(_old_locked_count) = this.mutex_unlock(&mutex.mutex_ref)? {
             // The mutex was locked by the current thread.
             interp_ok(Scalar::from_i32(0))
         } else {
@@ -588,10 +598,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         // Reading the field also has the side-effect that we detect double-`destroy`
-        // since we make the field unint below.
-        let mutex = mutex_get_data(this, mutex_op)?;
+        // since we make the field uninit below.
+        let mutex = mutex_get_data(this, mutex_op)?.clone();
 
-        if this.mutex_is_locked(mutex.id) {
+        if this.mutex_is_locked(&mutex.mutex_ref) {
             throw_ub_format!("destroyed a locked mutex");
         }
 
@@ -696,7 +706,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         // Reading the field also has the side-effect that we detect double-`destroy`
-        // since we make the field unint below.
+        // since we make the field uninit below.
         let id = rwlock_get_data(this, rwlock_op)?.id;
 
         if this.rwlock_is_locked(id) {
@@ -821,12 +831,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let data = cond_get_data(this, cond_op)?;
-        let mutex_id = mutex_get_data(this, mutex_op)?.id;
+        let data = *cond_get_data(this, cond_op)?;
+        let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
 
         this.condvar_wait(
             data.id,
-            mutex_id,
+            mutex_ref,
             None, // no timeout
             Scalar::from_i32(0),
             Scalar::from_i32(0), // retval_timeout -- unused
@@ -845,8 +855,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let data = cond_get_data(this, cond_op)?;
-        let mutex_id = mutex_get_data(this, mutex_op)?.id;
+        let data = *cond_get_data(this, cond_op)?;
+        let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
 
         // Extract the timeout.
         let duration = match this
@@ -869,7 +879,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         this.condvar_wait(
             data.id,
-            mutex_id,
+            mutex_ref,
             Some((timeout_clock, TimeoutAnchor::Absolute, duration)),
             Scalar::from_i32(0),
             this.eval_libc("ETIMEDOUT"), // retval_timeout
@@ -883,7 +893,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         // Reading the field also has the side-effect that we detect double-`destroy`
-        // since we make the field unint below.
+        // since we make the field uninit below.
         let id = cond_get_data(this, cond_op)?.id;
         if this.condvar_is_awaited(id) {
             throw_ub_format!("destroying an awaited conditional variable");
diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs
index 504efed3cfd..c145cf3ceb8 100644
--- a/src/tools/miri/src/shims/windows/foreign_items.rs
+++ b/src/tools/miri/src/shims/windows/foreign_items.rs
@@ -7,6 +7,7 @@ use rustc_span::Symbol;
 
 use self::shims::windows::handle::{Handle, PseudoHandle};
 use crate::shims::os_str::bytes_to_os_str;
+use crate::shims::windows::handle::HandleError;
 use crate::shims::windows::*;
 use crate::*;
 
@@ -488,7 +489,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let thread_id =
                     this.CreateThread(security, stacksize, start, arg, flags, thread)?;
 
-                this.write_scalar(Handle::Thread(thread_id.to_u32()).to_scalar(this), dest)?;
+                this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
             }
             "WaitForSingleObject" => {
                 let [handle, timeout] =
@@ -513,10 +514,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let handle = this.read_scalar(handle)?;
                 let name = this.read_wide_str(this.read_pointer(name)?)?;
 
-                let thread = match Handle::from_scalar(handle, this)? {
-                    Some(Handle::Thread(thread)) => this.thread_id_try_from(thread),
-                    Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
-                    _ => this.invalid_handle("SetThreadDescription")?,
+                let thread = match Handle::try_from_scalar(handle, this)? {
+                    Ok(Handle::Thread(thread)) => Ok(thread),
+                    Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
+                    Ok(_) | Err(HandleError::InvalidHandle) =>
+                        this.invalid_handle("SetThreadDescription")?,
+                    Err(HandleError::ThreadNotFound(e)) => Err(e),
                 };
                 let res = match thread {
                     Ok(thread) => {
@@ -536,10 +539,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let handle = this.read_scalar(handle)?;
                 let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name
 
-                let thread = match Handle::from_scalar(handle, this)? {
-                    Some(Handle::Thread(thread)) => this.thread_id_try_from(thread),
-                    Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
-                    _ => this.invalid_handle("GetThreadDescription")?,
+                let thread = match Handle::try_from_scalar(handle, this)? {
+                    Ok(Handle::Thread(thread)) => Ok(thread),
+                    Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
+                    Ok(_) | Err(HandleError::InvalidHandle) =>
+                        this.invalid_handle("GetThreadDescription")?,
+                    Err(HandleError::ThreadNotFound(e)) => Err(e),
                 };
                 let (name, res) = match thread {
                     Ok(thread) => {
diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs
index b40c00efedd..3d872b65a63 100644
--- a/src/tools/miri/src/shims/windows/handle.rs
+++ b/src/tools/miri/src/shims/windows/handle.rs
@@ -2,6 +2,7 @@ use std::mem::variant_count;
 
 use rustc_abi::HasDataLayout;
 
+use crate::concurrency::thread::ThreadNotFound;
 use crate::*;
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -14,7 +15,7 @@ pub enum PseudoHandle {
 pub enum Handle {
     Null,
     Pseudo(PseudoHandle),
-    Thread(u32),
+    Thread(ThreadId),
 }
 
 impl PseudoHandle {
@@ -34,6 +35,14 @@ impl PseudoHandle {
     }
 }
 
+/// Errors that can occur when constructing a [`Handle`] from a Scalar.
+pub enum HandleError {
+    /// There is no thread with the given ID.
+    ThreadNotFound(ThreadNotFound),
+    /// Can't convert scalar to handle because it is structurally invalid.
+    InvalidHandle,
+}
+
 impl Handle {
     const NULL_DISCRIMINANT: u32 = 0;
     const PSEUDO_DISCRIMINANT: u32 = 1;
@@ -51,7 +60,7 @@ impl Handle {
         match self {
             Self::Null => 0,
             Self::Pseudo(pseudo_handle) => pseudo_handle.value(),
-            Self::Thread(thread) => thread,
+            Self::Thread(thread) => thread.to_u32(),
         }
     }
 
@@ -95,7 +104,7 @@ impl Handle {
         match discriminant {
             Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null),
             Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)),
-            Self::THREAD_DISCRIMINANT => Some(Self::Thread(data)),
+            Self::THREAD_DISCRIMINANT => Some(Self::Thread(ThreadId::new_unchecked(data))),
             _ => None,
         }
     }
@@ -126,10 +135,14 @@ impl Handle {
         Scalar::from_target_isize(signed_handle.into(), cx)
     }
 
-    pub fn from_scalar<'tcx>(
+    /// Convert a scalar into a structured `Handle`.
+    /// Structurally invalid handles return [`HandleError::InvalidHandle`].
+    /// If the handle is structurally valid but semantically invalid, e.g. a for non-existent thread
+    /// ID, returns [`HandleError::ThreadNotFound`].
+    pub fn try_from_scalar<'tcx>(
         handle: Scalar,
-        cx: &impl HasDataLayout,
-    ) -> InterpResult<'tcx, Option<Self>> {
+        cx: &MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx, Result<Self, HandleError>> {
         let sign_extended_handle = handle.to_target_isize(cx)?;
 
         #[expect(clippy::cast_sign_loss)] // we want to lose the sign
@@ -137,10 +150,20 @@ impl Handle {
             signed_handle as u32
         } else {
             // if a handle doesn't fit in an i32, it isn't valid.
-            return interp_ok(None);
+            return interp_ok(Err(HandleError::InvalidHandle));
         };
 
-        interp_ok(Self::from_packed(handle))
+        match Self::from_packed(handle) {
+            Some(Self::Thread(thread)) => {
+                // validate the thread id
+                match cx.machine.threads.thread_id_try_from(thread.to_u32()) {
+                    Ok(id) => interp_ok(Ok(Self::Thread(id))),
+                    Err(e) => interp_ok(Err(HandleError::ThreadNotFound(e))),
+                }
+            }
+            Some(handle) => interp_ok(Ok(handle)),
+            None => interp_ok(Err(HandleError::InvalidHandle)),
+        }
     }
 }
 
@@ -158,14 +181,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         let handle = this.read_scalar(handle_op)?;
-        let ret = match Handle::from_scalar(handle, this)? {
-            Some(Handle::Thread(thread)) => {
-                if let Ok(thread) = this.thread_id_try_from(thread) {
-                    this.detach_thread(thread, /*allow_terminated_joined*/ true)?;
-                    this.eval_windows("c", "TRUE")
-                } else {
-                    this.invalid_handle("CloseHandle")?
-                }
+        let ret = match Handle::try_from_scalar(handle, this)? {
+            Ok(Handle::Thread(thread)) => {
+                this.detach_thread(thread, /*allow_terminated_joined*/ true)?;
+                this.eval_windows("c", "TRUE")
             }
             _ => this.invalid_handle("CloseHandle")?,
         };
diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs
index b03dedea146..a394e0430bc 100644
--- a/src/tools/miri/src/shims/windows/sync.rs
+++ b/src/tools/miri/src/shims/windows/sync.rs
@@ -20,10 +20,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
     // Windows sync primitives are pointer sized.
     // We only use the first 4 bytes for the id.
 
-    fn init_once_get_data(
-        &mut self,
+    fn init_once_get_data<'a>(
+        &'a mut self,
         init_once_ptr: &OpTy<'tcx>,
-    ) -> InterpResult<'tcx, WindowsInitOnce> {
+    ) -> InterpResult<'tcx, &'a WindowsInitOnce>
+    where
+        'tcx: 'a,
+    {
         let this = self.eval_context_mut();
 
         let init_once = this.deref_pointer(init_once_ptr)?;
diff --git a/src/tools/miri/src/shims/windows/thread.rs b/src/tools/miri/src/shims/windows/thread.rs
index 7af15fc647c..efc1c2286bc 100644
--- a/src/tools/miri/src/shims/windows/thread.rs
+++ b/src/tools/miri/src/shims/windows/thread.rs
@@ -65,15 +65,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let handle = this.read_scalar(handle_op)?;
         let timeout = this.read_scalar(timeout_op)?.to_u32()?;
 
-        let thread = match Handle::from_scalar(handle, this)? {
-            Some(Handle::Thread(thread)) =>
-                match this.thread_id_try_from(thread) {
-                    Ok(thread) => thread,
-                    Err(_) => this.invalid_handle("WaitForSingleObject")?,
-                },
+        let thread = match Handle::try_from_scalar(handle, this)? {
+            Ok(Handle::Thread(thread)) => thread,
             // Unlike on posix, the outcome of joining the current thread is not documented.
             // On current Windows, it just deadlocks.
-            Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
+            Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
             _ => this.invalid_handle("WaitForSingleObject")?,
         };
 
diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs
index 33b1f27713c..4c6c1cefeb1 100644
--- a/src/tools/miri/src/shims/x86/aesni.rs
+++ b/src/tools/miri/src/shims/x86/aesni.rs
@@ -132,7 +132,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 // Performs an AES round (given by `f`) on each 128-bit word of
 // `state` with the corresponding 128-bit key of `key`.
 fn aes_round<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     state: &OpTy<'tcx>,
     key: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
@@ -145,21 +145,20 @@ fn aes_round<'tcx>(
     assert_eq!(dest.layout.size.bytes() % 16, 0);
     let len = dest.layout.size.bytes() / 16;
 
-    let u128_array_layout =
-        this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u128, len))?;
+    let u128_array_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u128, len))?;
 
-    let state = state.transmute(u128_array_layout, this)?;
-    let key = key.transmute(u128_array_layout, this)?;
-    let dest = dest.transmute(u128_array_layout, this)?;
+    let state = state.transmute(u128_array_layout, ecx)?;
+    let key = key.transmute(u128_array_layout, ecx)?;
+    let dest = dest.transmute(u128_array_layout, ecx)?;
 
     for i in 0..len {
-        let state = this.read_scalar(&this.project_index(&state, i)?)?.to_u128()?;
-        let key = this.read_scalar(&this.project_index(&key, i)?)?.to_u128()?;
-        let dest = this.project_index(&dest, i)?;
+        let state = ecx.read_scalar(&ecx.project_index(&state, i)?)?.to_u128()?;
+        let key = ecx.read_scalar(&ecx.project_index(&key, i)?)?.to_u128()?;
+        let dest = ecx.project_index(&dest, i)?;
 
         let res = f(state, key);
 
-        this.write_scalar(Scalar::from_u128(res), &dest)?;
+        ecx.write_scalar(Scalar::from_u128(res), &dest)?;
     }
 
     interp_ok(())
diff --git a/src/tools/miri/src/shims/x86/gfni.rs b/src/tools/miri/src/shims/x86/gfni.rs
index 7b92d422cc5..92010345f55 100644
--- a/src/tools/miri/src/shims/x86/gfni.rs
+++ b/src/tools/miri/src/shims/x86/gfni.rs
@@ -75,21 +75,21 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 /// If `inverse` is set, then the inverse transformation with respect to the reduction polynomial
 /// x^8 + x^4 + x^3 + x + 1 is performed instead.
 fn affine_transform<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     imm8: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
     inverse: bool,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, right_len);
     assert_eq!(dest_len, left_len);
 
-    let imm8 = this.read_scalar(imm8)?.to_u8()?;
+    let imm8 = ecx.read_scalar(imm8)?.to_u8()?;
 
     // Each 8x8 bit matrix gets multiplied with eight bit vectors.
     // Therefore, the iteration is done in chunks of eight.
@@ -98,13 +98,13 @@ fn affine_transform<'tcx>(
         let mut matrix = [0u8; 8];
         for j in 0..8 {
             matrix[usize::try_from(j).unwrap()] =
-                this.read_scalar(&this.project_index(&right, i.wrapping_add(j))?)?.to_u8()?;
+                ecx.read_scalar(&ecx.project_index(&right, i.wrapping_add(j))?)?.to_u8()?;
         }
 
         // Multiply the matrix with the vector and perform the addition.
         for j in 0..8 {
             let index = i.wrapping_add(j);
-            let left = this.read_scalar(&this.project_index(&left, index)?)?.to_u8()?;
+            let left = ecx.read_scalar(&ecx.project_index(&left, index)?)?.to_u8()?;
             let left = if inverse { TABLE[usize::from(left)] } else { left };
 
             let mut res = 0;
@@ -124,8 +124,8 @@ fn affine_transform<'tcx>(
             // Perform the addition.
             res ^= imm8;
 
-            let dest = this.project_index(&dest, index)?;
-            this.write_scalar(Scalar::from_u8(res), &dest)?;
+            let dest = ecx.project_index(&dest, index)?;
+            ecx.write_scalar(Scalar::from_u8(res), &dest)?;
         }
     }
 
diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs
index 433e9e966f2..3e02a4b3637 100644
--- a/src/tools/miri/src/shims/x86/mod.rs
+++ b/src/tools/miri/src/shims/x86/mod.rs
@@ -1,4 +1,3 @@
-use rand::Rng as _;
 use rustc_abi::{ExternAbi, Size};
 use rustc_apfloat::Float;
 use rustc_apfloat::ieee::Single;
@@ -226,7 +225,7 @@ impl FloatBinOp {
     /// Convert from the `imm` argument used to specify the comparison
     /// operation in intrinsics such as `llvm.x86.sse.cmp.ss`.
     fn cmp_from_imm<'tcx>(
-        this: &crate::MiriInterpCx<'tcx>,
+        ecx: &crate::MiriInterpCx<'tcx>,
         imm: i8,
         intrinsic: Symbol,
     ) -> InterpResult<'tcx, Self> {
@@ -260,7 +259,7 @@ impl FloatBinOp {
         };
         // When bit 3 is 1 (only possible in AVX), unord is toggled.
         if imm & 0b1000 != 0 {
-            this.expect_target_feature_for_intrinsic(intrinsic, "avx")?;
+            ecx.expect_target_feature_for_intrinsic(intrinsic, "avx")?;
             unord = !unord;
         }
         interp_ok(Self::Cmp { gt, lt, eq, unord })
@@ -327,28 +326,28 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>(
 /// Performs `which` operation on the first component of `left` and `right`
 /// and copies the other components from `left`. The result is stored in `dest`.
 fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: FloatBinOp,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
     let res0 = bin_op_float::<F>(
         which,
-        &this.read_immediate(&this.project_index(&left, 0)?)?,
-        &this.read_immediate(&this.project_index(&right, 0)?)?,
+        &ecx.read_immediate(&ecx.project_index(&left, 0)?)?,
+        &ecx.read_immediate(&ecx.project_index(&right, 0)?)?,
     )?;
-    this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
+    ecx.write_scalar(res0, &ecx.project_index(&dest, 0)?)?;
 
     for i in 1..dest_len {
-        this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?;
+        ecx.copy_op(&ecx.project_index(&left, i)?, &ecx.project_index(&dest, i)?)?;
     }
 
     interp_ok(())
@@ -357,26 +356,26 @@ fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>(
 /// Performs `which` operation on each component of `left` and
 /// `right`, storing the result is stored in `dest`.
 fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: FloatBinOp,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
     for i in 0..dest_len {
-        let left = this.read_immediate(&this.project_index(&left, i)?)?;
-        let right = this.read_immediate(&this.project_index(&right, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
+        let right = ecx.read_immediate(&ecx.project_index(&right, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         let res = bin_op_float::<F>(which, &left, &right)?;
-        this.write_scalar(res, &dest)?;
+        ecx.write_scalar(res, &dest)?;
     }
 
     interp_ok(())
@@ -398,7 +397,7 @@ enum FloatUnaryOp {
 
 /// Performs `which` scalar operation on `op` and returns the result.
 fn unary_op_f32<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: FloatUnaryOp,
     op: &ImmTy<'tcx>,
 ) -> InterpResult<'tcx, Scalar> {
@@ -408,56 +407,38 @@ fn unary_op_f32<'tcx>(
             let div = (Single::from_u128(1).value / op).value;
             // Apply a relative error with a magnitude on the order of 2^-12 to simulate the
             // inaccuracy of RCP.
-            let res = apply_random_float_error(this, div, -12);
+            let res = math::apply_random_float_error(ecx, div, -12);
             interp_ok(Scalar::from_f32(res))
         }
         FloatUnaryOp::Rsqrt => {
-            let op = op.to_scalar().to_u32()?;
-            // FIXME using host floats
-            let sqrt = Single::from_bits(f32::from_bits(op).sqrt().to_bits().into());
-            let rsqrt = (Single::from_u128(1).value / sqrt).value;
+            let op = op.to_scalar().to_f32()?;
+            let rsqrt = (Single::from_u128(1).value / math::sqrt(op)).value;
             // Apply a relative error with a magnitude on the order of 2^-12 to simulate the
             // inaccuracy of RSQRT.
-            let res = apply_random_float_error(this, rsqrt, -12);
+            let res = math::apply_random_float_error(ecx, rsqrt, -12);
             interp_ok(Scalar::from_f32(res))
         }
     }
 }
 
-/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
-#[expect(clippy::arithmetic_side_effects)] // floating point arithmetic cannot panic
-fn apply_random_float_error<F: rustc_apfloat::Float>(
-    this: &mut crate::MiriInterpCx<'_>,
-    val: F,
-    err_scale: i32,
-) -> F {
-    let rng = this.machine.rng.get_mut();
-    // generates rand(0, 2^64) * 2^(scale - 64) = rand(0, 1) * 2^scale
-    let err = F::from_u128(rng.gen::<u64>().into()).value.scalbn(err_scale.strict_sub(64));
-    // give it a random sign
-    let err = if rng.gen::<bool>() { -err } else { err };
-    // multiple the value with (1+err)
-    (val * (F::from_u128(1).value + err).value).value
-}
-
 /// Performs `which` operation on the first component of `op` and copies
 /// the other components. The result is stored in `dest`.
 fn unary_op_ss<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: FloatUnaryOp,
     op: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, op_len);
 
-    let res0 = unary_op_f32(this, which, &this.read_immediate(&this.project_index(&op, 0)?)?)?;
-    this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
+    let res0 = unary_op_f32(ecx, which, &ecx.read_immediate(&ecx.project_index(&op, 0)?)?)?;
+    ecx.write_scalar(res0, &ecx.project_index(&dest, 0)?)?;
 
     for i in 1..dest_len {
-        this.copy_op(&this.project_index(&op, i)?, &this.project_index(&dest, i)?)?;
+        ecx.copy_op(&ecx.project_index(&op, i)?, &ecx.project_index(&dest, i)?)?;
     }
 
     interp_ok(())
@@ -466,22 +447,22 @@ fn unary_op_ss<'tcx>(
 /// Performs `which` operation on each component of `op`, storing the
 /// result is stored in `dest`.
 fn unary_op_ps<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: FloatUnaryOp,
     op: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, op_len);
 
     for i in 0..dest_len {
-        let op = this.read_immediate(&this.project_index(&op, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let op = ecx.read_immediate(&ecx.project_index(&op, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
-        let res = unary_op_f32(this, which, &op)?;
-        this.write_scalar(res, &dest)?;
+        let res = unary_op_f32(ecx, which, &op)?;
+        ecx.write_scalar(res, &dest)?;
     }
 
     interp_ok(())
@@ -503,14 +484,14 @@ enum ShiftOp {
 /// For arithmetic right-shifts, when right is larger than BITS - 1, the sign
 /// bit is copied to all bits.
 fn shift_simd_by_scalar<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     which: ShiftOp,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     // `right` may have a different length, and we only care about its
@@ -520,11 +501,11 @@ fn shift_simd_by_scalar<'tcx>(
     // by checked_{shl,shr} (u32).
     // It is ok to saturate the value to u32::MAX because any value
     // above BITS - 1 will produce the same result.
-    let shift = u32::try_from(extract_first_u64(this, right)?).unwrap_or(u32::MAX);
+    let shift = u32::try_from(extract_first_u64(ecx, right)?).unwrap_or(u32::MAX);
 
     for i in 0..dest_len {
-        let left = this.read_scalar(&this.project_index(&left, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         let res = match which {
             ShiftOp::Left => {
@@ -547,7 +528,7 @@ fn shift_simd_by_scalar<'tcx>(
                 Scalar::from_int(res, dest.layout.size)
             }
         };
-        this.write_scalar(res, &dest)?;
+        ecx.write_scalar(res, &dest)?;
     }
 
     interp_ok(())
@@ -559,23 +540,23 @@ fn shift_simd_by_scalar<'tcx>(
 /// For arithmetic right-shifts, when right is larger than BITS - 1, the sign
 /// bit is copied to all bits.
 fn shift_simd_by_simd<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     which: ShiftOp,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
     for i in 0..dest_len {
-        let left = this.read_scalar(&this.project_index(&left, i)?)?;
-        let right = this.read_scalar(&this.project_index(&right, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?;
+        let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         // It is ok to saturate the value to u32::MAX because any value
         // above BITS - 1 will produce the same result.
@@ -602,7 +583,7 @@ fn shift_simd_by_simd<'tcx>(
                 Scalar::from_int(res, dest.layout.size)
             }
         };
-        this.write_scalar(res, &dest)?;
+        ecx.write_scalar(res, &dest)?;
     }
 
     interp_ok(())
@@ -611,44 +592,44 @@ fn shift_simd_by_simd<'tcx>(
 /// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts
 /// the first value.
 fn extract_first_u64<'tcx>(
-    this: &crate::MiriInterpCx<'tcx>,
+    ecx: &crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
 ) -> InterpResult<'tcx, u64> {
     // Transmute vector to `[u64; 2]`
-    let array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?;
-    let op = op.transmute(array_layout, this)?;
+    let array_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u64, 2))?;
+    let op = op.transmute(array_layout, ecx)?;
 
     // Get the first u64 from the array
-    this.read_scalar(&this.project_index(&op, 0)?)?.to_u64()
+    ecx.read_scalar(&ecx.project_index(&op, 0)?)?.to_u64()
 }
 
 // Rounds the first element of `right` according to `rounding`
 // and copies the remaining elements from `left`.
 fn round_first<'tcx, F: rustc_apfloat::Float>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     rounding: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
-    let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;
+    let rounding = rounding_from_imm(ecx.read_scalar(rounding)?.to_i32()?)?;
 
-    let op0: F = this.read_scalar(&this.project_index(&right, 0)?)?.to_float()?;
+    let op0: F = ecx.read_scalar(&ecx.project_index(&right, 0)?)?.to_float()?;
     let res = op0.round_to_integral(rounding).value;
-    this.write_scalar(
+    ecx.write_scalar(
         Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
-        &this.project_index(&dest, 0)?,
+        &ecx.project_index(&dest, 0)?,
     )?;
 
     for i in 1..dest_len {
-        this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?;
+        ecx.copy_op(&ecx.project_index(&left, i)?, &ecx.project_index(&dest, i)?)?;
     }
 
     interp_ok(())
@@ -656,24 +637,24 @@ fn round_first<'tcx, F: rustc_apfloat::Float>(
 
 // Rounds all elements of `op` according to `rounding`.
 fn round_all<'tcx, F: rustc_apfloat::Float>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     rounding: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, op_len);
 
-    let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;
+    let rounding = rounding_from_imm(ecx.read_scalar(rounding)?.to_i32()?)?;
 
     for i in 0..dest_len {
-        let op: F = this.read_scalar(&this.project_index(&op, i)?)?.to_float()?;
+        let op: F = ecx.read_scalar(&ecx.project_index(&op, i)?)?.to_float()?;
         let res = op.round_to_integral(rounding).value;
-        this.write_scalar(
+        ecx.write_scalar(
             Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
-            &this.project_index(&dest, i)?,
+            &ecx.project_index(&dest, i)?,
         )?;
     }
 
@@ -708,31 +689,31 @@ fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::R
 /// If `op` has more elements than `dest`, extra elements are ignored. If `op`
 /// has less elements than `dest`, the rest is filled with zeros.
 fn convert_float_to_int<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     rnd: rustc_apfloat::Round,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     // Output must be *signed* integers.
-    assert!(matches!(dest.layout.field(this, 0).ty.kind(), ty::Int(_)));
+    assert!(matches!(dest.layout.field(ecx, 0).ty.kind(), ty::Int(_)));
 
     for i in 0..op_len.min(dest_len) {
-        let op = this.read_immediate(&this.project_index(&op, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let op = ecx.read_immediate(&ecx.project_index(&op, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
-        let res = this.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| {
+        let res = ecx.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| {
             // Fallback to minimum according to SSE/AVX semantics.
             ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout)
         });
-        this.write_immediate(*res, &dest)?;
+        ecx.write_immediate(*res, &dest)?;
     }
     // Fill remainder with zeros
     for i in op_len..dest_len {
-        let dest = this.project_index(&dest, i)?;
-        this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
+        let dest = ecx.project_index(&dest, i)?;
+        ecx.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
     }
 
     interp_ok(())
@@ -743,26 +724,26 @@ fn convert_float_to_int<'tcx>(
 /// In case of overflow (when the operand is the minimum value), the operation
 /// will wrap around.
 fn int_abs<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(op_len, dest_len);
 
-    let zero = ImmTy::from_int(0, op.layout.field(this, 0));
+    let zero = ImmTy::from_int(0, op.layout.field(ecx, 0));
 
     for i in 0..dest_len {
-        let op = this.read_immediate(&this.project_index(&op, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let op = ecx.read_immediate(&ecx.project_index(&op, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
-        let lt_zero = this.binary_op(mir::BinOp::Lt, &op, &zero)?;
+        let lt_zero = ecx.binary_op(mir::BinOp::Lt, &op, &zero)?;
         let res =
-            if lt_zero.to_scalar().to_bool()? { this.unary_op(mir::UnOp::Neg, &op)? } else { op };
+            if lt_zero.to_scalar().to_bool()? { ecx.unary_op(mir::UnOp::Neg, &op)? } else { op };
 
-        this.write_immediate(*res, &dest)?;
+        ecx.write_immediate(*res, &dest)?;
     }
 
     interp_ok(())
@@ -776,25 +757,25 @@ fn int_abs<'tcx>(
 /// * The third element is the `op` vector split into chunks, i.e, it's
 ///   type is `[[T; M]; N]` where `T` is the element type of `op`.
 fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     op: &P,
 ) -> InterpResult<'tcx, (u64, u64, P)> {
     let simd_layout = op.layout();
-    let (simd_len, element_ty) = simd_layout.ty.simd_size_and_type(this.tcx.tcx);
+    let (simd_len, element_ty) = simd_layout.ty.simd_size_and_type(ecx.tcx.tcx);
 
     assert_eq!(simd_layout.size.bits() % 128, 0);
     let num_chunks = simd_layout.size.bits() / 128;
     let items_per_chunk = simd_len.strict_div(num_chunks);
 
     // Transmute to `[[T; items_per_chunk]; num_chunks]`
-    let chunked_layout = this
+    let chunked_layout = ecx
         .layout_of(Ty::new_array(
-            this.tcx.tcx,
-            Ty::new_array(this.tcx.tcx, element_ty, items_per_chunk),
+            ecx.tcx.tcx,
+            Ty::new_array(ecx.tcx.tcx, element_ty, items_per_chunk),
             num_chunks,
         ))
         .unwrap();
-    let chunked_op = op.transmute(chunked_layout, this)?;
+    let chunked_op = op.transmute(chunked_layout, ecx)?;
 
     interp_ok((num_chunks, items_per_chunk, chunked_op))
 }
@@ -809,7 +790,7 @@ fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn horizontal_bin_op<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: mir::BinOp,
     saturating: bool,
     left: &OpTy<'tcx>,
@@ -819,15 +800,15 @@ fn horizontal_bin_op<'tcx>(
     assert_eq!(left.layout, dest.layout);
     assert_eq!(right.layout, dest.layout);
 
-    let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?;
-    let (_, _, right) = split_simd_to_128bit_chunks(this, right)?;
-    let (_, _, dest) = split_simd_to_128bit_chunks(this, dest)?;
+    let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
+    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
+    let (_, _, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
 
     let middle = items_per_chunk / 2;
     for i in 0..num_chunks {
-        let left = this.project_index(&left, i)?;
-        let right = this.project_index(&right, i)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.project_index(&left, i)?;
+        let right = ecx.project_index(&right, i)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         for j in 0..items_per_chunk {
             // `j` is the index in `dest`
@@ -835,16 +816,16 @@ fn horizontal_bin_op<'tcx>(
             let (k, src) = if j < middle { (j, &left) } else { (j.strict_sub(middle), &right) };
             // `base_i` is the index of the first item of the 2-item chunk in `src`
             let base_i = k.strict_mul(2);
-            let lhs = this.read_immediate(&this.project_index(src, base_i)?)?;
-            let rhs = this.read_immediate(&this.project_index(src, base_i.strict_add(1))?)?;
+            let lhs = ecx.read_immediate(&ecx.project_index(src, base_i)?)?;
+            let rhs = ecx.read_immediate(&ecx.project_index(src, base_i.strict_add(1))?)?;
 
             let res = if saturating {
-                Immediate::from(this.saturating_arith(which, &lhs, &rhs)?)
+                Immediate::from(ecx.saturating_arith(which, &lhs, &rhs)?)
             } else {
-                *this.binary_op(which, &lhs, &rhs)?
+                *ecx.binary_op(which, &lhs, &rhs)?
             };
 
-            this.write_immediate(res, &this.project_index(&dest, j)?)?;
+            ecx.write_immediate(res, &ecx.project_index(&dest, j)?)?;
         }
     }
 
@@ -860,7 +841,7 @@ fn horizontal_bin_op<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit blocks of `left` and `right`).
 fn conditional_dot_product<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     imm: &OpTy<'tcx>,
@@ -869,20 +850,20 @@ fn conditional_dot_product<'tcx>(
     assert_eq!(left.layout, dest.layout);
     assert_eq!(right.layout, dest.layout);
 
-    let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?;
-    let (_, _, right) = split_simd_to_128bit_chunks(this, right)?;
-    let (_, _, dest) = split_simd_to_128bit_chunks(this, dest)?;
+    let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
+    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
+    let (_, _, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
 
-    let element_layout = left.layout.field(this, 0).field(this, 0);
+    let element_layout = left.layout.field(ecx, 0).field(ecx, 0);
     assert!(items_per_chunk <= 4);
 
     // `imm` is a `u8` for SSE4.1 or an `i32` for AVX :/
-    let imm = this.read_scalar(imm)?.to_uint(imm.layout.size)?;
+    let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?;
 
     for i in 0..num_chunks {
-        let left = this.project_index(&left, i)?;
-        let right = this.project_index(&right, i)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.project_index(&left, i)?;
+        let right = ecx.project_index(&right, i)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         // Calculate dot product
         // Elements are floating point numbers, but we can use `from_int`
@@ -890,22 +871,22 @@ fn conditional_dot_product<'tcx>(
         let mut sum = ImmTy::from_int(0u8, element_layout);
         for j in 0..items_per_chunk {
             if imm & (1 << j.strict_add(4)) != 0 {
-                let left = this.read_immediate(&this.project_index(&left, j)?)?;
-                let right = this.read_immediate(&this.project_index(&right, j)?)?;
+                let left = ecx.read_immediate(&ecx.project_index(&left, j)?)?;
+                let right = ecx.read_immediate(&ecx.project_index(&right, j)?)?;
 
-                let mul = this.binary_op(mir::BinOp::Mul, &left, &right)?;
-                sum = this.binary_op(mir::BinOp::Add, &sum, &mul)?;
+                let mul = ecx.binary_op(mir::BinOp::Mul, &left, &right)?;
+                sum = ecx.binary_op(mir::BinOp::Add, &sum, &mul)?;
             }
         }
 
         // Write to destination (conditioned to imm)
         for j in 0..items_per_chunk {
-            let dest = this.project_index(&dest, j)?;
+            let dest = ecx.project_index(&dest, j)?;
 
             if imm & (1 << j) != 0 {
-                this.write_immediate(*sum, &dest)?;
+                ecx.write_immediate(*sum, &dest)?;
             } else {
-                this.write_scalar(Scalar::from_int(0u8, element_layout.size), &dest)?;
+                ecx.write_scalar(Scalar::from_int(0u8, element_layout.size), &dest)?;
             }
         }
     }
@@ -918,25 +899,25 @@ fn conditional_dot_product<'tcx>(
 /// The first is true when all the bits of `op & mask` are zero.
 /// The second is true when `(op & mask) == mask`
 fn test_bits_masked<'tcx>(
-    this: &crate::MiriInterpCx<'tcx>,
+    ecx: &crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     mask: &OpTy<'tcx>,
 ) -> InterpResult<'tcx, (bool, bool)> {
     assert_eq!(op.layout, mask.layout);
 
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (mask, mask_len) = this.project_to_simd(mask)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (mask, mask_len) = ecx.project_to_simd(mask)?;
 
     assert_eq!(op_len, mask_len);
 
     let mut all_zero = true;
     let mut masked_set = true;
     for i in 0..op_len {
-        let op = this.project_index(&op, i)?;
-        let mask = this.project_index(&mask, i)?;
+        let op = ecx.project_index(&op, i)?;
+        let mask = ecx.project_index(&mask, i)?;
 
-        let op = this.read_scalar(&op)?.to_uint(op.layout.size)?;
-        let mask = this.read_scalar(&mask)?.to_uint(mask.layout.size)?;
+        let op = ecx.read_scalar(&op)?.to_uint(op.layout.size)?;
+        let mask = ecx.read_scalar(&mask)?.to_uint(mask.layout.size)?;
         all_zero &= (op & mask) == 0;
         masked_set &= (op & mask) == mask;
     }
@@ -949,27 +930,27 @@ fn test_bits_masked<'tcx>(
 /// The first is true when the highest bit of each element of `op & mask` is zero.
 /// The second is true when the highest bit of each element of `!op & mask` is zero.
 fn test_high_bits_masked<'tcx>(
-    this: &crate::MiriInterpCx<'tcx>,
+    ecx: &crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     mask: &OpTy<'tcx>,
 ) -> InterpResult<'tcx, (bool, bool)> {
     assert_eq!(op.layout, mask.layout);
 
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (mask, mask_len) = this.project_to_simd(mask)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (mask, mask_len) = ecx.project_to_simd(mask)?;
 
     assert_eq!(op_len, mask_len);
 
-    let high_bit_offset = op.layout.field(this, 0).size.bits().strict_sub(1);
+    let high_bit_offset = op.layout.field(ecx, 0).size.bits().strict_sub(1);
 
     let mut direct = true;
     let mut negated = true;
     for i in 0..op_len {
-        let op = this.project_index(&op, i)?;
-        let mask = this.project_index(&mask, i)?;
+        let op = ecx.project_index(&op, i)?;
+        let mask = ecx.project_index(&mask, i)?;
 
-        let op = this.read_scalar(&op)?.to_uint(op.layout.size)?;
-        let mask = this.read_scalar(&mask)?.to_uint(mask.layout.size)?;
+        let op = ecx.read_scalar(&op)?.to_uint(op.layout.size)?;
+        let mask = ecx.read_scalar(&mask)?.to_uint(mask.layout.size)?;
         direct &= (op & mask) >> high_bit_offset == 0;
         negated &= (!op & mask) >> high_bit_offset == 0;
     }
@@ -980,30 +961,30 @@ fn test_high_bits_masked<'tcx>(
 /// Conditionally loads from `ptr` according the high bit of each
 /// element of `mask`. `ptr` does not need to be aligned.
 fn mask_load<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     ptr: &OpTy<'tcx>,
     mask: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (mask, mask_len) = this.project_to_simd(mask)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (mask, mask_len) = ecx.project_to_simd(mask)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, mask_len);
 
-    let mask_item_size = mask.layout.field(this, 0).size;
+    let mask_item_size = mask.layout.field(ecx, 0).size;
     let high_bit_offset = mask_item_size.bits().strict_sub(1);
 
-    let ptr = this.read_pointer(ptr)?;
+    let ptr = ecx.read_pointer(ptr)?;
     for i in 0..dest_len {
-        let mask = this.project_index(&mask, i)?;
-        let dest = this.project_index(&dest, i)?;
+        let mask = ecx.project_index(&mask, i)?;
+        let dest = ecx.project_index(&dest, i)?;
 
-        if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 {
-            let ptr = ptr.wrapping_offset(dest.layout.size * i, &this.tcx);
+        if ecx.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 {
+            let ptr = ptr.wrapping_offset(dest.layout.size * i, &ecx.tcx);
             // Unaligned copy, which is what we want.
-            this.mem_copy(ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?;
+            ecx.mem_copy(ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?;
         } else {
-            this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
+            ecx.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
         }
     }
 
@@ -1013,31 +994,31 @@ fn mask_load<'tcx>(
 /// Conditionally stores into `ptr` according the high bit of each
 /// element of `mask`. `ptr` does not need to be aligned.
 fn mask_store<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     ptr: &OpTy<'tcx>,
     mask: &OpTy<'tcx>,
     value: &OpTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (mask, mask_len) = this.project_to_simd(mask)?;
-    let (value, value_len) = this.project_to_simd(value)?;
+    let (mask, mask_len) = ecx.project_to_simd(mask)?;
+    let (value, value_len) = ecx.project_to_simd(value)?;
 
     assert_eq!(value_len, mask_len);
 
-    let mask_item_size = mask.layout.field(this, 0).size;
+    let mask_item_size = mask.layout.field(ecx, 0).size;
     let high_bit_offset = mask_item_size.bits().strict_sub(1);
 
-    let ptr = this.read_pointer(ptr)?;
+    let ptr = ecx.read_pointer(ptr)?;
     for i in 0..value_len {
-        let mask = this.project_index(&mask, i)?;
-        let value = this.project_index(&value, i)?;
+        let mask = ecx.project_index(&mask, i)?;
+        let value = ecx.project_index(&value, i)?;
 
-        if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 {
+        if ecx.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 {
             // *Non-inbounds* pointer arithmetic to compute the destination.
             // (That's why we can't use a place projection.)
-            let ptr = ptr.wrapping_offset(value.layout.size * i, &this.tcx);
+            let ptr = ptr.wrapping_offset(value.layout.size * i, &ecx.tcx);
             // Deref the pointer *unaligned*, and do the copy.
-            let dest = this.ptr_to_mplace_unaligned(ptr, value.layout);
-            this.copy_op(&value, &dest)?;
+            let dest = ecx.ptr_to_mplace_unaligned(ptr, value.layout);
+            ecx.copy_op(&value, &dest)?;
         }
     }
 
@@ -1056,7 +1037,7 @@ fn mask_store<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn mpsadbw<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     imm: &OpTy<'tcx>,
@@ -1065,13 +1046,13 @@ fn mpsadbw<'tcx>(
     assert_eq!(left.layout, right.layout);
     assert_eq!(left.layout.size, dest.layout.size);
 
-    let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?;
-    let (_, _, right) = split_simd_to_128bit_chunks(this, right)?;
-    let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(this, dest)?;
+    let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
+    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
+    let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
 
     assert_eq!(op_items_per_chunk, dest_items_per_chunk.strict_mul(2));
 
-    let imm = this.read_scalar(imm)?.to_uint(imm.layout.size)?;
+    let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?;
     // Bit 2 of `imm` specifies the offset for indices of `left`.
     // The offset is 0 when the bit is 0 or 4 when the bit is 1.
     let left_offset = u64::try_from((imm >> 2) & 1).unwrap().strict_mul(4);
@@ -1080,23 +1061,23 @@ fn mpsadbw<'tcx>(
     let right_offset = u64::try_from(imm & 0b11).unwrap().strict_mul(4);
 
     for i in 0..num_chunks {
-        let left = this.project_index(&left, i)?;
-        let right = this.project_index(&right, i)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.project_index(&left, i)?;
+        let right = ecx.project_index(&right, i)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         for j in 0..dest_items_per_chunk {
             let left_offset = left_offset.strict_add(j);
             let mut res: u16 = 0;
             for k in 0..4 {
-                let left = this
-                    .read_scalar(&this.project_index(&left, left_offset.strict_add(k))?)?
+                let left = ecx
+                    .read_scalar(&ecx.project_index(&left, left_offset.strict_add(k))?)?
                     .to_u8()?;
-                let right = this
-                    .read_scalar(&this.project_index(&right, right_offset.strict_add(k))?)?
+                let right = ecx
+                    .read_scalar(&ecx.project_index(&right, right_offset.strict_add(k))?)?
                     .to_u8()?;
                 res = res.strict_add(left.abs_diff(right).into());
             }
-            this.write_scalar(Scalar::from_u16(res), &this.project_index(&dest, j)?)?;
+            ecx.write_scalar(Scalar::from_u16(res), &ecx.project_index(&dest, j)?)?;
         }
     }
 
@@ -1111,22 +1092,22 @@ fn mpsadbw<'tcx>(
 /// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16>
 /// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16>
 fn pmulhrsw<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
     for i in 0..dest_len {
-        let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i16()?;
-        let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i16()?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?.to_i16()?;
+        let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_i16()?;
+        let dest = ecx.project_index(&dest, i)?;
 
         let res = (i32::from(left).strict_mul(right.into()) >> 14).strict_add(1) >> 1;
 
@@ -1135,7 +1116,7 @@ fn pmulhrsw<'tcx>(
         #[expect(clippy::cast_possible_truncation)]
         let res = res as i16;
 
-        this.write_scalar(Scalar::from_i16(res), &dest)?;
+        ecx.write_scalar(Scalar::from_i16(res), &dest)?;
     }
 
     interp_ok(())
@@ -1152,7 +1133,7 @@ fn pmulhrsw<'tcx>(
 ///
 /// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128>
 fn pclmulqdq<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     imm8: &OpTy<'tcx>,
@@ -1166,14 +1147,14 @@ fn pclmulqdq<'tcx>(
     // Transmute the input into arrays of `[u64; len]`.
     // Transmute the output into an array of `[u128, len / 2]`.
 
-    let src_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, len))?;
-    let dest_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u128, len / 2))?;
+    let src_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u64, len))?;
+    let dest_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u128, len / 2))?;
 
-    let left = left.transmute(src_layout, this)?;
-    let right = right.transmute(src_layout, this)?;
-    let dest = dest.transmute(dest_layout, this)?;
+    let left = left.transmute(src_layout, ecx)?;
+    let right = right.transmute(src_layout, ecx)?;
+    let dest = dest.transmute(dest_layout, ecx)?;
 
-    let imm8 = this.read_scalar(imm8)?.to_u8()?;
+    let imm8 = ecx.read_scalar(imm8)?.to_u8()?;
 
     for i in 0..(len / 2) {
         let lo = i.strict_mul(2);
@@ -1181,11 +1162,11 @@ fn pclmulqdq<'tcx>(
 
         // select the 64-bit integer from left that the user specified (low or high)
         let index = if (imm8 & 0x01) == 0 { lo } else { hi };
-        let left = this.read_scalar(&this.project_index(&left, index)?)?.to_u64()?;
+        let left = ecx.read_scalar(&ecx.project_index(&left, index)?)?.to_u64()?;
 
         // select the 64-bit integer from right that the user specified (low or high)
         let index = if (imm8 & 0x10) == 0 { lo } else { hi };
-        let right = this.read_scalar(&this.project_index(&right, index)?)?.to_u64()?;
+        let right = ecx.read_scalar(&ecx.project_index(&right, index)?)?.to_u64()?;
 
         // Perform carry-less multiplication.
         //
@@ -1203,8 +1184,8 @@ fn pclmulqdq<'tcx>(
             }
         }
 
-        let dest = this.project_index(&dest, i)?;
-        this.write_scalar(Scalar::from_u128(result), &dest)?;
+        let dest = ecx.project_index(&dest, i)?;
+        ecx.write_scalar(Scalar::from_u128(result), &dest)?;
     }
 
     interp_ok(())
@@ -1218,7 +1199,7 @@ fn pclmulqdq<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn pack_generic<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
@@ -1227,28 +1208,28 @@ fn pack_generic<'tcx>(
     assert_eq!(left.layout, right.layout);
     assert_eq!(left.layout.size, dest.layout.size);
 
-    let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?;
-    let (_, _, right) = split_simd_to_128bit_chunks(this, right)?;
-    let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(this, dest)?;
+    let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
+    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
+    let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
 
     assert_eq!(dest_items_per_chunk, op_items_per_chunk.strict_mul(2));
 
     for i in 0..num_chunks {
-        let left = this.project_index(&left, i)?;
-        let right = this.project_index(&right, i)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.project_index(&left, i)?;
+        let right = ecx.project_index(&right, i)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         for j in 0..op_items_per_chunk {
-            let left = this.read_scalar(&this.project_index(&left, j)?)?;
-            let right = this.read_scalar(&this.project_index(&right, j)?)?;
-            let left_dest = this.project_index(&dest, j)?;
-            let right_dest = this.project_index(&dest, j.strict_add(op_items_per_chunk))?;
+            let left = ecx.read_scalar(&ecx.project_index(&left, j)?)?;
+            let right = ecx.read_scalar(&ecx.project_index(&right, j)?)?;
+            let left_dest = ecx.project_index(&dest, j)?;
+            let right_dest = ecx.project_index(&dest, j.strict_add(op_items_per_chunk))?;
 
             let left_res = f(left)?;
             let right_res = f(right)?;
 
-            this.write_scalar(left_res, &left_dest)?;
-            this.write_scalar(right_res, &right_dest)?;
+            ecx.write_scalar(left_res, &left_dest)?;
+            ecx.write_scalar(right_res, &right_dest)?;
         }
     }
 
@@ -1262,12 +1243,12 @@ fn pack_generic<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn packsswb<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    pack_generic(this, left, right, dest, |op| {
+    pack_generic(ecx, left, right, dest, |op| {
         let op = op.to_i16()?;
         let res = i8::try_from(op).unwrap_or(if op < 0 { i8::MIN } else { i8::MAX });
         interp_ok(Scalar::from_i8(res))
@@ -1281,12 +1262,12 @@ fn packsswb<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn packuswb<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    pack_generic(this, left, right, dest, |op| {
+    pack_generic(ecx, left, right, dest, |op| {
         let op = op.to_i16()?;
         let res = u8::try_from(op).unwrap_or(if op < 0 { 0 } else { u8::MAX });
         interp_ok(Scalar::from_u8(res))
@@ -1300,12 +1281,12 @@ fn packuswb<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn packssdw<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    pack_generic(this, left, right, dest, |op| {
+    pack_generic(ecx, left, right, dest, |op| {
         let op = op.to_i32()?;
         let res = i16::try_from(op).unwrap_or(if op < 0 { i16::MIN } else { i16::MAX });
         interp_ok(Scalar::from_i16(res))
@@ -1319,12 +1300,12 @@ fn packssdw<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn packusdw<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    pack_generic(this, left, right, dest, |op| {
+    pack_generic(ecx, left, right, dest, |op| {
         let op = op.to_i32()?;
         let res = u16::try_from(op).unwrap_or(if op < 0 { 0 } else { u16::MAX });
         interp_ok(Scalar::from_u16(res))
@@ -1336,27 +1317,27 @@ fn packusdw<'tcx>(
 /// is written to the corresponding output element.
 /// In other words, multiplies `left` with `right.signum()`.
 fn psign<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
     for i in 0..dest_len {
-        let dest = this.project_index(&dest, i)?;
-        let left = this.read_immediate(&this.project_index(&left, i)?)?;
-        let right = this.read_scalar(&this.project_index(&right, i)?)?.to_int(dest.layout.size)?;
+        let dest = ecx.project_index(&dest, i)?;
+        let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
+        let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_int(dest.layout.size)?;
 
         let res =
-            this.binary_op(mir::BinOp::Mul, &left, &ImmTy::from_int(right.signum(), dest.layout))?;
+            ecx.binary_op(mir::BinOp::Mul, &left, &ImmTy::from_int(right.signum(), dest.layout))?;
 
-        this.write_immediate(*res, &dest)?;
+        ecx.write_immediate(*res, &dest)?;
     }
 
     interp_ok(())
@@ -1366,7 +1347,7 @@ fn psign<'tcx>(
 /// of `op` and returns both the sum and the overflow bit. `op` is expected to be
 /// either one of `mir::BinOp::AddWithOverflow` and `mir::BinOp::SubWithOverflow`.
 fn carrying_add<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     cb_in: &OpTy<'tcx>,
     a: &OpTy<'tcx>,
     b: &OpTy<'tcx>,
@@ -1374,13 +1355,13 @@ fn carrying_add<'tcx>(
 ) -> InterpResult<'tcx, (ImmTy<'tcx>, Scalar)> {
     assert!(op == mir::BinOp::AddWithOverflow || op == mir::BinOp::SubWithOverflow);
 
-    let cb_in = this.read_scalar(cb_in)?.to_u8()? != 0;
-    let a = this.read_immediate(a)?;
-    let b = this.read_immediate(b)?;
+    let cb_in = ecx.read_scalar(cb_in)?.to_u8()? != 0;
+    let a = ecx.read_immediate(a)?;
+    let b = ecx.read_immediate(b)?;
 
-    let (sum, overflow1) = this.binary_op(op, &a, &b)?.to_pair(this);
+    let (sum, overflow1) = ecx.binary_op(op, &a, &b)?.to_pair(ecx);
     let (sum, overflow2) =
-        this.binary_op(op, &sum, &ImmTy::from_uint(cb_in, a.layout))?.to_pair(this);
+        ecx.binary_op(op, &sum, &ImmTy::from_uint(cb_in, a.layout))?.to_pair(ecx);
     let cb_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?;
 
     interp_ok((sum, Scalar::from_u8(cb_out.into())))
diff --git a/src/tools/miri/src/shims/x86/sha.rs b/src/tools/miri/src/shims/x86/sha.rs
index 964204127d9..f18ff1ec253 100644
--- a/src/tools/miri/src/shims/x86/sha.rs
+++ b/src/tools/miri/src/shims/x86/sha.rs
@@ -23,27 +23,27 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Prefix should have already been checked.
         let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sha").unwrap();
 
-        fn read<'c>(this: &mut MiriInterpCx<'c>, reg: &OpTy<'c>) -> InterpResult<'c, [u32; 4]> {
+        fn read<'c>(ecx: &mut MiriInterpCx<'c>, reg: &OpTy<'c>) -> InterpResult<'c, [u32; 4]> {
             let mut res = [0; 4];
             // We reverse the order because x86 is little endian but the copied implementation uses
             // big endian.
             for (i, dst) in res.iter_mut().rev().enumerate() {
-                let projected = &this.project_index(reg, i.try_into().unwrap())?;
-                *dst = this.read_scalar(projected)?.to_u32()?
+                let projected = &ecx.project_index(reg, i.try_into().unwrap())?;
+                *dst = ecx.read_scalar(projected)?.to_u32()?
             }
             interp_ok(res)
         }
 
         fn write<'c>(
-            this: &mut MiriInterpCx<'c>,
+            ecx: &mut MiriInterpCx<'c>,
             dest: &MPlaceTy<'c>,
             val: [u32; 4],
         ) -> InterpResult<'c, ()> {
             // We reverse the order because x86 is little endian but the copied implementation uses
             // big endian.
             for (i, part) in val.into_iter().rev().enumerate() {
-                let projected = &this.project_index(dest, i.try_into().unwrap())?;
-                this.write_scalar(Scalar::from_u32(part), projected)?;
+                let projected = &ecx.project_index(dest, i.try_into().unwrap())?;
+                ecx.write_scalar(Scalar::from_u32(part), projected)?;
             }
             interp_ok(())
         }
diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs
index cc7cfab5041..0b058a9911e 100644
--- a/src/tools/miri/src/shims/x86/sse42.rs
+++ b/src/tools/miri/src/shims/x86/sse42.rs
@@ -70,7 +70,7 @@ const USE_SIGNED: u8 = 2;
 /// For more information, see the Intel Software Developer's Manual, Vol. 2b, Chapter 4.1.
 #[expect(clippy::arithmetic_side_effects)]
 fn compare_strings<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     str1: &OpTy<'tcx>,
     str2: &OpTy<'tcx>,
     len: Option<(u64, u64)>,
@@ -80,8 +80,8 @@ fn compare_strings<'tcx>(
     let (len1, len2) = if let Some(t) = len {
         t
     } else {
-        let len1 = implicit_len(this, str1, imm)?.unwrap_or(default_len);
-        let len2 = implicit_len(this, str2, imm)?.unwrap_or(default_len);
+        let len1 = implicit_len(ecx, str1, imm)?.unwrap_or(default_len);
+        let len2 = implicit_len(ecx, str2, imm)?.unwrap_or(default_len);
         (len1, len2)
     };
 
@@ -90,12 +90,12 @@ fn compare_strings<'tcx>(
         0 => {
             // Equal any: Checks which characters of `str2` are inside `str1`.
             for i in 0..len2 {
-                let ch2 = this.read_immediate(&this.project_index(str2, i)?)?;
+                let ch2 = ecx.read_immediate(&ecx.project_index(str2, i)?)?;
 
                 for j in 0..len1 {
-                    let ch1 = this.read_immediate(&this.project_index(str1, j)?)?;
+                    let ch1 = ecx.read_immediate(&ecx.project_index(str1, j)?)?;
 
-                    let eq = this.binary_op(mir::BinOp::Eq, &ch1, &ch2)?;
+                    let eq = ecx.binary_op(mir::BinOp::Eq, &ch1, &ch2)?;
                     if eq.to_scalar().to_bool()? {
                         result |= 1 << i;
                         break;
@@ -119,9 +119,9 @@ fn compare_strings<'tcx>(
 
             for i in 0..len2 {
                 for j in (0..len1).step_by(2) {
-                    let ch2 = get_ch(this.read_scalar(&this.project_index(str2, i)?)?)?;
-                    let ch1_1 = get_ch(this.read_scalar(&this.project_index(str1, j)?)?)?;
-                    let ch1_2 = get_ch(this.read_scalar(&this.project_index(str1, j + 1)?)?)?;
+                    let ch2 = get_ch(ecx.read_scalar(&ecx.project_index(str2, i)?)?)?;
+                    let ch1_1 = get_ch(ecx.read_scalar(&ecx.project_index(str1, j)?)?)?;
+                    let ch1_2 = get_ch(ecx.read_scalar(&ecx.project_index(str1, j + 1)?)?)?;
 
                     if ch1_1 <= ch2 && ch2 <= ch1_2 {
                         result |= 1 << i;
@@ -135,9 +135,9 @@ fn compare_strings<'tcx>(
             result ^= (1 << len1.max(len2)) - 1;
 
             for i in 0..len1.min(len2) {
-                let ch1 = this.read_immediate(&this.project_index(str1, i)?)?;
-                let ch2 = this.read_immediate(&this.project_index(str2, i)?)?;
-                let eq = this.binary_op(mir::BinOp::Eq, &ch1, &ch2)?;
+                let ch1 = ecx.read_immediate(&ecx.project_index(str1, i)?)?;
+                let ch2 = ecx.read_immediate(&ecx.project_index(str2, i)?)?;
+                let eq = ecx.binary_op(mir::BinOp::Eq, &ch1, &ch2)?;
                 result |= i32::from(eq.to_scalar().to_bool()?) << i;
             }
         }
@@ -159,9 +159,9 @@ fn compare_strings<'tcx>(
                         if k >= default_len {
                             break;
                         } else {
-                            let ch1 = this.read_immediate(&this.project_index(str1, j)?)?;
-                            let ch2 = this.read_immediate(&this.project_index(str2, k)?)?;
-                            let ne = this.binary_op(mir::BinOp::Ne, &ch1, &ch2)?;
+                            let ch1 = ecx.read_immediate(&ecx.project_index(str1, j)?)?;
+                            let ch2 = ecx.read_immediate(&ecx.project_index(str2, k)?)?;
+                            let ne = ecx.binary_op(mir::BinOp::Ne, &ch1, &ch2)?;
 
                             if ne.to_scalar().to_bool()? {
                                 result &= !(1 << i);
@@ -198,16 +198,16 @@ fn compare_strings<'tcx>(
 /// corresponding to the x86 128-bit integer SIMD type.
 fn deconstruct_args<'tcx>(
     unprefixed_name: &str,
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     link_name: Symbol,
     abi: ExternAbi,
     args: &[OpTy<'tcx>],
 ) -> InterpResult<'tcx, (OpTy<'tcx>, OpTy<'tcx>, Option<(u64, u64)>, u8)> {
-    let array_layout_fn = |this: &mut MiriInterpCx<'tcx>, imm: u8| {
+    let array_layout_fn = |ecx: &mut MiriInterpCx<'tcx>, imm: u8| {
         if imm & USE_WORDS != 0 {
-            this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u16, 8))
+            ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u16, 8))
         } else {
-            this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u8, 16))
+            ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u8, 16))
         }
     };
 
@@ -223,26 +223,26 @@ fn deconstruct_args<'tcx>(
 
     if is_explicit {
         let [str1, len1, str2, len2, imm] =
-            this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
-        let imm = this.read_scalar(imm)?.to_u8()?;
+            ecx.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+        let imm = ecx.read_scalar(imm)?.to_u8()?;
 
         let default_len = default_len::<u32>(imm);
-        let len1 = u64::from(this.read_scalar(len1)?.to_u32()?.min(default_len));
-        let len2 = u64::from(this.read_scalar(len2)?.to_u32()?.min(default_len));
+        let len1 = u64::from(ecx.read_scalar(len1)?.to_u32()?.min(default_len));
+        let len2 = u64::from(ecx.read_scalar(len2)?.to_u32()?.min(default_len));
 
-        let array_layout = array_layout_fn(this, imm)?;
-        let str1 = str1.transmute(array_layout, this)?;
-        let str2 = str2.transmute(array_layout, this)?;
+        let array_layout = array_layout_fn(ecx, imm)?;
+        let str1 = str1.transmute(array_layout, ecx)?;
+        let str2 = str2.transmute(array_layout, ecx)?;
 
         interp_ok((str1, str2, Some((len1, len2)), imm))
     } else {
         let [str1, str2, imm] =
-            this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
-        let imm = this.read_scalar(imm)?.to_u8()?;
+            ecx.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+        let imm = ecx.read_scalar(imm)?.to_u8()?;
 
-        let array_layout = array_layout_fn(this, imm)?;
-        let str1 = str1.transmute(array_layout, this)?;
-        let str2 = str2.transmute(array_layout, this)?;
+        let array_layout = array_layout_fn(ecx, imm)?;
+        let str1 = str1.transmute(array_layout, ecx)?;
+        let str2 = str2.transmute(array_layout, ecx)?;
 
         interp_ok((str1, str2, None, imm))
     }
@@ -251,16 +251,16 @@ fn deconstruct_args<'tcx>(
 /// Calculate the c-style string length for a given string `str`.
 /// The string is either a length 16 array of bytes a length 8 array of two-byte words.
 fn implicit_len<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     str: &OpTy<'tcx>,
     imm: u8,
 ) -> InterpResult<'tcx, Option<u64>> {
     let mut result = None;
-    let zero = ImmTy::from_int(0, str.layout.field(this, 0));
+    let zero = ImmTy::from_int(0, str.layout.field(ecx, 0));
 
     for i in 0..default_len::<u64>(imm) {
-        let ch = this.read_immediate(&this.project_index(str, i)?)?;
-        let is_zero = this.binary_op(mir::BinOp::Eq, &ch, &zero)?;
+        let ch = ecx.read_immediate(&ecx.project_index(str, i)?)?;
+        let is_zero = ecx.binary_op(mir::BinOp::Eq, &ch, &zero)?;
         if is_zero.to_scalar().to_bool()? {
             result = Some(i);
             break;
diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs
new file mode 100644
index 00000000000..65d29b2c6ba
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs
@@ -0,0 +1,65 @@
+//@only-target: linux
+//~^ERROR: deadlocked
+//~^^ERROR: deadlocked
+//@compile-flags: -Zmiri-preemption-rate=0
+//@error-in-other-file: deadlock
+
+use std::thread;
+
+// Test the behaviour of a thread being blocked on an eventfd read, get unblocked, and then
+// get blocked again.
+
+// The expected execution is
+// 1. Thread 1 blocks.
+// 2. Thread 2 blocks.
+// 3. Thread 3 unblocks both thread 1 and thread 2.
+// 4. Thread 1 reads.
+// 5. Thread 2's `read` deadlocked.
+
+fn main() {
+    // eventfd write will block when EFD_NONBLOCK flag is clear
+    // and the addition caused counter to exceed u64::MAX - 1.
+    let flags = libc::EFD_CLOEXEC;
+    let fd = unsafe { libc::eventfd(0, flags) };
+
+    let thread1 = thread::spawn(move || {
+        thread::park();
+        let mut buf: [u8; 8] = [0; 8];
+        // This read will block initially.
+        let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
+        assert_eq!(res, 8);
+        let counter = u64::from_ne_bytes(buf);
+        assert_eq!(counter, 1_u64);
+    });
+
+    let thread2 = thread::spawn(move || {
+        thread::park();
+        let mut buf: [u8; 8] = [0; 8];
+        // This read will block initially, then get unblocked by thread3, then get blocked again
+        // because the `read` in thread1 executes first and set the counter to 0 again.
+        let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
+        //~^ERROR: deadlocked
+        assert_eq!(res, 8);
+        let counter = u64::from_ne_bytes(buf);
+        assert_eq!(counter, 1_u64);
+    });
+
+    let thread3 = thread::spawn(move || {
+        thread::park();
+        let sized_8_data = 1_u64.to_ne_bytes();
+        // Write 1 to the counter, so both thread1 and thread2 will unblock.
+        let res: i64 = unsafe {
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+
+    thread1.thread().unpark();
+    thread2.thread().unpark();
+    thread3.thread().unpark();
+
+    thread1.join().unwrap();
+    thread2.join().unwrap();
+    thread3.join().unwrap();
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr
new file mode 100644
index 00000000000..bb235345c5e
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr
@@ -0,0 +1,41 @@
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
+   |                                                                  ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> tests/fail-dep/libc/eventfd_block_read_twice.rs:LL:CC
+   |
+LL |     thread2.join().unwrap();
+   |     ^^^^^^^^^^^^^^
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+error: deadlock: the evaluated program deadlocked
+  --> tests/fail-dep/libc/eventfd_block_read_twice.rs:LL:CC
+   |
+LL |         let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
+   |                                                                          ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside closure at tests/fail-dep/libc/eventfd_block_read_twice.rs:LL:CC
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs
new file mode 100644
index 00000000000..f9d34d2fb58
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs
@@ -0,0 +1,71 @@
+//@only-target: linux
+//~^ERROR: deadlocked
+//~^^ERROR: deadlocked
+//@compile-flags: -Zmiri-preemption-rate=0
+//@error-in-other-file: deadlock
+
+use std::thread;
+
+// Test the behaviour of a thread being blocked on an eventfd `write`, get unblocked, and then
+// get blocked again.
+
+// The expected execution is
+// 1. Thread 1 blocks.
+// 2. Thread 2 blocks.
+// 3. Thread 3 unblocks both thread 1 and thread 2.
+// 4. Thread 1 writes u64::MAX.
+// 5. Thread 2's `write` deadlocked.
+fn main() {
+    // eventfd write will block when EFD_NONBLOCK flag is clear
+    // and the addition caused counter to exceed u64::MAX - 1.
+    let flags = libc::EFD_CLOEXEC;
+    let fd = unsafe { libc::eventfd(0, flags) };
+    // Write u64 - 1, so the all subsequent write will block.
+    let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes();
+    let res: i64 = unsafe {
+        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+    };
+    assert_eq!(res, 8);
+
+    let thread1 = thread::spawn(move || {
+        thread::park();
+        let sized_8_data = (u64::MAX - 1).to_ne_bytes();
+        let res: i64 = unsafe {
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+
+    let thread2 = thread::spawn(move || {
+        thread::park();
+        let sized_8_data = (u64::MAX - 1).to_ne_bytes();
+        // Write u64::MAX - 1, so the all subsequent write will block.
+        let res: i64 = unsafe {
+            // This `write` will initially blocked, then get unblocked by thread3, then get blocked again
+            // because the `write` in thread1 executes first.
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+            //~^ERROR: deadlocked
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+
+    let thread3 = thread::spawn(move || {
+        thread::park();
+        let mut buf: [u8; 8] = [0; 8];
+        // This will unblock both `write` in thread1 and thread2.
+        let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
+        assert_eq!(res, 8);
+        let counter = u64::from_ne_bytes(buf);
+        assert_eq!(counter, (u64::MAX - 1));
+    });
+
+    thread1.thread().unpark();
+    thread2.thread().unpark();
+    thread3.thread().unpark();
+
+    thread1.join().unwrap();
+    thread2.join().unwrap();
+    thread3.join().unwrap();
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr
new file mode 100644
index 00000000000..d9163a5748c
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr
@@ -0,0 +1,41 @@
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
+   |                                                                  ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> tests/fail-dep/libc/eventfd_block_write_twice.rs:LL:CC
+   |
+LL |     thread2.join().unwrap();
+   |     ^^^^^^^^^^^^^^
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+error: deadlock: the evaluated program deadlocked
+  --> tests/fail-dep/libc/eventfd_block_write_twice.rs:LL:CC
+   |
+LL |             libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+   |                                                                            ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside closure at tests/fail-dep/libc/eventfd_block_write_twice.rs:LL:CC
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs
new file mode 100644
index 00000000000..7f5ec477e19
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs
@@ -0,0 +1,96 @@
+//@compile-flags: -Zmiri-preemption-rate=0
+//~^ERROR: deadlocked
+//~^^ERROR: deadlocked
+//@only-target: linux
+//@error-in-other-file: deadlock
+
+use std::convert::TryInto;
+use std::thread;
+use std::thread::spawn;
+
+// Using `as` cast since `EPOLLET` wraps around
+const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _;
+
+#[track_caller]
+fn check_epoll_wait<const N: usize>(
+    epfd: i32,
+    expected_notifications: &[(u32, u64)],
+    timeout: i32,
+) {
+    let epoll_event = libc::epoll_event { events: 0, u64: 0 };
+    let mut array: [libc::epoll_event; N] = [epoll_event; N];
+    let maxsize = N;
+    let array_ptr = array.as_mut_ptr();
+    let res = unsafe { libc::epoll_wait(epfd, array_ptr, maxsize.try_into().unwrap(), timeout) };
+    if res < 0 {
+        panic!("epoll_wait failed: {}", std::io::Error::last_os_error());
+    }
+    assert_eq!(
+        res,
+        expected_notifications.len().try_into().unwrap(),
+        "got wrong number of notifications"
+    );
+    let slice = unsafe { std::slice::from_raw_parts(array_ptr, res.try_into().unwrap()) };
+    for (return_event, expected_event) in slice.iter().zip(expected_notifications.iter()) {
+        let event = return_event.events;
+        let data = return_event.u64;
+        assert_eq!(event, expected_event.0, "got wrong events");
+        assert_eq!(data, expected_event.1, "got wrong data");
+    }
+}
+
+// Test if only one thread is unblocked if multiple threads blocked on same epfd.
+// Expected execution:
+// 1. Thread 2 blocks.
+// 2. Thread 3 blocks.
+// 3. Thread 1 unblocks thread 3.
+// 4. Thread 2 deadlocks.
+fn main() {
+    // Create an epoll instance.
+    let epfd = unsafe { libc::epoll_create1(0) };
+    assert_ne!(epfd, -1);
+
+    // Create a socketpair instance.
+    let mut fds = [-1, -1];
+    let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
+    assert_eq!(res, 0);
+
+    // Register one side of the socketpair with epoll.
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
+    let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
+    assert_eq!(res, 0);
+
+    // epoll_wait to clear notification.
+    let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
+    let expected_value = fds[0] as u64;
+    check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0);
+
+    let thread1 = spawn(move || {
+        thread::park();
+        let data = "abcde".as_bytes().as_ptr();
+        let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
+        assert_eq!(res, 5);
+    });
+
+    let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
+    let expected_value = fds[0] as u64;
+    let thread2 = spawn(move || {
+        thread::park();
+        check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], -1);
+        //~^ERROR: deadlocked
+    });
+    let thread3 = spawn(move || {
+        thread::park();
+        check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], -1);
+    });
+
+    thread2.thread().unpark();
+    thread::yield_now();
+    thread3.thread().unpark();
+    thread::yield_now();
+    thread1.thread().unpark();
+
+    thread1.join().unwrap();
+    thread2.join().unwrap();
+    thread3.join().unwrap();
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr
new file mode 100644
index 00000000000..010dabc1364
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr
@@ -0,0 +1,41 @@
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
+   |                                                                  ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
+   |
+LL |     thread2.join().unwrap();
+   |     ^^^^^^^^^^^^^^
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+error: deadlock: the evaluated program deadlocked
+  --> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
+   |
+LL |         check_epoll_wait::<TAG>(epfd, &[(expected_event, expected_value)], -1);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside closure at tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs
deleted file mode 100644
index 0212a63bd0f..00000000000
--- a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//@only-target: linux
-fn main() {
-    // eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0.
-    // This will pass when blocking is implemented.
-    let flags = libc::EFD_CLOEXEC;
-    let fd = unsafe { libc::eventfd(0, flags) };
-    let mut buf: [u8; 8] = [0; 8];
-    let _res: i32 = unsafe {
-        libc::read(fd, buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap() //~ERROR: blocking is unsupported
-    };
-}
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr
deleted file mode 100644
index aff30c81ebd..00000000000
--- a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error: unsupported operation: eventfd: blocking is unsupported
-  --> tests/fail-dep/libc/libc_eventfd_read_block.rs:LL:CC
-   |
-LL |         libc::read(fd, buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap()
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ eventfd: blocking is unsupported
-   |
-   = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
-   = note: BACKTRACE:
-   = note: inside `main` at tests/fail-dep/libc/libc_eventfd_read_block.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs
deleted file mode 100644
index ed6ad466901..00000000000
--- a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-//@only-target: linux
-fn main() {
-    // eventfd write will block when EFD_NONBLOCK flag is clear
-    // and the addition caused counter to exceed u64::MAX - 1.
-    // This will pass when blocking is implemented.
-    let flags = libc::EFD_CLOEXEC;
-    let fd = unsafe { libc::eventfd(0, flags) };
-    // Write u64 - 1.
-    let mut sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes();
-    let res: i64 = unsafe {
-        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
-    };
-    assert_eq!(res, 8);
-
-    // Write 1.
-    sized_8_data = 1_u64.to_ne_bytes();
-    // Write 1 to the counter.
-    let _res: i64 = unsafe {
-        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap() //~ERROR: blocking is unsupported
-    };
-}
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr
deleted file mode 100644
index 2b606605794..00000000000
--- a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error: unsupported operation: eventfd: blocking is unsupported
-  --> tests/fail-dep/libc/libc_eventfd_write_block.rs:LL:CC
-   |
-LL |         libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ eventfd: blocking is unsupported
-   |
-   = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
-   = note: BACKTRACE:
-   = note: inside `main` at tests/fail-dep/libc/libc_eventfd_write_block.rs:LL:CC
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs
index a47bb671e32..a2e8a533c43 100644
--- a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs
+++ b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs
@@ -8,8 +8,8 @@
 
 struct Foo(u64);
 impl Foo {
-    #[rustfmt::skip] // rustfmt is wrong about which line contains an error
-    fn add(&mut self, n: u64) -> u64 { //~ ERROR: /reborrow through .* is forbidden/
+    fn add(&mut self, n: u64) -> u64 {
+        //~^ ERROR: /reborrow through .* is forbidden/
         self.0 + n
     }
 }
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
index c92d9c3fe70..dd9c0eb0b54 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
@@ -1,5 +1,5 @@
 //@only-target: linux
-// test_race depends on a deterministic schedule.
+// test_race, test_blocking_read and test_blocking_write depend on a deterministic schedule.
 //@compile-flags: -Zmiri-preemption-rate=0
 
 // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
@@ -11,6 +11,9 @@ fn main() {
     test_read_write();
     test_race();
     test_syscall();
+    test_blocking_read();
+    test_blocking_write();
+    test_two_threads_blocked_on_eventfd();
 }
 
 fn read_bytes<const N: usize>(fd: i32, buf: &mut [u8; N]) -> i32 {
@@ -118,3 +121,117 @@ fn test_syscall() {
     let fd = unsafe { libc::syscall(libc::SYS_eventfd2, initval, flags) };
     assert_ne!(fd, -1);
 }
+
+// This test will block on eventfd read then get unblocked by `write`.
+fn test_blocking_read() {
+    // eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0.
+    let flags = libc::EFD_CLOEXEC;
+    let fd = unsafe { libc::eventfd(0, flags) };
+    let thread1 = thread::spawn(move || {
+        let mut buf: [u8; 8] = [0; 8];
+        // This will block.
+        let res = read_bytes(fd, &mut buf);
+        // read returns number of bytes has been read, which is always 8.
+        assert_eq!(res, 8);
+        let counter = u64::from_ne_bytes(buf);
+        assert_eq!(counter, 1);
+    });
+    let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
+    // Pass control to thread1 so it can block on eventfd `read`.
+    thread::yield_now();
+    // Write 1 to the counter to unblock thread1.
+    let res = write_bytes(fd, sized_8_data);
+    assert_eq!(res, 8);
+    thread1.join().unwrap();
+}
+
+/// This test will block on eventfd `write` then get unblocked by `read`.
+fn test_blocking_write() {
+    // eventfd write will block when EFD_NONBLOCK flag is clear
+    // and the addition caused counter to exceed u64::MAX - 1.
+    let flags = libc::EFD_CLOEXEC;
+    let fd = unsafe { libc::eventfd(0, flags) };
+    // Write u64 - 1, so the all subsequent write will block.
+    let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes();
+    let res: i64 = unsafe {
+        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+    };
+    assert_eq!(res, 8);
+
+    let thread1 = thread::spawn(move || {
+        let sized_8_data = 1_u64.to_ne_bytes();
+        // Write 1 to the counter, this will block.
+        let res: i64 = unsafe {
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+    let mut buf: [u8; 8] = [0; 8];
+    // Pass control to thread1 so it can block on eventfd `write`.
+    thread::yield_now();
+    // This will unblock previously blocked eventfd read.
+    let res = read_bytes(fd, &mut buf);
+    // read returns number of bytes has been read, which is always 8.
+    assert_eq!(res, 8);
+    let counter = u64::from_ne_bytes(buf);
+    assert_eq!(counter, (u64::MAX - 1));
+    thread1.join().unwrap();
+}
+
+// Test two threads blocked on eventfd.
+// Expected behaviour:
+// 1. thread1 and thread2 both blocked on `write`.
+// 2. thread3 unblocks both thread1 and thread2
+// 3. The write in thread1 and thread2 return successfully.
+fn test_two_threads_blocked_on_eventfd() {
+    // eventfd write will block when EFD_NONBLOCK flag is clear
+    // and the addition caused counter to exceed u64::MAX - 1.
+    let flags = libc::EFD_CLOEXEC;
+    let fd = unsafe { libc::eventfd(0, flags) };
+    // Write u64 - 1, so the all subsequent write will block.
+    let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes();
+    let res: i64 = unsafe {
+        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+    };
+    assert_eq!(res, 8);
+
+    let thread1 = thread::spawn(move || {
+        thread::park();
+        let sized_8_data = 1_u64.to_ne_bytes();
+        let res: i64 = unsafe {
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+
+    let thread2 = thread::spawn(move || {
+        thread::park();
+        let sized_8_data = 1_u64.to_ne_bytes();
+        let res: i64 = unsafe {
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+
+    let thread3 = thread::spawn(move || {
+        thread::park();
+        let mut buf: [u8; 8] = [0; 8];
+        // This will unblock previously blocked eventfd read.
+        let res = read_bytes(fd, &mut buf);
+        // read returns number of bytes has been read, which is always 8.
+        assert_eq!(res, 8);
+        let counter = u64::from_ne_bytes(buf);
+        assert_eq!(counter, (u64::MAX - 1));
+    });
+
+    thread1.thread().unpark();
+    thread2.thread().unpark();
+    thread3.thread().unpark();
+
+    thread1.join().unwrap();
+    thread2.join().unwrap();
+    thread3.join().unwrap();
+}
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 66843ca584b..4de315e3589 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -959,10 +959,20 @@ pub fn libm() {
         unsafe { ldexp(a, b) }
     }
 
-    assert_approx_eq!(64f32.sqrt(), 8f32);
-    assert_approx_eq!(64f64.sqrt(), 8f64);
+    assert_eq!(64_f32.sqrt(), 8_f32);
+    assert_eq!(64_f64.sqrt(), 8_f64);
+    assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY);
+    assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY);
+    assert_eq!(0.0_f32.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal);
+    assert_eq!(0.0_f64.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal);
+    assert_eq!((-0.0_f32).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal);
+    assert_eq!((-0.0_f64).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal);
     assert!((-5.0_f32).sqrt().is_nan());
     assert!((-5.0_f64).sqrt().is_nan());
+    assert!(f32::NEG_INFINITY.sqrt().is_nan());
+    assert!(f64::NEG_INFINITY.sqrt().is_nan());
+    assert!(f32::NAN.sqrt().is_nan());
+    assert!(f64::NAN.sqrt().is_nan());
 
     assert_approx_eq!(25f32.powi(-2), 0.0016f32);
     assert_approx_eq!(23.2f64.powi(2), 538.24f64);
diff --git a/src/tools/miri/tests/pass/stacked-borrows/2phase.rs b/src/tools/miri/tests/pass/stacked-borrows/2phase.rs
index eb543d691e1..fb4ba605837 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/2phase.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/2phase.rs
@@ -1,3 +1,6 @@
+// FIXME: this miscompiles with optimizations, see <https://github.com/rust-lang/rust/issues/132898>.
+//@compile-flags: -Zmir-opt-level=0
+
 trait S: Sized {
     fn tpb(&mut self, _s: Self) {}
 }
@@ -75,6 +78,25 @@ fn with_interior_mutability() {
     });
 }
 
+// This one really shouldn't be accepted, but since we treat 2phase as raw, we do accept it.
+// Tree Borrows rejects it.
+fn aliasing_violation() {
+    struct Foo(u64);
+    impl Foo {
+        fn add(&mut self, n: u64) -> u64 {
+            self.0 + n
+        }
+    }
+
+    let mut f = Foo(0);
+    let alias = &mut f.0 as *mut u64;
+    let res = f.add(unsafe {
+        *alias = 42;
+        0
+    });
+    assert_eq!(res, 42);
+}
+
 fn main() {
     two_phase1();
     two_phase2();
@@ -84,4 +106,5 @@ fn main() {
     with_interior_mutability();
     two_phase_overlapping1();
     two_phase_overlapping2();
+    aliasing_violation();
 }
diff --git a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
index c75824d7f9b..4261f411eea 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
@@ -8,6 +8,7 @@ fn main() {
     mut_raw_then_mut_shr();
     mut_shr_then_mut_raw();
     mut_raw_mut();
+    mut_raw_mut2();
     partially_invalidate_mut();
     drop_after_sharing();
     // direct_mut_to_const_raw();
@@ -96,6 +97,18 @@ fn mut_raw_mut() {
     assert_eq!(x, 4);
 }
 
+// A variant of `mut_raw_mut` that does *not* get accepted by Tree Borrows.
+// It's kind of an accident that we accept it in Stacked Borrows...
+fn mut_raw_mut2() {
+    unsafe {
+        let mut root = 0;
+        let to = &mut root as *mut i32;
+        *to = 0;
+        let _val = root;
+        *to = 0;
+    }
+}
+
 fn partially_invalidate_mut() {
     let data = &mut (0u8, 0u8);
     let reborrow = &mut *data as *mut (u8, u8);
diff --git a/tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.stderr b/tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.stderr
index 2af75876d5b..04a41f0d9dd 100644
--- a/tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.stderr
+++ b/tests/ui/specialization/min_specialization/specialize_with_generalize_lifetimes.stderr
@@ -4,11 +4,7 @@ error[E0477]: the type `&'a i32` does not fulfill the required lifetime
 LL | impl<'a> Tr for &'a i32 {
    | ^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: type must satisfy the static lifetime as required by this binding
-  --> $DIR/specialize_with_generalize_lifetimes.rs:12:15
-   |
-LL | impl<T: Any + 'static> Tr for T {
-   |               ^^^^^^^
+   = note: type must satisfy the static lifetime
 
 error[E0477]: the type `Wrapper<'a>` does not fulfill the required lifetime
   --> $DIR/specialize_with_generalize_lifetimes.rs:31:1
@@ -16,11 +12,7 @@ error[E0477]: the type `Wrapper<'a>` does not fulfill the required lifetime
 LL | impl<'a> Tr for Wrapper<'a> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: type must satisfy the static lifetime as required by this binding
-  --> $DIR/specialize_with_generalize_lifetimes.rs:12:15
-   |
-LL | impl<T: Any + 'static> Tr for T {
-   |               ^^^^^^^
+   = note: type must satisfy the static lifetime
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/traits/const-traits/effects/dont-prefer-param-env-for-infer-self-ty.rs b/tests/ui/traits/const-traits/effects/dont-prefer-param-env-for-infer-self-ty.rs
new file mode 100644
index 00000000000..08dcd7d80b3
--- /dev/null
+++ b/tests/ui/traits/const-traits/effects/dont-prefer-param-env-for-infer-self-ty.rs
@@ -0,0 +1,16 @@
+//@ check-pass
+
+#![feature(const_trait_impl)]
+
+#[const_trait]
+trait Foo {}
+
+impl<T> const Foo for (T,) where T: ~const Foo {}
+
+const fn needs_const_foo(_: impl ~const Foo + Copy) {}
+
+const fn test<T: ~const Foo + Copy>(t: T) {
+    needs_const_foo((t,));
+}
+
+fn main() {}