about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-05-19 21:30:43 +0000
committerbors <bors@rust-lang.org>2024-05-19 21:30:43 +0000
commitd84b9037541f45dc2c52a41d723265af211c0497 (patch)
treefd4371c21acf0b2fb705673f6ccadb66056bf841
parent1d1283ed095ed605297b148e1284e1c53290ec91 (diff)
parent7a45322300f1f9263a706fa980de90b8a3375e7b (diff)
downloadrust-d84b9037541f45dc2c52a41d723265af211c0497.tar.gz
rust-d84b9037541f45dc2c52a41d723265af211c0497.zip
Auto merge of #125294 - matthiaskrgr:rollup-w42c829, r=matthiaskrgr
Rollup of 4 pull requests

Successful merges:

 - #124948 (chore: Remove repeated words (extension of #124924))
 - #124992 (Add example to IsTerminal::is_terminal)
 - #125279 (make `Debug` impl for `Term` simpler)
 - #125286 (Miri subtree update)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock4
-rw-r--r--compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs2
-rw-r--r--compiler/rustc_lint/src/non_local_def.rs2
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs12
-rw-r--r--compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs2
-rw-r--r--library/portable-simd/crates/core_simd/src/ops.rs2
-rw-r--r--library/std/src/io/stdio.rs34
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs2
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.rs2
-rw-r--r--src/tools/compiletest/src/common.rs4
-rw-r--r--src/tools/compiletest/src/lib.rs2
-rw-r--r--src/tools/jsondoclint/src/validator.rs2
-rw-r--r--src/tools/lld-wrapper/src/main.rs2
-rw-r--r--src/tools/miri/.gitignore1
-rw-r--r--src/tools/miri/cargo-miri/Cargo.lock4
-rw-r--r--src/tools/miri/cargo-miri/Cargo.toml2
-rw-r--r--src/tools/miri/cargo-miri/src/setup.rs87
-rw-r--r--src/tools/miri/cargo-miri/src/util.rs2
-rwxr-xr-xsrc/tools/miri/ci/ci.sh20
-rw-r--r--src/tools/miri/miri-script/src/args.rs136
-rw-r--r--src/tools/miri/miri-script/src/commands.rs99
-rw-r--r--src/tools/miri/miri-script/src/main.rs158
-rw-r--r--src/tools/miri/miri-script/src/util.rs34
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/alloc_bytes.rs109
-rw-r--r--src/tools/miri/src/bin/miri.rs5
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs2
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs6
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs2
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs2
-rw-r--r--src/tools/miri/src/concurrency/thread.rs2
-rw-r--r--src/tools/miri/src/concurrency/weak_memory.rs2
-rw-r--r--src/tools/miri/src/diagnostics.rs2
-rw-r--r--src/tools/miri/src/intrinsics/atomic.rs68
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs183
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs2
-rw-r--r--src/tools/miri/src/lib.rs3
-rw-r--r--src/tools/miri/src/machine.rs28
-rw-r--r--src/tools/miri/src/provenance_gc.rs2
-rw-r--r--src/tools/miri/src/shims/alloc.rs69
-rw-r--r--src/tools/miri/src/shims/extern_static.rs14
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs28
-rw-r--r--src/tools/miri/src/shims/mod.rs17
-rw-r--r--src/tools/miri/src/shims/panic.rs13
-rw-r--r--src/tools/miri/src/shims/unix/android/foreign_items.rs32
-rw-r--r--src/tools/miri/src/shims/unix/android/mod.rs1
-rw-r--r--src/tools/miri/src/shims/unix/fd.rs37
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs75
-rw-r--r--src/tools/miri/src/shims/unix/freebsd/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs5
-rw-r--r--src/tools/miri/src/shims/unix/linux/eventfd.rs5
-rw-r--r--src/tools/miri/src/shims/unix/linux/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/unix/macos/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/unix/mem.rs4
-rw-r--r--src/tools/miri/src/shims/unix/mod.rs1
-rw-r--r--src/tools/miri/src/shims/unix/solarish/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/unix/sync.rs2
-rw-r--r--src/tools/miri/src/shims/wasi/foreign_items.rs40
-rw-r--r--src/tools/miri/src/shims/wasi/mod.rs1
-rw-r--r--src/tools/miri/src/shims/windows/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/x86/aesni.rs2
-rw-r--r--src/tools/miri/src/shims/x86/avx.rs2
-rw-r--r--src/tools/miri/src/shims/x86/avx2.rs4
-rw-r--r--src/tools/miri/src/shims/x86/mod.rs12
-rw-r--r--src/tools/miri/src/shims/x86/sse.rs2
-rw-r--r--src/tools/miri/src/shims/x86/sse2.rs2
-rw-r--r--src/tools/miri/src/shims/x86/sse3.rs2
-rw-r--r--src/tools/miri/src/shims/x86/sse41.rs2
-rw-r--r--src/tools/miri/src/shims/x86/ssse3.rs2
-rw-r--r--src/tools/miri/test_dependencies/Cargo.toml2
-rw-r--r--src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs15
-rw-r--r--src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr15
-rw-r--r--src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs14
-rw-r--r--src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr25
-rw-r--r--src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.rs10
-rw-r--r--src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr15
-rw-r--r--src/tools/miri/tests/fail/environ-gets-deallocated.rs9
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-mem.rs137
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-misc.rs20
-rw-r--r--src/tools/miri/tests/pass/empty_main.rs3
-rw-r--r--src/tools/miri/tests/pass/float.rs67
-rw-r--r--src/tools/miri/tests/pass/float_fast_math.rs34
-rw-r--r--src/tools/miri/tests/pass/integer-ops.rs61
-rw-r--r--src/tools/miri/tests/pass/integers.rs58
-rw-r--r--src/tools/miri/tests/pass/panic/unwind_dwarf.rs96
-rw-r--r--src/tools/miri/tests/pass/shims/env/home.rs8
-rw-r--r--tests/ui/associated-types/associated-types-eq-2.rs2
-rw-r--r--tests/ui/coherence/associated-type2.rs2
-rw-r--r--tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs2
-rw-r--r--tests/ui/parser/label-is-actually-char.rs2
-rw-r--r--tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs2
-rw-r--r--tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs2
93 files changed, 1372 insertions, 650 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 186892af21c..a9283d03cb3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3426,9 +3426,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-build-sysroot"
-version = "0.4.7"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab1dbbd1bdf65fdac44c885f6cca147ba179108ce284b60a08ccc04b1f1dbac0"
+checksum = "fa3ca63cc537c1cb69e4c2c0afc5fda2ccd36ac84c97d5a4ae05e69b1c834afb"
 dependencies = [
  "anyhow",
  "rustc_version",
diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 15050c87b39..7802825a1b8 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -652,7 +652,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
                     }
 
                     // FIXME: We make sure that this is a normal top-level binding,
-                    // but we could suggest `todo!()` for all uninitialized bindings in the pattern pattern
+                    // but we could suggest `todo!()` for all uninitialized bindings in the pattern
                     if let hir::StmtKind::Let(hir::LetStmt { span, ty, init: None, pat, .. }) =
                         &ex.kind
                         && let hir::PatKind::Binding(..) = pat.kind
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index 5a9eab1ffea..fb7d3f40093 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -3112,7 +3112,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
             let true_errors = ocx.select_where_possible();
 
-            // Do a leak check -- we can't really report report a useful error here,
+            // Do a leak check -- we can't really report a useful error here,
             // but it at least avoids an ICE when the error has to do with higher-ranked
             // lifetimes.
             self.leak_check(outer_universe, Some(snapshot))?;
diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs
index 885c0bb3a89..b3e93748a16 100644
--- a/compiler/rustc_lint/src/non_local_def.rs
+++ b/compiler/rustc_lint/src/non_local_def.rs
@@ -38,7 +38,7 @@ declare_lint! {
     ///
     /// Creating non-local definitions go against expectation and can create discrepancies
     /// in tooling. It should be avoided. It may become deny-by-default in edition 2024
-    /// and higher, see see the tracking issue <https://github.com/rust-lang/rust/issues/120363>.
+    /// and higher, see the tracking issue <https://github.com/rust-lang/rust/issues/120363>.
     ///
     /// An `impl` definition is non-local if it is nested inside an item and neither
     /// the type nor the trait are at the same nesting level as the `impl` block.
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index ce63fc20ddb..44f09b8ba21 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -536,14 +536,10 @@ unsafe impl<'tcx> Sync for Term<'tcx> where &'tcx (Ty<'tcx>, Const<'tcx>): Sync
 
 impl Debug for Term<'_> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let data = if let Some(ty) = self.ty() {
-            format!("Term::Ty({ty:?})")
-        } else if let Some(ct) = self.ct() {
-            format!("Term::Ct({ct:?})")
-        } else {
-            unreachable!()
-        };
-        f.write_str(&data)
+        match self.unpack() {
+            TermKind::Ty(ty) => write!(f, "Term::Ty({ty:?})"),
+            TermKind::Const(ct) => write!(f, "Term::Const({ct:?})"),
+        }
     }
 }
 
diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
index 21433cfdb61..62eb07e8287 100644
--- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
+++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs
@@ -269,7 +269,7 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc
 /// if a function is member of the group derived from this type id. Therefore, in the first call to
 /// typeid_for_fnabi (when type ids are attached to functions and methods), it can only include at
 /// most as much information that would be available in the second call (i.e., during code
-/// generation at call sites); otherwise, the type ids would not not match.
+/// generation at call sites); otherwise, the type ids would not match.
 ///
 /// For this, it:
 ///
diff --git a/library/portable-simd/crates/core_simd/src/ops.rs b/library/portable-simd/crates/core_simd/src/ops.rs
index d8e10eeaa1a..dd7303a97b1 100644
--- a/library/portable-simd/crates/core_simd/src/ops.rs
+++ b/library/portable-simd/crates/core_simd/src/ops.rs
@@ -122,7 +122,7 @@ macro_rules! for_base_types {
                     #[inline]
                     #[must_use = "operator returns a new vector without mutating the inputs"]
                     // TODO: only useful for int Div::div, but we hope that this
-                    // will essentially always always get inlined anyway.
+                    // will essentially always get inlined anyway.
                     #[track_caller]
                     fn $call(self, rhs: Self) -> Self::Output {
                         $macro_impl!(self, rhs, $inner, $scalar)
diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs
index 07fa9259e0b..c8968b74b12 100644
--- a/library/std/src/io/stdio.rs
+++ b/library/std/src/io/stdio.rs
@@ -1161,7 +1161,41 @@ pub trait IsTerminal: crate::sealed::Sealed {
     /// starting with `msys-` or `cygwin-` and ending in `-pty` will be considered terminals.
     /// Note that this [may change in the future][changes].
     ///
+    /// # Examples
+    ///
+    /// An example of a type for which `IsTerminal` is implemented is [`Stdin`]:
+    ///
+    /// ```no_run
+    /// use std::io::{self, IsTerminal, Write};
+    ///
+    /// fn main() -> io::Result<()> {
+    ///     let stdin = io::stdin();
+    ///
+    ///     // Indicate that the user is prompted for input, if this is a terminal.
+    ///     if stdin.is_terminal() {
+    ///         print!("> ");
+    ///         io::stdout().flush()?;
+    ///     }
+    ///
+    ///     let mut name = String::new();
+    ///     let _ = stdin.read_line(&mut name)?;
+    ///
+    ///     println!("Hello {}", name.trim_end());
+    ///
+    ///     Ok(())
+    /// }
+    /// ```
+    ///
+    /// The example can be run in two ways:
+    ///
+    /// - If you run this example by piping some text to it, e.g. `echo "foo" | path/to/executable`
+    ///   it will print: `Hello foo`.
+    /// - If you instead run the example interactively by running the executable directly, it will
+    ///   panic with the message "Expected input to be piped to the process".
+    ///
+    ///
     /// [changes]: io#platform-specific-behavior
+    /// [`Stdin`]: crate::io::Stdin
     #[stable(feature = "is_terminal", since = "1.70.0")]
     fn is_terminal(&self) -> bool;
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index 1c695655536..f26f164fa54 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -127,7 +127,7 @@ pub(super) fn check<'tcx>(
     }
 }
 
-/// checks for for collecting into a (generic) method or function argument
+/// checks for collecting into a (generic) method or function argument
 /// taking an `IntoIterator`
 fn check_collect_into_intoiterator<'tcx>(
     cx: &LateContext<'tcx>,
diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs
index fcd716f4144..5d6e488972c 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.rs
+++ b/src/tools/clippy/tests/ui/ptr_arg.rs
@@ -282,7 +282,7 @@ mod issue_9218 {
         todo!()
     }
 
-    // These two's return types don't use use 'a so it's not okay
+    // These two's return types don't use 'a so it's not okay
     fn cow_bad_ret_ty_1<'a>(input: &'a Cow<'a, str>) -> &'static str {
         //~^ ERROR: using a reference to `Cow` is not recommended
         todo!()
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index afbcc3e92bc..7ff45edd4b2 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -250,7 +250,7 @@ pub struct Config {
     /// Only run tests that match these filters
     pub filters: Vec<String>,
 
-    /// Skip tests tests matching these substrings. Corresponds to
+    /// Skip tests matching these substrings. Corresponds to
     /// `test::TestOpts::skip`. `filter_exact` does not apply to these flags.
     pub skip: Vec<String>,
 
@@ -381,7 +381,7 @@ pub struct Config {
     /// Whether to rerun tests even if the inputs are unchanged.
     pub force_rerun: bool,
 
-    /// Only rerun the tests that result has been modified accoring to Git status
+    /// Only rerun the tests that result has been modified according to Git status
     pub only_modified: bool,
 
     pub target_cfgs: OnceLock<TargetCfgs>,
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 2e45caec46c..99bde107f3a 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -950,7 +950,7 @@ fn is_android_gdb_target(target: &str) -> bool {
     )
 }
 
-/// Returns `true` if the given target is a MSVC target for the purpouses of CDB testing.
+/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing.
 fn is_pc_windows_msvc_target(target: &str) -> bool {
     target.ends_with("-pc-windows-msvc")
 }
diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs
index 9e08f7e5f9b..904c2b614f3 100644
--- a/src/tools/jsondoclint/src/validator.rs
+++ b/src/tools/jsondoclint/src/validator.rs
@@ -21,7 +21,7 @@ const LOCAL_CRATE_ID: u32 = 0;
 ///              it is well formed. This involves calling `check_*` functions on
 ///              fields of that item, and `add_*` functions on [`Id`]s.
 /// - `add_*`: These add an [`Id`] to the worklist, after validating it to check if
-///            the `Id` is a kind expected in this suituation.
+///            the `Id` is a kind expected in this situation.
 #[derive(Debug)]
 pub struct Validator<'a> {
     pub(crate) errs: Vec<Error>,
diff --git a/src/tools/lld-wrapper/src/main.rs b/src/tools/lld-wrapper/src/main.rs
index da94e686f38..79e279a8614 100644
--- a/src/tools/lld-wrapper/src/main.rs
+++ b/src/tools/lld-wrapper/src/main.rs
@@ -1,6 +1,6 @@
 //! Script to invoke the bundled rust-lld with the correct flavor.
 //!
-//! lld supports multiple command line interfaces. If `-flavor <flavor>` are passed as the first
+//! `lld` supports multiple command line interfaces. If `-flavor <flavor>` are passed as the first
 //! two arguments the `<flavor>` command line interface is used to process the remaining arguments.
 //! If no `-flavor` argument is present the flavor is determined by the executable name.
 //!
diff --git a/src/tools/miri/.gitignore b/src/tools/miri/.gitignore
index 97e006e8b1b..03c5591b787 100644
--- a/src/tools/miri/.gitignore
+++ b/src/tools/miri/.gitignore
@@ -5,6 +5,7 @@ tex/*/out
 *.out
 *.rs.bk
 .vscode
+.helix
 *.mm_profdata
 perf.data
 perf.data.old
diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock
index b8ead460249..8bd8f103053 100644
--- a/src/tools/miri/cargo-miri/Cargo.lock
+++ b/src/tools/miri/cargo-miri/Cargo.lock
@@ -178,9 +178,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-build-sysroot"
-version = "0.4.7"
+version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab1dbbd1bdf65fdac44c885f6cca147ba179108ce284b60a08ccc04b1f1dbac0"
+checksum = "fa3ca63cc537c1cb69e4c2c0afc5fda2ccd36ac84c97d5a4ae05e69b1c834afb"
 dependencies = [
  "anyhow",
  "rustc_version",
diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml
index b16068b6d19..6acdbc46f64 100644
--- a/src/tools/miri/cargo-miri/Cargo.toml
+++ b/src/tools/miri/cargo-miri/Cargo.toml
@@ -18,7 +18,7 @@ directories = "5"
 rustc_version = "0.4"
 serde_json = "1.0.40"
 cargo_metadata = "0.18.0"
-rustc-build-sysroot = "0.4.6"
+rustc-build-sysroot = "0.5.2"
 
 # Enable some feature flags that dev-dependencies need but dependencies
 # do not.  This makes `./miri install` after `./miri build` faster.
diff --git a/src/tools/miri/cargo-miri/src/setup.rs b/src/tools/miri/cargo-miri/src/setup.rs
index 9a58e6fa018..fe67aad465c 100644
--- a/src/tools/miri/cargo-miri/src/setup.rs
+++ b/src/tools/miri/cargo-miri/src/setup.rs
@@ -2,11 +2,10 @@
 
 use std::env;
 use std::ffi::OsStr;
-use std::fmt::Write;
 use std::path::PathBuf;
 use std::process::{self, Command};
 
-use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig};
+use rustc_build_sysroot::{BuildMode, SysrootBuilder, SysrootConfig, SysrootStatus};
 use rustc_version::VersionMeta;
 
 use crate::util::*;
@@ -24,6 +23,7 @@ pub fn setup(
     let only_setup = matches!(subcommand, MiriCommand::Setup);
     let ask_user = !only_setup;
     let print_sysroot = only_setup && has_arg_flag("--print-sysroot"); // whether we just print the sysroot path
+    let show_setup = only_setup && !print_sysroot;
     if !only_setup {
         if let Some(sysroot) = std::env::var_os("MIRI_SYSROOT") {
             // Skip setup step if MIRI_SYSROOT is explicitly set, *unless* we are `cargo miri setup`.
@@ -115,18 +115,16 @@ pub fn setup(
         // `config.toml`.
         command.env("RUSTC_WRAPPER", "");
 
-        if only_setup && !print_sysroot {
+        if show_setup {
             // Forward output. Even make it verbose, if requested.
+            command.stdout(process::Stdio::inherit());
+            command.stderr(process::Stdio::inherit());
             for _ in 0..verbose {
                 command.arg("-v");
             }
             if quiet {
                 command.arg("--quiet");
             }
-        } else {
-            // Suppress output.
-            command.stdout(process::Stdio::null());
-            command.stderr(process::Stdio::null());
         }
 
         command
@@ -137,49 +135,52 @@ pub fn setup(
     // not apply `RUSTFLAGS` to the sysroot either.
     let rustflags = &["-Cdebug-assertions=off", "-Coverflow-checks=on"];
 
-    // Do the build.
-    if print_sysroot || quiet {
-        // Be silent.
-    } else {
-        let mut msg = String::new();
-        write!(msg, "Preparing a sysroot for Miri (target: {target})").unwrap();
-        if verbose > 0 {
-            write!(msg, " in {}", sysroot_dir.display()).unwrap();
-        }
-        write!(msg, "...").unwrap();
-        if only_setup {
-            // We want to be explicit.
-            eprintln!("{msg}");
-        } else {
-            // We want to be quiet, but still let the user know that something is happening.
-            eprint!("{msg} ");
+    let mut after_build_output = String::new(); // what should be printed when the build is done.
+    let notify = || {
+        if !quiet {
+            eprint!("Preparing a sysroot for Miri (target: {target})");
+            if verbose > 0 {
+                eprint!(" in {}", sysroot_dir.display());
+            }
+            if show_setup {
+                // Cargo will print things, so we need to finish this line.
+                eprintln!("...");
+                after_build_output = format!(
+                    "A sysroot for Miri is now available in `{}`.\n",
+                    sysroot_dir.display()
+                );
+            } else {
+                // Keep all output on a single line.
+                eprint!("... ");
+                after_build_output = format!("done\n");
+            }
         }
-    }
-    SysrootBuilder::new(&sysroot_dir, target)
+    };
+
+    // Do the build.
+    let status = SysrootBuilder::new(&sysroot_dir, target)
         .build_mode(BuildMode::Check)
         .rustc_version(rustc_version.clone())
         .sysroot_config(sysroot_config)
         .rustflags(rustflags)
         .cargo(cargo_cmd)
-        .build_from_source(&rust_src)
-        .unwrap_or_else(|err| {
-            if print_sysroot {
-                show_error!("failed to build sysroot")
-            } else if only_setup {
-                show_error!("failed to build sysroot: {err:?}")
-            } else {
-                show_error!(
-                    "failed to build sysroot; run `cargo miri setup` to see the error details"
-                )
-            }
-        });
-    if print_sysroot || quiet {
-        // Be silent.
-    } else if only_setup {
-        eprintln!("A sysroot for Miri is now available in `{}`.", sysroot_dir.display());
-    } else {
-        eprintln!("done");
+        .when_build_required(notify)
+        .build_from_source(&rust_src);
+    match status {
+        Ok(SysrootStatus::AlreadyCached) =>
+            if !quiet && show_setup {
+                eprintln!(
+                    "A sysroot for Miri is already available in `{}`.",
+                    sysroot_dir.display()
+                );
+            },
+        Ok(SysrootStatus::SysrootBuilt) => {
+            // Print what `notify` prepared.
+            eprint!("{after_build_output}");
+        }
+        Err(err) => show_error!("failed to build sysroot: {err:?}"),
     }
+
     if print_sysroot {
         // Print just the sysroot and nothing else to stdout; this way we do not need any escaping.
         println!("{}", sysroot_dir.display());
diff --git a/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs
index d99957d9c22..28a824e54f6 100644
--- a/src/tools/miri/cargo-miri/src/util.rs
+++ b/src/tools/miri/cargo-miri/src/util.rs
@@ -269,7 +269,7 @@ pub fn get_target_dir(meta: &Metadata) -> PathBuf {
     output
 }
 
-/// Determines where the sysroot of this exeuction is
+/// Determines where the sysroot of this execution is
 ///
 /// Either in a user-specified spot by an envar, or in a default cache location.
 pub fn get_sysroot_dir() -> PathBuf {
diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh
index f5fbb05d896..4e92169b3d0 100755
--- a/src/tools/miri/ci/ci.sh
+++ b/src/tools/miri/ci/ci.sh
@@ -144,16 +144,16 @@ case $HOST_TARGET in
     TEST_TARGET=arm-unknown-linux-gnueabi run_tests
     TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice
     # Partially supported targets (tier 2)
-    VERY_BASIC="integer vec string btreemap" # common things we test on all of them (if they have std), requires no target-specific shims
-    BASIC="$VERY_BASIC hello hashmap alloc align" # ensures we have the shims for stdout and basic data structures
-    TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus
-    TEST_TARGET=i686-unknown-freebsd   run_tests_minimal $BASIC panic/panic concurrency/simple atomic threadname libc-mem libc-misc libc-random libc-time fs env num_cpus
-    TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random
-    TEST_TARGET=x86_64-pc-solaris run_tests_minimal $VERY_BASIC hello panic/panic concurrency/simple pthread-sync libc-mem libc-misc libc-random
-    TEST_TARGET=aarch64-linux-android  run_tests_minimal $VERY_BASIC hello panic/panic
-    TEST_TARGET=wasm32-wasi run_tests_minimal $VERY_BASIC wasm
-    TEST_TARGET=wasm32-unknown-unknown run_tests_minimal $VERY_BASIC wasm
-    TEST_TARGET=thumbv7em-none-eabihf run_tests_minimal no_std
+    BASIC="empty_main integer vec string btreemap hello hashmap heap_alloc align" # ensures we have the basics: stdout/stderr, system allocator, randomness (for HashMap initialization)
+    UNIX="panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there
+    TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs
+    TEST_TARGET=i686-unknown-freebsd   run_tests_minimal $BASIC $UNIX threadname libc-time fs
+    TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX pthread-sync
+    TEST_TARGET=x86_64-pc-solaris      run_tests_minimal $BASIC $UNIX pthread-sync
+    TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX
+    TEST_TARGET=wasm32-wasip2          run_tests_minimal empty_main wasm heap_alloc libc-mem
+    TEST_TARGET=wasm32-unknown-unknown run_tests_minimal empty_main wasm
+    TEST_TARGET=thumbv7em-none-eabihf  run_tests_minimal no_std
     # Custom target JSON file
     TEST_TARGET=tests/avr.json MIRI_NO_STD=1 run_tests_minimal no_std
     ;;
diff --git a/src/tools/miri/miri-script/src/args.rs b/src/tools/miri/miri-script/src/args.rs
new file mode 100644
index 00000000000..16a21757b35
--- /dev/null
+++ b/src/tools/miri/miri-script/src/args.rs
@@ -0,0 +1,136 @@
+use std::env;
+use std::iter;
+
+use anyhow::{bail, Result};
+
+pub struct Args {
+    args: iter::Peekable<env::Args>,
+    /// Set to `true` once we saw a `--`.
+    terminated: bool,
+}
+
+impl Args {
+    pub fn new() -> Self {
+        let mut args = Args { args: env::args().peekable(), terminated: false };
+        args.args.next().unwrap(); // skip program name
+        args
+    }
+
+    /// Get the next argument without any interpretation.
+    pub fn next_raw(&mut self) -> Option<String> {
+        self.args.next()
+    }
+
+    /// Consume a `-$f` flag if present.
+    pub fn get_short_flag(&mut self, flag: char) -> Result<bool> {
+        if self.terminated {
+            return Ok(false);
+        }
+        if let Some(next) = self.args.peek() {
+            if let Some(next) = next.strip_prefix("-") {
+                if let Some(next) = next.strip_prefix(flag) {
+                    if next.is_empty() {
+                        self.args.next().unwrap(); // consume this argument
+                        return Ok(true);
+                    } else {
+                        bail!("`-{flag}` followed by value");
+                    }
+                }
+            }
+        }
+        Ok(false)
+    }
+
+    /// Consume a `--$name` flag if present.
+    pub fn get_long_flag(&mut self, name: &str) -> Result<bool> {
+        if self.terminated {
+            return Ok(false);
+        }
+        if let Some(next) = self.args.peek() {
+            if let Some(next) = next.strip_prefix("--") {
+                if next == name {
+                    self.args.next().unwrap(); // consume this argument
+                    return Ok(true);
+                }
+            }
+        }
+        Ok(false)
+    }
+
+    /// Consume a `--$name val` or `--$name=val` option if present.
+    pub fn get_long_opt(&mut self, name: &str) -> Result<Option<String>> {
+        assert!(!name.is_empty());
+        if self.terminated {
+            return Ok(None);
+        }
+        let Some(next) = self.args.peek() else { return Ok(None) };
+        let Some(next) = next.strip_prefix("--") else { return Ok(None) };
+        let Some(next) = next.strip_prefix(name) else { return Ok(None) };
+        // Starts with `--flag`.
+        Ok(if let Some(val) = next.strip_prefix("=") {
+            // `--flag=val` form
+            let val = val.into();
+            self.args.next().unwrap(); // consume this argument
+            Some(val)
+        } else if next.is_empty() {
+            // `--flag val` form
+            self.args.next().unwrap(); // consume this argument
+            let Some(val) = self.args.next() else { bail!("`--{name}` not followed by value") };
+            Some(val)
+        } else {
+            // Some unrelated flag, like `--flag-more` or so.
+            None
+        })
+    }
+
+    /// Consume a `--$name=val` or `--$name` option if present; the latter
+    /// produces a default value. (`--$name val` is *not* accepted for this form
+    /// of argument, it understands `val` already as the next argument!)
+    pub fn get_long_opt_with_default(
+        &mut self,
+        name: &str,
+        default: &str,
+    ) -> Result<Option<String>> {
+        assert!(!name.is_empty());
+        if self.terminated {
+            return Ok(None);
+        }
+        let Some(next) = self.args.peek() else { return Ok(None) };
+        let Some(next) = next.strip_prefix("--") else { return Ok(None) };
+        let Some(next) = next.strip_prefix(name) else { return Ok(None) };
+        // Starts with `--flag`.
+        Ok(if let Some(val) = next.strip_prefix("=") {
+            // `--flag=val` form
+            let val = val.into();
+            self.args.next().unwrap(); // consume this argument
+            Some(val)
+        } else if next.is_empty() {
+            // `--flag` form
+            self.args.next().unwrap(); // consume this argument
+            Some(default.into())
+        } else {
+            // Some unrelated flag, like `--flag-more` or so.
+            None
+        })
+    }
+
+    /// Returns the next free argument or uninterpreted flag, or `None` if there are no more
+    /// arguments left. `--` is returned as well, but it is interpreted in the sense that no more
+    /// flags will be parsed after this.
+    pub fn get_other(&mut self) -> Option<String> {
+        if self.terminated {
+            return self.args.next();
+        }
+        let next = self.args.next()?;
+        if next == "--" {
+            self.terminated = true; // don't parse any more flags
+            // This is where our parser is special, we do yield the `--`.
+        }
+        Some(next)
+    }
+
+    /// Return the rest of the aguments entirely unparsed.
+    pub fn remainder(self) -> Vec<String> {
+        self.args.collect()
+    }
+}
diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs
index 8e2b07ad805..be0fdbc177a 100644
--- a/src/tools/miri/miri-script/src/commands.rs
+++ b/src/tools/miri/miri-script/src/commands.rs
@@ -25,7 +25,11 @@ impl MiriEnv {
     /// Returns the location of the sysroot.
     ///
     /// If the target is None the sysroot will be built for the host machine.
-    fn build_miri_sysroot(&mut self, quiet: bool, target: Option<&OsStr>) -> Result<PathBuf> {
+    fn build_miri_sysroot(
+        &mut self,
+        quiet: bool,
+        target: Option<impl AsRef<OsStr>>,
+    ) -> Result<PathBuf> {
         if let Some(miri_sysroot) = self.sh.var_os("MIRI_SYSROOT") {
             // Sysroot already set, use that.
             return Ok(miri_sysroot.into());
@@ -37,33 +41,27 @@ impl MiriEnv {
         self.build(path!(self.miri_dir / "Cargo.toml"), &[], quiet)?;
         self.build(&manifest_path, &[], quiet)?;
 
-        let target_flag =
-            if let Some(target) = target { vec![OsStr::new("--target"), target] } else { vec![] };
+        let target_flag = if let Some(target) = &target {
+            vec![OsStr::new("--target"), target.as_ref()]
+        } else {
+            vec![]
+        };
         let target_flag = &target_flag;
 
         if !quiet {
-            if let Some(target) = target {
-                eprintln!("$ (building Miri sysroot for {})", target.to_string_lossy());
-            } else {
-                eprintln!("$ (building Miri sysroot)");
+            eprint!("$ cargo miri setup");
+            if let Some(target) = &target {
+                eprint!(" --target {target}", target = target.as_ref().to_string_lossy());
             }
+            eprintln!();
         }
 
-        let output = cmd!(self.sh,
+        let mut cmd = cmd!(self.sh,
             "cargo +{toolchain} --quiet run {cargo_extra_flags...} --manifest-path {manifest_path} --
              miri setup --print-sysroot {target_flag...}"
-        ).read();
-        let Ok(output) = output else {
-            // Run it again (without `--print-sysroot` or `--quiet`) so the user can see the error.
-            cmd!(
-                self.sh,
-                "cargo +{toolchain} run {cargo_extra_flags...} --manifest-path {manifest_path} --
-                miri setup {target_flag...}"
-            )
-            .run()
-            .with_context(|| "`cargo miri setup` failed")?;
-            panic!("`cargo miri setup` didn't fail again the 2nd time?");
-        };
+        );
+        cmd.set_quiet(quiet);
+        let output = cmd.read()?;
         self.sh.set_var("MIRI_SYSROOT", &output);
         Ok(output.into())
     }
@@ -166,8 +164,8 @@ impl Command {
             Command::Build { flags } => Self::build(flags),
             Command::Check { flags } => Self::check(flags),
             Command::Test { bless, flags, target } => Self::test(bless, flags, target),
-            Command::Run { dep, verbose, many_seeds, flags } =>
-                Self::run(dep, verbose, many_seeds, flags),
+            Command::Run { dep, verbose, many_seeds, target, edition, flags } =>
+                Self::run(dep, verbose, many_seeds, target, edition, flags),
             Command::Fmt { flags } => Self::fmt(flags),
             Command::Clippy { flags } => Self::clippy(flags),
             Command::Cargo { flags } => Self::cargo(flags),
@@ -178,7 +176,7 @@ impl Command {
         }
     }
 
-    fn toolchain(flags: Vec<OsString>) -> Result<()> {
+    fn toolchain(flags: Vec<String>) -> Result<()> {
         // Make sure rustup-toolchain-install-master is installed.
         which::which("rustup-toolchain-install-master")
             .context("Please install rustup-toolchain-install-master by running 'cargo install rustup-toolchain-install-master'")?;
@@ -255,7 +253,7 @@ impl Command {
         cmd!(sh, "git fetch http://localhost:{JOSH_PORT}/rust-lang/rust.git@{commit}{JOSH_FILTER}.git")
             .run()
             .map_err(|e| {
-                // Try to un-do the previous `git commit`, to leave the repo in the state we found it it.
+                // Try to un-do the previous `git commit`, to leave the repo in the state we found it.
                 cmd!(sh, "git reset --hard HEAD^")
                     .run()
                     .expect("FAILED to clean up again after failed `git fetch`, sorry for that");
@@ -373,7 +371,7 @@ impl Command {
         Ok(())
     }
 
-    fn bench(target: Option<OsString>, benches: Vec<OsString>) -> Result<()> {
+    fn bench(target: Option<String>, benches: Vec<String>) -> Result<()> {
         // The hyperfine to use
         let hyperfine = env::var("HYPERFINE");
         let hyperfine = hyperfine.as_deref().unwrap_or("hyperfine -w 1 -m 5 --shell=none");
@@ -387,14 +385,14 @@ impl Command {
         let sh = Shell::new()?;
         sh.change_dir(miri_dir()?);
         let benches_dir = "bench-cargo-miri";
-        let benches = if benches.is_empty() {
+        let benches: Vec<OsString> = if benches.is_empty() {
             sh.read_dir(benches_dir)?
                 .into_iter()
                 .filter(|path| path.is_dir())
                 .map(Into::into)
                 .collect()
         } else {
-            benches.to_owned()
+            benches.into_iter().map(Into::into).collect()
         };
         let target_flag = if let Some(target) = target {
             let mut flag = OsString::from("--target=");
@@ -418,28 +416,28 @@ impl Command {
         Ok(())
     }
 
-    fn install(flags: Vec<OsString>) -> Result<()> {
+    fn install(flags: Vec<String>) -> Result<()> {
         let e = MiriEnv::new()?;
         e.install_to_sysroot(e.miri_dir.clone(), &flags)?;
         e.install_to_sysroot(path!(e.miri_dir / "cargo-miri"), &flags)?;
         Ok(())
     }
 
-    fn build(flags: Vec<OsString>) -> Result<()> {
+    fn build(flags: Vec<String>) -> Result<()> {
         let e = MiriEnv::new()?;
         e.build(path!(e.miri_dir / "Cargo.toml"), &flags, /* quiet */ false)?;
         e.build(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags, /* quiet */ false)?;
         Ok(())
     }
 
-    fn check(flags: Vec<OsString>) -> Result<()> {
+    fn check(flags: Vec<String>) -> Result<()> {
         let e = MiriEnv::new()?;
         e.check(path!(e.miri_dir / "Cargo.toml"), &flags)?;
         e.check(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
         Ok(())
     }
 
-    fn clippy(flags: Vec<OsString>) -> Result<()> {
+    fn clippy(flags: Vec<String>) -> Result<()> {
         let e = MiriEnv::new()?;
         e.clippy(path!(e.miri_dir / "Cargo.toml"), &flags)?;
         e.clippy(path!(e.miri_dir / "cargo-miri" / "Cargo.toml"), &flags)?;
@@ -447,7 +445,7 @@ impl Command {
         Ok(())
     }
 
-    fn cargo(flags: Vec<OsString>) -> Result<()> {
+    fn cargo(flags: Vec<String>) -> Result<()> {
         let e = MiriEnv::new()?;
         let toolchain = &e.toolchain;
         // We carefully kept the working dir intact, so this will run cargo *on the workspace in the
@@ -456,7 +454,7 @@ impl Command {
         Ok(())
     }
 
-    fn test(bless: bool, mut flags: Vec<OsString>, target: Option<OsString>) -> Result<()> {
+    fn test(bless: bool, mut flags: Vec<String>, target: Option<String>) -> Result<()> {
         let mut e = MiriEnv::new()?;
 
         // Prepare a sysroot.
@@ -484,21 +482,30 @@ impl Command {
         dep: bool,
         verbose: bool,
         many_seeds: Option<Range<u32>>,
-        mut flags: Vec<OsString>,
+        target: Option<String>,
+        edition: Option<String>,
+        flags: Vec<String>,
     ) -> Result<()> {
         let mut e = MiriEnv::new()?;
-        let target = arg_flag_value(&flags, "--target");
-
-        // Scan for "--edition", set one ourselves if that flag is not present.
-        let have_edition = arg_flag_value(&flags, "--edition").is_some();
-        if !have_edition {
-            flags.push("--edition=2021".into()); // keep in sync with `tests/ui.rs`.`
+        // More flags that we will pass before `flags`
+        // (because `flags` may contain `--`).
+        let mut early_flags = Vec::<OsString>::new();
+
+        // Add target, edition to flags.
+        if let Some(target) = &target {
+            early_flags.push("--target".into());
+            early_flags.push(target.into());
+        }
+        if verbose {
+            early_flags.push("--verbose".into());
         }
+        early_flags.push("--edition".into());
+        early_flags.push(edition.as_deref().unwrap_or("2021").into());
 
-        // Prepare a sysroot, and add it to the flags.
+        // Prepare a sysroot, add it to the flags.
         let miri_sysroot = e.build_miri_sysroot(/* quiet */ !verbose, target.as_deref())?;
-        flags.push("--sysroot".into());
-        flags.push(miri_sysroot.into());
+        early_flags.push("--sysroot".into());
+        early_flags.push(miri_sysroot.into());
 
         // Compute everything needed to run the actual command. Also add MIRIFLAGS.
         let miri_manifest = path!(e.miri_dir / "Cargo.toml");
@@ -524,7 +531,7 @@ impl Command {
             };
             cmd.set_quiet(!verbose);
             // Add Miri flags
-            let cmd = cmd.args(&miri_flags).args(seed_flag).args(&flags);
+            let cmd = cmd.args(&miri_flags).args(&seed_flag).args(&early_flags).args(&flags);
             // And run the thing.
             Ok(cmd.run()?)
         };
@@ -543,7 +550,7 @@ impl Command {
         Ok(())
     }
 
-    fn fmt(flags: Vec<OsString>) -> Result<()> {
+    fn fmt(flags: Vec<String>) -> Result<()> {
         use itertools::Itertools;
 
         let e = MiriEnv::new()?;
@@ -565,6 +572,6 @@ impl Command {
             .filter_ok(|item| item.file_type().is_file())
             .map_ok(|item| item.into_path());
 
-        e.format_files(files, &e.toolchain[..], &config_path, &flags[..])
+        e.format_files(files, &e.toolchain[..], &config_path, &flags)
     }
 }
diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs
index a8626ceb45d..d436ef0c5aa 100644
--- a/src/tools/miri/miri-script/src/main.rs
+++ b/src/tools/miri/miri-script/src/main.rs
@@ -1,10 +1,10 @@
 #![allow(clippy::needless_question_mark)]
 
+mod args;
 mod commands;
 mod util;
 
-use std::ffi::OsString;
-use std::{env, ops::Range};
+use std::ops::Range;
 
 use anyhow::{anyhow, bail, Context, Result};
 
@@ -16,26 +16,26 @@ pub enum Command {
     /// sysroot, to prevent conflicts with other toolchains.
     Install {
         /// Flags that are passed through to `cargo install`.
-        flags: Vec<OsString>,
+        flags: Vec<String>,
     },
     /// Just build miri.
     Build {
         /// Flags that are passed through to `cargo build`.
-        flags: Vec<OsString>,
+        flags: Vec<String>,
     },
     /// Just check miri.
     Check {
         /// Flags that are passed through to `cargo check`.
-        flags: Vec<OsString>,
+        flags: Vec<String>,
     },
     /// Build miri, set up a sysroot and then run the test suite.
     Test {
         bless: bool,
         /// The cross-interpretation target.
         /// If none then the host is the target.
-        target: Option<OsString>,
+        target: Option<String>,
         /// Flags that are passed through to the test harness.
-        flags: Vec<OsString>,
+        flags: Vec<String>,
     },
     /// Build miri, set up a sysroot and then run the driver with the given <flags>.
     /// (Also respects MIRIFLAGS environment variable.)
@@ -43,33 +43,35 @@ pub enum Command {
         dep: bool,
         verbose: bool,
         many_seeds: Option<Range<u32>>,
+        target: Option<String>,
+        edition: Option<String>,
         /// Flags that are passed through to `miri`.
-        flags: Vec<OsString>,
+        flags: Vec<String>,
     },
     /// Format all sources and tests.
     Fmt {
         /// Flags that are passed through to `rustfmt`.
-        flags: Vec<OsString>,
+        flags: Vec<String>,
     },
     /// Runs clippy on all sources.
     Clippy {
         /// Flags that are passed through to `cargo clippy`.
-        flags: Vec<OsString>,
+        flags: Vec<String>,
     },
     /// Runs just `cargo <flags>` with the Miri-specific environment variables.
     /// Mainly meant to be invoked by rust-analyzer.
-    Cargo { flags: Vec<OsString> },
+    Cargo { flags: Vec<String> },
     /// Runs the benchmarks from bench-cargo-miri in hyperfine. hyperfine needs to be installed.
     Bench {
-        target: Option<OsString>,
+        target: Option<String>,
         /// List of benchmarks to run. By default all benchmarks are run.
-        benches: Vec<OsString>,
+        benches: Vec<String>,
     },
     /// Update and activate the rustup toolchain 'miri' to the commit given in the
     /// `rust-version` file.
     /// `rustup-toolchain-install-master` must be installed for this to work. Any extra
     /// flags are passed to `rustup-toolchain-install-master`.
-    Toolchain { flags: Vec<OsString> },
+    Toolchain { flags: Vec<String> },
     /// Pull and merge Miri changes from the rustc repo. Defaults to fetching the latest
     /// rustc commit. The fetched commit is stored in the `rust-version` file, so the
     /// next `./miri toolchain` will install the rustc that just got pulled.
@@ -145,113 +147,95 @@ Pass extra flags to all cargo invocations. (Ignored by `./miri cargo`.)"#;
 fn main() -> Result<()> {
     // We are hand-rolling our own argument parser, since `clap` can't express what we need
     // (https://github.com/clap-rs/clap/issues/5055).
-    let mut args = env::args_os().peekable();
-    args.next().unwrap(); // skip program name
-    let command = match args.next().and_then(|s| s.into_string().ok()).as_deref() {
-        Some("build") => Command::Build { flags: args.collect() },
-        Some("check") => Command::Check { flags: args.collect() },
+    let mut args = args::Args::new();
+    let command = match args.next_raw().as_deref() {
+        Some("build") => Command::Build { flags: args.remainder() },
+        Some("check") => Command::Check { flags: args.remainder() },
         Some("test") => {
             let mut target = None;
             let mut bless = false;
-
-            while let Some(arg) = args.peek().and_then(|s| s.to_str()) {
-                match arg {
-                    "--bless" => bless = true,
-                    "--target" => {
-                        // Skip "--target"
-                        args.next().unwrap();
-                        // Next argument is the target triple.
-                        let val = args.peek().ok_or_else(|| {
-                            anyhow!("`--target` must be followed by target triple")
-                        })?;
-                        target = Some(val.to_owned());
-                    }
-                    // Only parse the leading flags.
-                    _ => break,
+            let mut flags = Vec::new();
+            loop {
+                if args.get_long_flag("bless")? {
+                    bless = true;
+                } else if let Some(val) = args.get_long_opt("target")? {
+                    target = Some(val);
+                } else if let Some(flag) = args.get_other() {
+                    flags.push(flag);
+                } else {
+                    break;
                 }
-
-                // Consume the flag, look at the next one.
-                args.next().unwrap();
             }
-
-            Command::Test { bless, flags: args.collect(), target }
+            Command::Test { bless, flags, target }
         }
         Some("run") => {
             let mut dep = false;
             let mut verbose = false;
             let mut many_seeds = None;
-            while let Some(arg) = args.peek().and_then(|s| s.to_str()) {
-                if arg == "--dep" {
+            let mut target = None;
+            let mut edition = None;
+            let mut flags = Vec::new();
+            loop {
+                if args.get_long_flag("dep")? {
                     dep = true;
-                } else if arg == "-v" || arg == "--verbose" {
+                } else if args.get_long_flag("verbose")? || args.get_short_flag('v')? {
                     verbose = true;
-                } else if arg == "--many-seeds" {
-                    many_seeds = Some(0..256);
-                } else if let Some(val) = arg.strip_prefix("--many-seeds=") {
+                } else if let Some(val) = args.get_long_opt_with_default("many-seeds", "0..256")? {
                     let (from, to) = val.split_once("..").ok_or_else(|| {
-                        anyhow!("invalid format for `--many-seeds`: expected `from..to`")
+                        anyhow!("invalid format for `--many-seeds-range`: expected `from..to`")
                     })?;
                     let from: u32 = if from.is_empty() {
                         0
                     } else {
-                        from.parse().context("invalid `from` in `--many-seeds=from..to")?
+                        from.parse().context("invalid `from` in `--many-seeds-range=from..to")?
                     };
-                    let to: u32 = to.parse().context("invalid `to` in `--many-seeds=from..to")?;
+                    let to: u32 =
+                        to.parse().context("invalid `to` in `--many-seeds-range=from..to")?;
                     many_seeds = Some(from..to);
+                } else if let Some(val) = args.get_long_opt("target")? {
+                    target = Some(val);
+                } else if let Some(val) = args.get_long_opt("edition")? {
+                    edition = Some(val);
+                } else if let Some(flag) = args.get_other() {
+                    flags.push(flag);
                 } else {
-                    break; // not for us
+                    break;
                 }
-                // Consume the flag, look at the next one.
-                args.next().unwrap();
             }
-            Command::Run { dep, verbose, many_seeds, flags: args.collect() }
+            Command::Run { dep, verbose, many_seeds, target, edition, flags }
         }
-        Some("fmt") => Command::Fmt { flags: args.collect() },
-        Some("clippy") => Command::Clippy { flags: args.collect() },
-        Some("cargo") => Command::Cargo { flags: args.collect() },
-        Some("install") => Command::Install { flags: args.collect() },
+        Some("fmt") => Command::Fmt { flags: args.remainder() },
+        Some("clippy") => Command::Clippy { flags: args.remainder() },
+        Some("cargo") => Command::Cargo { flags: args.remainder() },
+        Some("install") => Command::Install { flags: args.remainder() },
         Some("bench") => {
             let mut target = None;
-            while let Some(arg) = args.peek().and_then(|s| s.to_str()) {
-                match arg {
-                    "--target" => {
-                        // Skip "--target"
-                        args.next().unwrap();
-                        // Next argument is the target triple.
-                        let val = args.peek().ok_or_else(|| {
-                            anyhow!("`--target` must be followed by target triple")
-                        })?;
-                        target = Some(val.to_owned());
-                    }
-                    // Only parse the leading flags.
-                    _ => break,
+            let mut benches = Vec::new();
+            loop {
+                if let Some(val) = args.get_long_opt("target")? {
+                    target = Some(val);
+                } else if let Some(flag) = args.get_other() {
+                    benches.push(flag);
+                } else {
+                    break;
                 }
-
-                // Consume the flag, look at the next one.
-                args.next().unwrap();
             }
-
-            Command::Bench { target, benches: args.collect() }
+            Command::Bench { target, benches }
         }
-        Some("toolchain") => Command::Toolchain { flags: args.collect() },
+        Some("toolchain") => Command::Toolchain { flags: args.remainder() },
         Some("rustc-pull") => {
-            let commit = args.next().map(|a| a.to_string_lossy().into_owned());
-            if args.next().is_some() {
+            let commit = args.next_raw();
+            if args.next_raw().is_some() {
                 bail!("Too many arguments for `./miri rustc-pull`");
             }
             Command::RustcPull { commit }
         }
         Some("rustc-push") => {
-            let github_user = args
-                .next()
-                .ok_or_else(|| {
-                    anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`")
-                })?
-                .to_string_lossy()
-                .into_owned();
-            let branch =
-                args.next().unwrap_or_else(|| "miri-sync".into()).to_string_lossy().into_owned();
-            if args.next().is_some() {
+            let github_user = args.next_raw().ok_or_else(|| {
+                anyhow!("Missing first argument for `./miri rustc-push GITHUB_USER [BRANCH]`")
+            })?;
+            let branch = args.next_raw().unwrap_or_else(|| "miri-sync".into());
+            if args.next_raw().is_some() {
                 bail!("Too many arguments for `./miri rustc-push GITHUB_USER BRANCH`");
             }
             Command::RustcPush { github_user, branch }
diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs
index 2b5791f3ea5..e9095a45fce 100644
--- a/src/tools/miri/miri-script/src/util.rs
+++ b/src/tools/miri/miri-script/src/util.rs
@@ -27,30 +27,6 @@ pub fn flagsplit(flags: &str) -> Vec<String> {
     flags.split(' ').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect()
 }
 
-pub fn arg_flag_value(
-    args: impl IntoIterator<Item = impl AsRef<OsStr>>,
-    flag: &str,
-) -> Option<OsString> {
-    let mut args = args.into_iter();
-    while let Some(arg) = args.next() {
-        let arg = arg.as_ref();
-        if arg == "--" {
-            return None;
-        }
-        let Some(arg) = arg.to_str() else {
-            // Skip non-UTF-8 arguments.
-            continue;
-        };
-        if arg == flag {
-            // Next one is the value.
-            return Some(args.next()?.as_ref().to_owned());
-        } else if let Some(val) = arg.strip_prefix(flag).and_then(|s| s.strip_prefix("=")) {
-            return Some(val.to_owned().into());
-        }
-    }
-    None
-}
-
 /// Some extra state we track for building Miri, such as the right RUSTFLAGS.
 pub struct MiriEnv {
     /// miri_dir is the root of the miri repository checkout we are working in.
@@ -133,7 +109,7 @@ impl MiriEnv {
     pub fn build(
         &self,
         manifest_path: impl AsRef<OsStr>,
-        args: &[OsString],
+        args: &[String],
         quiet: bool,
     ) -> Result<()> {
         let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
@@ -149,21 +125,21 @@ impl MiriEnv {
         Ok(())
     }
 
-    pub fn check(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
+    pub fn check(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
         let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
         cmd!(self.sh, "cargo +{toolchain} check {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
             .run()?;
         Ok(())
     }
 
-    pub fn clippy(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
+    pub fn clippy(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
         let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
         cmd!(self.sh, "cargo +{toolchain} clippy {cargo_extra_flags...} --manifest-path {manifest_path} --all-targets {args...}")
             .run()?;
         Ok(())
     }
 
-    pub fn test(&self, manifest_path: impl AsRef<OsStr>, args: &[OsString]) -> Result<()> {
+    pub fn test(&self, manifest_path: impl AsRef<OsStr>, args: &[String]) -> Result<()> {
         let MiriEnv { toolchain, cargo_extra_flags, .. } = self;
         cmd!(
             self.sh,
@@ -181,7 +157,7 @@ impl MiriEnv {
         files: impl Iterator<Item = Result<PathBuf, walkdir::Error>>,
         toolchain: &str,
         config_path: &Path,
-        flags: &[OsString],
+        flags: &[String],
     ) -> anyhow::Result<()> {
         use itertools::Itertools;
 
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 3636c856d0b..207ef6c5de7 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-ef15976387ad9c1cdceaabf469e0cf35f5852f6d
+6579ed89f0fcc26da71afdd11d30d63f6f812a0a
diff --git a/src/tools/miri/src/alloc_bytes.rs b/src/tools/miri/src/alloc_bytes.rs
new file mode 100644
index 00000000000..7952abdf9f4
--- /dev/null
+++ b/src/tools/miri/src/alloc_bytes.rs
@@ -0,0 +1,109 @@
+use std::alloc;
+use std::alloc::Layout;
+use std::borrow::Cow;
+use std::slice;
+
+use rustc_middle::mir::interpret::AllocBytes;
+use rustc_target::abi::{Align, Size};
+
+/// Allocation bytes that explicitly handle the layout of the data they're storing.
+/// This is necessary to interface with native code that accesses the program store in Miri.
+#[derive(Debug)]
+pub struct MiriAllocBytes {
+    /// Stored layout information about the allocation.
+    layout: alloc::Layout,
+    /// Pointer to the allocation contents.
+    /// Invariant:
+    /// * If `self.layout.size() == 0`, then `self.ptr` is some suitably aligned pointer
+    ///   without provenance (and no actual memory was allocated).
+    /// * Otherwise, `self.ptr` points to memory allocated with `self.layout`.
+    ptr: *mut u8,
+}
+
+impl Clone for MiriAllocBytes {
+    fn clone(&self) -> Self {
+        let bytes: Cow<'_, [u8]> = Cow::Borrowed(self);
+        let align = Align::from_bytes(self.layout.align().try_into().unwrap()).unwrap();
+        MiriAllocBytes::from_bytes(bytes, align)
+    }
+}
+
+impl Drop for MiriAllocBytes {
+    fn drop(&mut self) {
+        if self.layout.size() != 0 {
+            // SAFETY: Invariant, `self.ptr` points to memory allocated with `self.layout`.
+            unsafe { alloc::dealloc(self.ptr, self.layout) }
+        }
+    }
+}
+
+impl std::ops::Deref for MiriAllocBytes {
+    type Target = [u8];
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes.
+        // Note that due to the invariant this is true even if `self.layout.size() == 0`.
+        unsafe { slice::from_raw_parts(self.ptr, self.layout.size()) }
+    }
+}
+
+impl std::ops::DerefMut for MiriAllocBytes {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        // SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes.
+        // Note that due to the invariant this is true even if `self.layout.size() == 0`.
+        unsafe { slice::from_raw_parts_mut(self.ptr, self.layout.size()) }
+    }
+}
+
+impl MiriAllocBytes {
+    /// This method factors out how a `MiriAllocBytes` object is allocated,
+    /// specifically given an allocation function `alloc_fn`.
+    /// `alloc_fn` is only used if `size != 0`.
+    /// Returns `Err(layout)` if the allocation function returns a `ptr` that is `ptr.is_null()`.
+    fn alloc_with(
+        size: usize,
+        align: usize,
+        alloc_fn: impl FnOnce(Layout) -> *mut u8,
+    ) -> Result<MiriAllocBytes, Layout> {
+        let layout = Layout::from_size_align(size, align).unwrap();
+        let ptr = if size == 0 {
+            std::ptr::without_provenance_mut(align)
+        } else {
+            let ptr = alloc_fn(layout);
+            if ptr.is_null() {
+                return Err(layout);
+            }
+            ptr
+        };
+        // SAFETY: All `MiriAllocBytes` invariants are fulfilled.
+        Ok(Self { ptr, layout })
+    }
+}
+
+impl AllocBytes for MiriAllocBytes {
+    fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align) -> Self {
+        let slice = slice.into();
+        let size = slice.len();
+        let align = align.bytes_usize();
+        // SAFETY: `alloc_fn` will only be used if `size != 0`.
+        let alloc_fn = |layout| unsafe { alloc::alloc(layout) };
+        let alloc_bytes = MiriAllocBytes::alloc_with(size, align, alloc_fn)
+            .unwrap_or_else(|layout| alloc::handle_alloc_error(layout));
+        // SAFETY: `alloc_bytes.ptr` and `slice.as_ptr()` are non-null, properly aligned
+        // and valid for the `size`-many bytes to be copied.
+        unsafe { alloc_bytes.ptr.copy_from(slice.as_ptr(), size) };
+        alloc_bytes
+    }
+
+    fn zeroed(size: Size, align: Align) -> Option<Self> {
+        let size = size.bytes_usize();
+        let align = align.bytes_usize();
+        // SAFETY: `alloc_fn` will only be used if `size != 0`.
+        let alloc_fn = |layout| unsafe { alloc::alloc_zeroed(layout) };
+        MiriAllocBytes::alloc_with(size, align, alloc_fn).ok()
+    }
+
+    fn as_mut_ptr(&mut self) -> *mut u8 {
+        self.ptr
+    }
+}
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 305e9cd8d34..d748febeed4 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -405,9 +405,12 @@ fn main() {
 
     let mut rustc_args = vec![];
     let mut after_dashdash = false;
-
     // If user has explicitly enabled/disabled isolation
     let mut isolation_enabled: Option<bool> = None;
+
+    // Note that we require values to be given with `=`, not with a space.
+    // This matches how rustc parses `-Z`.
+    // However, unlike rustc we do not accept a space after `-Z`.
     for arg in args {
         if rustc_args.is_empty() {
             // Very first arg: binary name.
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
index 55ff09c53fe..3b6c29b5eb1 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
@@ -9,7 +9,7 @@ use crate::borrow_tracker::{
 };
 use crate::ProvenanceExtra;
 
-/// Exactly what cache size we should use is a difficult tradeoff. There will always be some
+/// Exactly what cache size we should use is a difficult trade-off. There will always be some
 /// workload which has a `BorTag` working set which exceeds the size of the cache, and ends up
 /// falling back to linear searches of the borrow stack very often.
 /// The cost of making this value too large is that the loop in `Stack::insert` which ensures the
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
index b9f0b5bc17a..8abc8530f7c 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
@@ -390,7 +390,7 @@ struct DisplayFmtWrapper {
     warning_text: S,
 }
 
-/// Formating of the permissions on each range.
+/// Formatting of the permissions on each range.
 ///
 /// Example:
 /// ```rust,ignore (private type)
@@ -422,7 +422,7 @@ struct DisplayFmtPermission {
     range_sep: S,
 }
 
-/// Formating of the tree structure.
+/// Formatting of the tree structure.
 ///
 /// Example:
 /// ```rust,ignore (private type)
@@ -487,7 +487,7 @@ struct DisplayFmtAccess {
     meh: S,
 }
 
-/// All parameters to determine how the tree is formated.
+/// All parameters to determine how the tree is formatted.
 struct DisplayFmt {
     wrapper: DisplayFmtWrapper,
     perm: DisplayFmtPermission,
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
index bec51c7cdf2..28848e244ee 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
@@ -202,7 +202,7 @@ impl Permission {
         Self { inner: Frozen }
     }
 
-    /// Default initial permission of  the root of a new tre at out-of-bounds positions.
+    /// Default initial permission of  the root of a new tree at out-of-bounds positions.
     /// Must *only* be used for the root, this is not in general an "initial" permission!
     pub fn new_disabled() -> Self {
         Self { inner: Disabled }
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs
index 6777f41ac2d..19acbdb697b 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs
@@ -483,7 +483,7 @@ mod spurious_read {
         /// that causes UB in the target but not in the source.
         /// This implementation simply explores the reachable space
         /// by all sequences of `TestEvent`.
-        /// This function can be instanciated with `RetX` and `RetY`
+        /// This function can be instantiated with `RetX` and `RetY`
         /// among `NoRet` or `AllowRet` to resp. forbid/allow `x`/`y` to lose their
         /// protector.
         fn distinguishable<RetX, RetY>(&self, other: &Self) -> bool
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index 6953ce81c5e..24e2c253852 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -111,7 +111,7 @@ pub enum BlockReason {
     Condvar(CondvarId),
     /// Blocked on a reader-writer lock.
     RwLock(RwLockId),
-    /// Blocled on a Futex variable.
+    /// Blocked on a Futex variable.
     Futex { addr: u64 },
     /// Blocked on an InitOnce.
     InitOnce(InitOnceId),
diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs
index 574962c48d4..8c71eeb27aa 100644
--- a/src/tools/miri/src/concurrency/weak_memory.rs
+++ b/src/tools/miri/src/concurrency/weak_memory.rs
@@ -48,7 +48,7 @@
 //! One consequence of this difference is that safe/sound Rust allows for more operations on atomic locations
 //! than the C++20 atomic API was intended to allow, such as non-atomically accessing
 //! a previously atomically accessed location, or accessing previously atomically accessed locations with a differently sized operation
-//! (such as accessing the top 16 bits of an AtomicU32). These scenarios are generally undiscussed in formalisations of C++ memory model.
+//! (such as accessing the top 16 bits of an AtomicU32). These scenarios are generally undiscussed in formalizations of C++ memory model.
 //! In Rust, these operations can only be done through a `&mut AtomicFoo` reference or one derived from it, therefore these operations
 //! can only happen after all previous accesses on the same locations. This implementation is adapted to allow these operations.
 //! A mixed atomicity read that races with writes, or a write that races with reads or writes will still cause UBs to be thrown.
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index cb5ed27b6cf..189c4a20bf6 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -459,7 +459,7 @@ pub fn report_error<'tcx, 'mir>(
 
 pub fn report_leaks<'mir, 'tcx>(
     ecx: &InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
-    leaks: Vec<(AllocId, MemoryKind, Allocation<Provenance, AllocExtra<'tcx>>)>,
+    leaks: Vec<(AllocId, MemoryKind, Allocation<Provenance, AllocExtra<'tcx>, MiriAllocBytes>)>,
 ) {
     let mut any_pruned = false;
     for (id, kind, mut alloc) in leaks {
diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs
index 40f6b8a64ef..501dbc1603e 100644
--- a/src/tools/miri/src/intrinsics/atomic.rs
+++ b/src/tools/miri/src/intrinsics/atomic.rs
@@ -25,98 +25,98 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
         let intrinsic_structure: Vec<_> = intrinsic_name.split('_').collect();
 
-        fn read_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicReadOrd> {
-            Ok(match ord {
+        fn read_ord(ord: &str) -> AtomicReadOrd {
+            match ord {
                 "seqcst" => AtomicReadOrd::SeqCst,
                 "acquire" => AtomicReadOrd::Acquire,
                 "relaxed" => AtomicReadOrd::Relaxed,
-                _ => throw_unsup_format!("unsupported read ordering `{ord}`"),
-            })
+                _ => panic!("invalid read ordering `{ord}`"),
+            }
         }
 
-        fn write_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicWriteOrd> {
-            Ok(match ord {
+        fn write_ord(ord: &str) -> AtomicWriteOrd {
+            match ord {
                 "seqcst" => AtomicWriteOrd::SeqCst,
                 "release" => AtomicWriteOrd::Release,
                 "relaxed" => AtomicWriteOrd::Relaxed,
-                _ => throw_unsup_format!("unsupported write ordering `{ord}`"),
-            })
+                _ => panic!("invalid write ordering `{ord}`"),
+            }
         }
 
-        fn rw_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicRwOrd> {
-            Ok(match ord {
+        fn rw_ord(ord: &str) -> AtomicRwOrd {
+            match ord {
                 "seqcst" => AtomicRwOrd::SeqCst,
                 "acqrel" => AtomicRwOrd::AcqRel,
                 "acquire" => AtomicRwOrd::Acquire,
                 "release" => AtomicRwOrd::Release,
                 "relaxed" => AtomicRwOrd::Relaxed,
-                _ => throw_unsup_format!("unsupported read-write ordering `{ord}`"),
-            })
+                _ => panic!("invalid read-write ordering `{ord}`"),
+            }
         }
 
-        fn fence_ord<'tcx>(ord: &str) -> InterpResult<'tcx, AtomicFenceOrd> {
-            Ok(match ord {
+        fn fence_ord(ord: &str) -> AtomicFenceOrd {
+            match ord {
                 "seqcst" => AtomicFenceOrd::SeqCst,
                 "acqrel" => AtomicFenceOrd::AcqRel,
                 "acquire" => AtomicFenceOrd::Acquire,
                 "release" => AtomicFenceOrd::Release,
-                _ => throw_unsup_format!("unsupported fence ordering `{ord}`"),
-            })
+                _ => panic!("invalid fence ordering `{ord}`"),
+            }
         }
 
         match &*intrinsic_structure {
-            ["load", ord] => this.atomic_load(args, dest, read_ord(ord)?)?,
-            ["store", ord] => this.atomic_store(args, write_ord(ord)?)?,
+            ["load", ord] => this.atomic_load(args, dest, read_ord(ord))?,
+            ["store", ord] => this.atomic_store(args, write_ord(ord))?,
 
-            ["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord)?)?,
-            ["singlethreadfence", ord] => this.compiler_fence_intrinsic(args, fence_ord(ord)?)?,
+            ["fence", ord] => this.atomic_fence_intrinsic(args, fence_ord(ord))?,
+            ["singlethreadfence", ord] => this.compiler_fence_intrinsic(args, fence_ord(ord))?,
 
-            ["xchg", ord] => this.atomic_exchange(args, dest, rw_ord(ord)?)?,
+            ["xchg", ord] => this.atomic_exchange(args, dest, rw_ord(ord))?,
             ["cxchg", ord1, ord2] =>
-                this.atomic_compare_exchange(args, dest, rw_ord(ord1)?, read_ord(ord2)?)?,
+                this.atomic_compare_exchange(args, dest, rw_ord(ord1), read_ord(ord2))?,
             ["cxchgweak", ord1, ord2] =>
-                this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1)?, read_ord(ord2)?)?,
+                this.atomic_compare_exchange_weak(args, dest, rw_ord(ord1), read_ord(ord2))?,
 
             ["or", ord] =>
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord)?)?,
+                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord))?,
             ["xor", ord] =>
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord)?)?,
+                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord))?,
             ["and", ord] =>
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord)?)?,
+                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord))?,
             ["nand", ord] =>
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord)?)?,
+                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord))?,
             ["xadd", ord] =>
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord)?)?,
+                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord))?,
             ["xsub", ord] =>
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord)?)?,
+                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord))?,
             ["min", ord] => {
                 // Later we will use the type to indicate signed vs unsigned,
                 // so make sure it matches the intrinsic name.
                 assert!(matches!(args[1].layout.ty.kind(), ty::Int(_)));
-                this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord)?)?;
+                this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?;
             }
             ["umin", ord] => {
                 // Later we will use the type to indicate signed vs unsigned,
                 // so make sure it matches the intrinsic name.
                 assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_)));
-                this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord)?)?;
+                this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?;
             }
             ["max", ord] => {
                 // Later we will use the type to indicate signed vs unsigned,
                 // so make sure it matches the intrinsic name.
                 assert!(matches!(args[1].layout.ty.kind(), ty::Int(_)));
-                this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?;
+                this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?;
             }
             ["umax", ord] => {
                 // Later we will use the type to indicate signed vs unsigned,
                 // so make sure it matches the intrinsic name.
                 assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_)));
-                this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord)?)?;
+                this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?;
             }
 
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
 
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index 9e7fc7a21fc..0a7927d0621 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -26,7 +26,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         args: &[OpTy<'tcx, Provenance>],
         dest: &MPlaceTy<'tcx, Provenance>,
         ret: Option<mir::BasicBlock>,
-        _unwind: mir::UnwindAction,
+        unwind: mir::UnwindAction,
     ) -> InterpResult<'tcx, Option<ty::Instance<'tcx>>> {
         let this = self.eval_context_mut();
 
@@ -62,11 +62,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     args: instance.args,
                 }))
             }
-            EmulateItemResult::NeedsJumping => {
+            EmulateItemResult::NeedsReturn => {
                 trace!("{:?}", this.dump_place(&dest.clone().into()));
                 this.return_to_block(ret)?;
                 Ok(None)
             }
+            EmulateItemResult::NeedsUnwind => {
+                // Jump to the unwind block to begin unwinding.
+                this.unwind_to_block(unwind)?;
+                Ok(None)
+            }
             EmulateItemResult::AlreadyJumped => Ok(None),
         }
     }
@@ -167,6 +172,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 // This is a "bitwise" operation, so there's no NaN non-determinism.
                 this.write_scalar(Scalar::from_f64(f.abs()), dest)?;
             }
+
             "floorf32" | "ceilf32" | "truncf32" | "roundf32" | "rintf32" => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f32()?;
@@ -182,6 +188,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
+            "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => {
+                let [f] = check_arg_count(args)?;
+                let f = this.read_scalar(f)?.to_f64()?;
+                let mode = match intrinsic_name {
+                    "floorf64" => Round::TowardNegative,
+                    "ceilf64" => Round::TowardPositive,
+                    "truncf64" => Round::TowardZero,
+                    "roundf64" => Round::NearestTiesToAway,
+                    "rintf64" => Round::NearestTiesToEven,
+                    _ => bug!(),
+                };
+                let res = f.round_to_integral(mode).value;
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
+
             #[rustfmt::skip]
             | "sinf32"
             | "cosf32"
@@ -211,22 +233,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
-
-            "floorf64" | "ceilf64" | "truncf64" | "roundf64" | "rintf64" => {
-                let [f] = check_arg_count(args)?;
-                let f = this.read_scalar(f)?.to_f64()?;
-                let mode = match intrinsic_name {
-                    "floorf64" => Round::TowardNegative,
-                    "ceilf64" => Round::TowardPositive,
-                    "truncf64" => Round::TowardZero,
-                    "roundf64" => Round::NearestTiesToAway,
-                    "rintf64" => Round::NearestTiesToEven,
-                    _ => bug!(),
-                };
-                let res = f.round_to_integral(mode).value;
-                let res = this.adjust_nan(res, &[f]);
-                this.write_scalar(res, dest)?;
-            }
             #[rustfmt::skip]
             | "sinf64"
             | "cosf64"
@@ -257,61 +263,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 this.write_scalar(res, dest)?;
             }
 
-            #[rustfmt::skip]
-            | "fadd_fast"
-            | "fsub_fast"
-            | "fmul_fast"
-            | "fdiv_fast"
-            | "frem_fast"
-            => {
-                let [a, b] = check_arg_count(args)?;
-                let a = this.read_immediate(a)?;
-                let b = this.read_immediate(b)?;
-                let op = match intrinsic_name {
-                    "fadd_fast" => mir::BinOp::Add,
-                    "fsub_fast" => mir::BinOp::Sub,
-                    "fmul_fast" => mir::BinOp::Mul,
-                    "fdiv_fast" => mir::BinOp::Div,
-                    "frem_fast" => mir::BinOp::Rem,
-                    _ => bug!(),
-                };
-                let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> {
-                    let ty::Float(fty) = x.layout.ty.kind() else {
-                        bug!("float_finite: non-float input type {}", x.layout.ty)
-                    };
-                    Ok(match fty {
-                        FloatTy::F16 => unimplemented!("f16_f128"),
-                        FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
-                        FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
-                        FloatTy::F128 => unimplemented!("f16_f128"),
-                    })
-                };
-                match (float_finite(&a)?, float_finite(&b)?) {
-                    (false, false) => throw_ub_format!(
-                        "`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
-                    ),
-                    (false, _) => throw_ub_format!(
-                        "`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
-                    ),
-                    (_, false) => throw_ub_format!(
-                        "`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
-                    ),
-                    _ => {}
-                }
-                let res = this.wrapping_binary_op(op, &a, &b)?;
-                if !float_finite(&res)? {
-                    throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
-                }
-                // This cannot be a NaN so we also don't have to apply any non-determinism.
-                // (Also, `wrapping_binary_op` already called `generate_nan` if needed.)
-                this.write_immediate(*res, dest)?;
-            }
-
-            #[rustfmt::skip]
-            | "minnumf32"
-            | "maxnumf32"
-            | "copysignf32"
-            => {
+            "minnumf32" | "maxnumf32" | "copysignf32" => {
                 let [a, b] = check_arg_count(args)?;
                 let a = this.read_scalar(a)?.to_f32()?;
                 let b = this.read_scalar(b)?.to_f32()?;
@@ -323,12 +275,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 };
                 this.write_scalar(Scalar::from_f32(res), dest)?;
             }
-
-            #[rustfmt::skip]
-            | "minnumf64"
-            | "maxnumf64"
-            | "copysignf64"
-            => {
+            "minnumf64" | "maxnumf64" | "copysignf64" => {
                 let [a, b] = check_arg_count(args)?;
                 let a = this.read_scalar(a)?.to_f64()?;
                 let b = this.read_scalar(b)?.to_f64()?;
@@ -351,7 +298,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let res = this.adjust_nan(res, &[a, b, c]);
                 this.write_scalar(res, dest)?;
             }
-
             "fmaf64" => {
                 let [a, b, c] = check_arg_count(args)?;
                 let a = this.read_scalar(a)?.to_f64()?;
@@ -372,7 +318,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let res = this.adjust_nan(res, &[f1, f2]);
                 this.write_scalar(res, dest)?;
             }
-
             "powf64" => {
                 let [f1, f2] = check_arg_count(args)?;
                 let f1 = this.read_scalar(f1)?.to_f64()?;
@@ -392,7 +337,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
-
             "powif64" => {
                 let [f, i] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f64()?;
@@ -403,6 +347,79 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 this.write_scalar(res, dest)?;
             }
 
+            #[rustfmt::skip]
+            | "fadd_algebraic"
+            | "fsub_algebraic"
+            | "fmul_algebraic"
+            | "fdiv_algebraic"
+            | "frem_algebraic"
+            => {
+                let [a, b] = check_arg_count(args)?;
+                let a = this.read_immediate(a)?;
+                let b = this.read_immediate(b)?;
+                let op = match intrinsic_name {
+                    "fadd_algebraic" => mir::BinOp::Add,
+                    "fsub_algebraic" => mir::BinOp::Sub,
+                    "fmul_algebraic" => mir::BinOp::Mul,
+                    "fdiv_algebraic" => mir::BinOp::Div,
+                    "frem_algebraic" => mir::BinOp::Rem,
+                    _ => bug!(),
+                };
+                let res = this.wrapping_binary_op(op, &a, &b)?;
+                // `wrapping_binary_op` already called `generate_nan` if necessary.
+                this.write_immediate(*res, dest)?;
+            }
+
+            #[rustfmt::skip]
+            | "fadd_fast"
+            | "fsub_fast"
+            | "fmul_fast"
+            | "fdiv_fast"
+            | "frem_fast"
+            => {
+                let [a, b] = check_arg_count(args)?;
+                let a = this.read_immediate(a)?;
+                let b = this.read_immediate(b)?;
+                let op = match intrinsic_name {
+                    "fadd_fast" => mir::BinOp::Add,
+                    "fsub_fast" => mir::BinOp::Sub,
+                    "fmul_fast" => mir::BinOp::Mul,
+                    "fdiv_fast" => mir::BinOp::Div,
+                    "frem_fast" => mir::BinOp::Rem,
+                    _ => bug!(),
+                };
+                let float_finite = |x: &ImmTy<'tcx, _>| -> InterpResult<'tcx, bool> {
+                    let ty::Float(fty) = x.layout.ty.kind() else {
+                        bug!("float_finite: non-float input type {}", x.layout.ty)
+                    };
+                    Ok(match fty {
+                        FloatTy::F16 => unimplemented!("f16_f128"),
+                        FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
+                        FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
+                        FloatTy::F128 => unimplemented!("f16_f128"),
+                    })
+                };
+                match (float_finite(&a)?, float_finite(&b)?) {
+                    (false, false) => throw_ub_format!(
+                        "`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
+                    ),
+                    (false, _) => throw_ub_format!(
+                        "`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
+                    ),
+                    (_, false) => throw_ub_format!(
+                        "`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
+                    ),
+                    _ => {}
+                }
+                let res = this.wrapping_binary_op(op, &a, &b)?;
+                if !float_finite(&res)? {
+                    throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
+                }
+                // This cannot be a NaN so we also don't have to apply any non-determinism.
+                // (Also, `wrapping_binary_op` already called `generate_nan` if needed.)
+                this.write_immediate(*res, dest)?;
+            }
+
             "float_to_int_unchecked" => {
                 let [val] = check_arg_count(args)?;
                 let val = this.read_immediate(val)?;
@@ -429,6 +446,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             _ => return Ok(EmulateItemResult::NotSupported),
         }
 
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index d0a78429ca8..4cde364fbc4 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -746,7 +746,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 
     fn fminmax_op(
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 54eb6a3bd66..e4879f2f531 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -13,6 +13,7 @@
 #![feature(lint_reasons)]
 #![feature(trait_upcasting)]
 #![feature(strict_overflow_ops)]
+#![feature(strict_provenance)]
 // Configure clippy and other lints
 #![allow(
     clippy::collapsible_else_if,
@@ -74,6 +75,7 @@ extern crate rustc_target;
 extern crate rustc_driver;
 
 mod alloc_addresses;
+mod alloc_bytes;
 mod borrow_tracker;
 mod clock;
 mod concurrency;
@@ -107,6 +109,7 @@ pub use crate::shims::tls::TlsData;
 pub use crate::shims::EmulateItemResult;
 
 pub use crate::alloc_addresses::{EvalContextExt as _, ProvenanceMode};
+pub use crate::alloc_bytes::MiriAllocBytes;
 pub use crate::borrow_tracker::stacked_borrows::{
     EvalContextExt as _, Item, Permission, Stack, Stacks,
 };
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 8854b185280..cbf02d701bc 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -30,14 +30,13 @@ use rustc_target::abi::{Align, Size};
 use rustc_target::spec::abi::Abi;
 
 use crate::{
-    concurrency::{data_race, weak_memory},
-    shims::unix,
+    concurrency::{
+        data_race::{self, NaReadType, NaWriteType},
+        weak_memory,
+    },
     *,
 };
 
-use self::concurrency::data_race::NaReadType;
-use self::concurrency::data_race::NaWriteType;
-
 /// First real-time signal.
 /// `signal(7)` says this must be between 32 and 64 and specifies 34 or 35
 /// as typical values.
@@ -206,11 +205,11 @@ pub enum Provenance {
     /// whether *some* exposed pointer could have done what we want to do, and if the answer is yes
     /// then we allow the access. This allows too much code in two ways:
     /// - The same wildcard pointer can "take the role" of multiple different exposed pointers on
-    ///   subsequenct memory accesses.
+    ///   subsequent memory accesses.
     /// - In the aliasing model, we don't just have to know the borrow tag of the pointer used for
     ///   the access, we also have to update the aliasing state -- and that update can be very
     ///   different depending on which borrow tag we pick! Stacked Borrows has support for this by
-    ///   switching to a stack that is only approximately known, i.e. we overapproximate the effect
+    ///   switching to a stack that is only approximately known, i.e. we over-approximate the effect
     ///   of using *any* exposed pointer for this access, and only keep information about the borrow
     ///   stack that would be true with all possible choices.
     Wildcard,
@@ -464,9 +463,9 @@ pub struct MiriMachine<'mir, 'tcx> {
     pub(crate) validate: bool,
 
     /// The table of file descriptors.
-    pub(crate) fds: unix::FdTable,
+    pub(crate) fds: shims::FdTable,
     /// The table of directory descriptors.
-    pub(crate) dirs: unix::DirTable,
+    pub(crate) dirs: shims::DirTable,
 
     /// This machine's monotone clock.
     pub(crate) clock: Clock,
@@ -641,7 +640,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
             tls: TlsData::default(),
             isolated_op: config.isolated_op,
             validate: config.validate,
-            fds: unix::FdTable::new(config.mute_stdout_stderr),
+            fds: shims::FdTable::new(config.mute_stdout_stderr),
             dirs: Default::default(),
             layouts,
             threads: ThreadManager::default(),
@@ -862,7 +861,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
 
     type Provenance = Provenance;
     type ProvenanceExtra = ProvenanceExtra;
-    type Bytes = Box<[u8]>;
+    type Bytes = MiriAllocBytes;
 
     type MemoryMap =
         MonoHashMap<AllocId, (MemoryKind, Allocation<Provenance, Self::AllocExtra, Self::Bytes>)>;
@@ -1088,8 +1087,9 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
         id: AllocId,
         alloc: Cow<'b, Allocation>,
         kind: Option<MemoryKind>,
-    ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>> {
-        let kind = kind.expect("we set our GLOBAL_KIND so this cannot be None");
+    ) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
+    {
+        let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
         if ecx.machine.tracked_alloc_ids.contains(&id) {
             ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(
                 id,
@@ -1126,7 +1126,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
             Some(ecx.generate_stacktrace())
         };
 
-        let alloc: Allocation<Provenance, Self::AllocExtra> = alloc.adjust_from_tcx(
+        let alloc: Allocation<Provenance, Self::AllocExtra, Self::Bytes> = alloc.adjust_from_tcx(
             &ecx.tcx,
             AllocExtra {
                 borrow_tracker,
diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs
index f23d7dfd52d..ecd614bf467 100644
--- a/src/tools/miri/src/provenance_gc.rs
+++ b/src/tools/miri/src/provenance_gc.rs
@@ -122,7 +122,7 @@ impl VisitProvenance for OpTy<'_, Provenance> {
     }
 }
 
-impl VisitProvenance for Allocation<Provenance, AllocExtra<'_>> {
+impl VisitProvenance for Allocation<Provenance, AllocExtra<'_>, MiriAllocBytes> {
     fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
         for prov in self.provenance().provenances() {
             prov.visit_provenance(visit);
diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs
index 1deb9a5654e..ca672bdc611 100644
--- a/src/tools/miri/src/shims/alloc.rs
+++ b/src/tools/miri/src/shims/alloc.rs
@@ -87,7 +87,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             }
             AllocatorKind::Default => {
                 default(this)?;
-                Ok(EmulateItemResult::NeedsJumping)
+                Ok(EmulateItemResult::NeedsReturn)
             }
         }
     }
@@ -111,6 +111,32 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         Ok(ptr.into())
     }
 
+    fn posix_memalign(
+        &mut self,
+        memptr: &OpTy<'tcx, Provenance>,
+        align: &OpTy<'tcx, Provenance>,
+        size: &OpTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, Scalar<Provenance>> {
+        let this = self.eval_context_mut();
+        let memptr = this.deref_pointer(memptr)?;
+        let align = this.read_target_usize(align)?;
+        let size = this.read_target_usize(size)?;
+
+        // Align must be power of 2, and also at least ptr-sized (POSIX rules).
+        // But failure to adhere to this is not UB, it's an error condition.
+        if !align.is_power_of_two() || align < this.pointer_size().bytes() {
+            Ok(this.eval_libc("EINVAL"))
+        } else {
+            let ptr = this.allocate_ptr(
+                Size::from_bytes(size),
+                Align::from_bytes(align).unwrap(),
+                MiriMemoryKind::C.into(),
+            )?;
+            this.write_pointer(ptr, &memptr)?;
+            Ok(Scalar::from_i32(0))
+        }
+    }
+
     fn free(&mut self, ptr: Pointer<Option<Provenance>>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
         if !this.ptr_is_null(ptr)? {
@@ -146,4 +172,45 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             }
         }
     }
+
+    fn aligned_alloc(
+        &mut self,
+        align: &OpTy<'tcx, Provenance>,
+        size: &OpTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, Pointer<Option<Provenance>>> {
+        let this = self.eval_context_mut();
+        let align = this.read_target_usize(align)?;
+        let size = this.read_target_usize(size)?;
+
+        // Alignment must be a power of 2, and "supported by the implementation".
+        // We decide that "supported by the implementation" means that the
+        // size must be a multiple of the alignment. (This restriction seems common
+        // enough that it is stated on <https://en.cppreference.com/w/c/memory/aligned_alloc>
+        // as a general rule, but the actual standard has no such rule.)
+        // If any of these are violated, we have to return NULL.
+        // All fundamental alignments must be supported.
+        //
+        // macOS and Illumos are buggy in that they require the alignment
+        // to be at least the size of a pointer, so they do not support all fundamental
+        // alignments. We do not emulate those platform bugs.
+        //
+        // Linux also sets errno to EINVAL, but that's non-standard behavior that we do not
+        // emulate.
+        // FreeBSD says some of these cases are UB but that's violating the C standard.
+        // http://en.cppreference.com/w/cpp/memory/c/aligned_alloc
+        // Linux: https://linux.die.net/man/3/aligned_alloc
+        // FreeBSD: https://man.freebsd.org/cgi/man.cgi?query=aligned_alloc&apropos=0&sektion=3&manpath=FreeBSD+9-current&format=html
+        match size.checked_rem(align) {
+            Some(0) if align.is_power_of_two() => {
+                let align = align.max(this.malloc_align(size).bytes());
+                let ptr = this.allocate_ptr(
+                    Size::from_bytes(size),
+                    Align::from_bytes(align).unwrap(),
+                    MiriMemoryKind::C.into(),
+                )?;
+                Ok(ptr.into())
+            }
+            _ => Ok(Pointer::null()),
+        }
+    }
 }
diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs
index c3c7ef7c1fd..b9817a18773 100644
--- a/src/tools/miri/src/shims/extern_static.rs
+++ b/src/tools/miri/src/shims/extern_static.rs
@@ -55,6 +55,12 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
         let val = ImmTy::from_int(val, this.machine.layouts.u8);
         Self::alloc_extern_static(this, "__rust_alloc_error_handler_should_panic", val)?;
 
+        if this.target_os_is_unix() {
+            // "environ" is mandated by POSIX.
+            let environ = this.machine.env_vars.unix().environ();
+            Self::add_extern_static(this, "environ", environ);
+        }
+
         match this.tcx.sess.target.os.as_ref() {
             "linux" => {
                 Self::null_ptr_extern_statics(
@@ -62,19 +68,13 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
                     &["__cxa_thread_atexit_impl", "__clock_gettime64"],
                 )?;
                 Self::weak_symbol_extern_statics(this, &["getrandom", "statx"])?;
-                // "environ"
-                let environ = this.machine.env_vars.unix().environ();
-                Self::add_extern_static(this, "environ", environ);
             }
             "freebsd" => {
                 Self::null_ptr_extern_statics(this, &["__cxa_thread_atexit_impl"])?;
-                // "environ"
-                let environ = this.machine.env_vars.unix().environ();
-                Self::add_extern_static(this, "environ", environ);
             }
             "android" => {
                 Self::null_ptr_extern_statics(this, &["bsd_signal"])?;
-                Self::weak_symbol_extern_statics(this, &["signal"])?;
+                Self::weak_symbol_extern_statics(this, &["signal", "getrandom"])?;
             }
             "windows" => {
                 // "_tls_used"
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index d431c28d55a..eccccb4a449 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -82,11 +82,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         }
 
         // The rest either implements the logic, or falls back to `lookup_exported_symbol`.
-        match this.emulate_foreign_item_inner(link_name, abi, args, dest, unwind)? {
-            EmulateItemResult::NeedsJumping => {
+        match this.emulate_foreign_item_inner(link_name, abi, args, dest)? {
+            EmulateItemResult::NeedsReturn => {
                 trace!("{:?}", this.dump_place(&dest.clone().into()));
                 this.return_to_block(ret)?;
             }
+            EmulateItemResult::NeedsUnwind => {
+                // Jump to the unwind block to begin unwinding.
+                this.unwind_to_block(unwind)?;
+            }
             EmulateItemResult::AlreadyJumped => (),
             EmulateItemResult::NotSupported => {
                 if let Some(body) = this.lookup_exported_symbol(link_name)? {
@@ -108,6 +112,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let this = self.eval_context_ref();
         match this.tcx.sess.target.os.as_ref() {
             os if this.target_os_is_unix() => shims::unix::foreign_items::is_dyn_sym(name, os),
+            "wasi" => shims::wasi::foreign_items::is_dyn_sym(name),
             "windows" => shims::windows::foreign_items::is_dyn_sym(name),
             _ => false,
         }
@@ -205,7 +210,6 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         abi: Abi,
         args: &[OpTy<'tcx, Provenance>],
         dest: &MPlaceTy<'tcx, Provenance>,
-        unwind: mir::UnwindAction,
     ) -> InterpResult<'tcx, EmulateItemResult> {
         let this = self.eval_context_mut();
 
@@ -217,7 +221,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             // by the specified `.so` file; we should continue and check if it corresponds to
             // a provided shim.
             if this.call_native_fn(link_name, dest, args)? {
-                return Ok(EmulateItemResult::NeedsJumping);
+                return Ok(EmulateItemResult::NeedsReturn);
             }
         }
 
@@ -262,9 +266,9 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         match link_name.as_str() {
             // Miri-specific extern functions
             "miri_start_unwind" => {
-                // `check_shim` happens inside `handle_miri_start_unwind`.
-                this.handle_miri_start_unwind(abi, link_name, args, unwind)?;
-                return Ok(EmulateItemResult::AlreadyJumped);
+                let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                this.handle_miri_start_unwind(payload)?;
+                return Ok(EmulateItemResult::NeedsUnwind);
             }
             "miri_run_provenance_gc" => {
                 let [] = this.check_shim(abi, Abi::Rust, link_name, args)?;
@@ -479,7 +483,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     "__rust_alloc" => return this.emulate_allocator(default),
                     "miri_alloc" => {
                         default(this)?;
-                        return Ok(EmulateItemResult::NeedsJumping);
+                        return Ok(EmulateItemResult::NeedsReturn);
                     }
                     _ => unreachable!(),
                 }
@@ -539,7 +543,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     }
                     "miri_dealloc" => {
                         default(this)?;
-                        return Ok(EmulateItemResult::NeedsJumping);
+                        return Ok(EmulateItemResult::NeedsReturn);
                     }
                     _ => unreachable!(),
                 }
@@ -947,6 +951,10 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                         shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
                             this, link_name, abi, args, dest,
                         ),
+                    "wasi" =>
+                        shims::wasi::foreign_items::EvalContextExt::emulate_foreign_item_inner(
+                            this, link_name, abi, args, dest,
+                        ),
                     "windows" =>
                         shims::windows::foreign_items::EvalContextExt::emulate_foreign_item_inner(
                             this, link_name, abi, args, dest,
@@ -956,6 +964,6 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         };
         // We only fall through to here if we did *not* hit the `_` arm above,
         // i.e., if we actually emulated the function with one of the shims.
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs
index aaa3c69b92d..a41a2883c91 100644
--- a/src/tools/miri/src/shims/mod.rs
+++ b/src/tools/miri/src/shims/mod.rs
@@ -2,25 +2,30 @@
 
 mod alloc;
 mod backtrace;
-pub mod foreign_items;
 #[cfg(target_os = "linux")]
-pub mod native_lib;
-pub mod unix;
-pub mod windows;
+mod native_lib;
+mod unix;
+mod wasi;
+mod windows;
 mod x86;
 
 pub mod env;
 pub mod extern_static;
+pub mod foreign_items;
 pub mod os_str;
 pub mod panic;
 pub mod time;
 pub mod tls;
 
+pub use unix::{DirTable, FdTable};
+
 /// What needs to be done after emulating an item (a shim or an intrinsic) is done.
 pub enum EmulateItemResult {
     /// The caller is expected to jump to the return block.
-    NeedsJumping,
-    /// Jumping has already been taken care of.
+    NeedsReturn,
+    /// The caller is expected to jump to the unwind block.
+    NeedsUnwind,
+    /// Jumping to the next block has already been taken care of.
     AlreadyJumped,
     /// The item is not supported.
     NotSupported,
diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs
index 4444d297469..e0e5396a455 100644
--- a/src/tools/miri/src/shims/panic.rs
+++ b/src/tools/miri/src/shims/panic.rs
@@ -13,7 +13,6 @@
 
 use rustc_ast::Mutability;
 use rustc_middle::{mir, ty};
-use rustc_span::Symbol;
 use rustc_target::spec::abi::Abi;
 use rustc_target::spec::PanicStrategy;
 
@@ -46,25 +45,15 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir,
 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     /// Handles the special `miri_start_unwind` intrinsic, which is called
     /// by libpanic_unwind to delegate the actual unwinding process to Miri.
-    fn handle_miri_start_unwind(
-        &mut self,
-        abi: Abi,
-        link_name: Symbol,
-        args: &[OpTy<'tcx, Provenance>],
-        unwind: mir::UnwindAction,
-    ) -> InterpResult<'tcx> {
+    fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
         trace!("miri_start_unwind: {:?}", this.frame().instance);
 
-        // Get the raw pointer stored in arg[0] (the panic payload).
-        let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?;
         let payload = this.read_scalar(payload)?;
         let thread = this.active_thread_mut();
         thread.panic_payloads.push(payload);
 
-        // Jump to the unwind block to begin unwinding.
-        this.unwind_to_block(unwind)?;
         Ok(())
     }
 
diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs
new file mode 100644
index 00000000000..590a5672f15
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs
@@ -0,0 +1,32 @@
+use rustc_span::Symbol;
+use rustc_target::spec::abi::Abi;
+
+use crate::*;
+
+pub fn is_dyn_sym(_name: &str) -> bool {
+    false
+}
+
+impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
+pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
+    fn emulate_foreign_item_inner(
+        &mut self,
+        link_name: Symbol,
+        abi: Abi,
+        args: &[OpTy<'tcx, Provenance>],
+        dest: &MPlaceTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, EmulateItemResult> {
+        let this = self.eval_context_mut();
+        match link_name.as_str() {
+            // Miscellaneous
+            "__errno" => {
+                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let errno_place = this.last_error_place()?;
+                this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
+            }
+
+            _ => return Ok(EmulateItemResult::NotSupported),
+        }
+        Ok(EmulateItemResult::NeedsReturn)
+    }
+}
diff --git a/src/tools/miri/src/shims/unix/android/mod.rs b/src/tools/miri/src/shims/unix/android/mod.rs
new file mode 100644
index 00000000000..09c6507b24f
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/android/mod.rs
@@ -0,0 +1 @@
+pub mod foreign_items;
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs
index a53cd607ef0..8159960f826 100644
--- a/src/tools/miri/src/shims/unix/fd.rs
+++ b/src/tools/miri/src/shims/unix/fd.rs
@@ -7,7 +7,6 @@ use std::collections::BTreeMap;
 use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write};
 use std::rc::Rc;
 
-use rustc_middle::ty::TyCtxt;
 use rustc_target::abi::Size;
 
 use crate::shims::unix::*;
@@ -22,7 +21,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
         &mut self,
         _communicate_allowed: bool,
         _bytes: &mut [u8],
-        _tcx: TyCtxt<'tcx>,
+        _ecx: &mut MiriInterpCx<'_, 'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         throw_unsup_format!("cannot read from {}", self.name());
     }
@@ -32,7 +31,7 @@ pub trait FileDescription: std::fmt::Debug + Any {
         &mut self,
         _communicate_allowed: bool,
         _bytes: &[u8],
-        _tcx: TyCtxt<'tcx>,
+        _ecx: &mut MiriInterpCx<'_, 'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         throw_unsup_format!("cannot write to {}", self.name());
     }
@@ -82,7 +81,7 @@ impl FileDescription for io::Stdin {
         &mut self,
         communicate_allowed: bool,
         bytes: &mut [u8],
-        _tcx: TyCtxt<'tcx>,
+        _ecx: &mut MiriInterpCx<'_, 'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         if !communicate_allowed {
             // We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
@@ -105,7 +104,7 @@ impl FileDescription for io::Stdout {
         &mut self,
         _communicate_allowed: bool,
         bytes: &[u8],
-        _tcx: TyCtxt<'tcx>,
+        _ecx: &mut MiriInterpCx<'_, 'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         // We allow writing to stderr even with isolation enabled.
         let result = Write::write(self, bytes);
@@ -133,7 +132,7 @@ impl FileDescription for io::Stderr {
         &mut self,
         _communicate_allowed: bool,
         bytes: &[u8],
-        _tcx: TyCtxt<'tcx>,
+        _ecx: &mut MiriInterpCx<'_, 'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         // We allow writing to stderr even with isolation enabled.
         // No need to flush, stderr is not buffered.
@@ -158,7 +157,7 @@ impl FileDescription for NullOutput {
         &mut self,
         _communicate_allowed: bool,
         bytes: &[u8],
-        _tcx: TyCtxt<'tcx>,
+        _ecx: &mut MiriInterpCx<'_, 'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         // We just don't write anything, but report to the user that we did.
         Ok(Ok(bytes.len()))
@@ -173,6 +172,14 @@ impl FileDescriptor {
         FileDescriptor(Rc::new(RefCell::new(Box::new(fd))))
     }
 
+    pub fn borrow(&self) -> Ref<'_, dyn FileDescription> {
+        Ref::map(self.0.borrow(), |fd| fd.as_ref())
+    }
+
+    pub fn borrow_mut(&self) -> RefMut<'_, dyn FileDescription> {
+        RefMut::map(self.0.borrow_mut(), |fd| fd.as_mut())
+    }
+
     pub fn close<'ctx>(self, communicate_allowed: bool) -> InterpResult<'ctx, io::Result<()>> {
         // Destroy this `Rc` using `into_inner` so we can call `close` instead of
         // implicitly running the destructor of the file description.
@@ -242,12 +249,12 @@ impl FdTable {
 
     pub fn get(&self, fd: i32) -> Option<Ref<'_, dyn FileDescription>> {
         let fd = self.fds.get(&fd)?;
-        Some(Ref::map(fd.0.borrow(), |fd| fd.as_ref()))
+        Some(fd.borrow())
     }
 
     pub fn get_mut(&self, fd: i32) -> Option<RefMut<'_, dyn FileDescription>> {
         let fd = self.fds.get(&fd)?;
-        Some(RefMut::map(fd.0.borrow_mut(), |fd| fd.as_mut()))
+        Some(fd.borrow_mut())
     }
 
     pub fn dup(&self, fd: i32) -> Option<FileDescriptor> {
@@ -370,7 +377,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             .min(u64::try_from(isize::MAX).unwrap());
         let communicate = this.machine.communicate();
 
-        let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
+        // We temporarily dup the FD to be able to retain mutable access to `this`.
+        let Some(file_descriptor) = this.machine.fds.dup(fd) else {
             trace!("read: FD not found");
             return this.fd_not_found();
         };
@@ -383,7 +391,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         // `File::read` never returns a value larger than `count`,
         // so this cannot fail.
         let result = file_descriptor
-            .read(communicate, &mut bytes, *this.tcx)?
+            .borrow_mut()
+            .read(communicate, &mut bytes, this)?
             .map(|c| i64::try_from(c).unwrap());
         drop(file_descriptor);
 
@@ -421,12 +430,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let communicate = this.machine.communicate();
 
         let bytes = this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(count))?.to_owned();
-        let Some(mut file_descriptor) = this.machine.fds.get_mut(fd) else {
+        // We temporarily dup the FD to be able to retain mutable access to `this`.
+        let Some(file_descriptor) = this.machine.fds.dup(fd) else {
             return this.fd_not_found();
         };
 
         let result = file_descriptor
-            .write(communicate, &bytes, *this.tcx)?
+            .borrow_mut()
+            .write(communicate, &bytes, this)?
             .map(|c| i64::try_from(c).unwrap());
         drop(file_descriptor);
 
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 08f64e499b5..86dd23f31c7 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -3,13 +3,13 @@ use std::str;
 
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_span::Symbol;
-use rustc_target::abi::{Align, Size};
 use rustc_target::spec::abi::Abi;
 
 use crate::shims::alloc::EvalContextExt as _;
 use crate::shims::unix::*;
 use crate::*;
 
+use shims::unix::android::foreign_items as android;
 use shims::unix::freebsd::foreign_items as freebsd;
 use shims::unix::linux::foreign_items as linux;
 use shims::unix::macos::foreign_items as macos;
@@ -27,6 +27,7 @@ pub fn is_dyn_sym(name: &str, target_os: &str) -> bool {
         // Give specific OSes a chance to allow their symbols.
         _ =>
             match target_os {
+                "android" => android::is_dyn_sym(name),
                 "freebsd" => freebsd::is_dyn_sym(name),
                 "linux" => linux::is_dyn_sym(name),
                 "macos" => macos::is_dyn_sym(name),
@@ -249,28 +250,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
             // Allocation
             "posix_memalign" => {
-                let [ret, align, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-                let ret = this.deref_pointer(ret)?;
-                let align = this.read_target_usize(align)?;
-                let size = this.read_target_usize(size)?;
-                // Align must be power of 2, and also at least ptr-sized (POSIX rules).
-                // But failure to adhere to this is not UB, it's an error condition.
-                if !align.is_power_of_two() || align < this.pointer_size().bytes() {
-                    let einval = this.eval_libc_i32("EINVAL");
-                    this.write_int(einval, dest)?;
-                } else {
-                    if size == 0 {
-                        this.write_null(&ret)?;
-                    } else {
-                        let ptr = this.allocate_ptr(
-                            Size::from_bytes(size),
-                            Align::from_bytes(align).unwrap(),
-                            MiriMemoryKind::C.into(),
-                        )?;
-                        this.write_pointer(ptr, &ret)?;
-                    }
-                    this.write_null(dest)?;
-                }
+                let [memptr, align, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let result = this.posix_memalign(memptr, align, size)?;
+                this.write_scalar(result, dest)?;
             }
 
             "mmap" => {
@@ -287,7 +269,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
             "reallocarray" => {
                 // Currently this function does not exist on all Unixes, e.g. on macOS.
-                if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd") {
+                if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") {
                     throw_unsup_format!(
                         "`reallocarray` is not supported on {}",
                         this.tcx.sess.target.os
@@ -315,6 +297,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                     }
                 }
             }
+            "aligned_alloc" => {
+                // This is a C11 function, we assume all Unixes have it.
+                // (MSVC explicitly does not support this.)
+                let [align, size] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let res = this.aligned_alloc(align, size)?;
+                this.write_pointer(res, dest)?;
+            }
 
             // Dynamic symbol loading
             "dlsym" => {
@@ -605,7 +595,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             "getentropy" => {
                 // This function is non-standard but exists with the same signature and behavior on
                 // Linux, macOS, FreeBSD and Solaris/Illumos.
-                if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd" | "illumos" | "solaris") {
+                if !matches!(&*this.tcx.sess.target.os, "linux" | "macos" | "freebsd" | "illumos" | "solaris" | "android") {
                     throw_unsup_format!(
                         "`getentropy` is not supported on {}",
                         this.tcx.sess.target.os
@@ -634,9 +624,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             "getrandom" => {
                 // This function is non-standard but exists with the same signature and behavior on
                 // Linux, FreeBSD and Solaris/Illumos.
-                if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris") {
+                if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android") {
                     throw_unsup_format!(
-                        "`getentropy` is not supported on {}",
+                        "`getrandom` is not supported on {}",
                         this.tcx.sess.target.os
                     );
                 }
@@ -649,6 +639,31 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 this.gen_random(ptr, len)?;
                 this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
             }
+            "_Unwind_RaiseException" => {
+                // This is not formally part of POSIX, but it is very wide-spread on POSIX systems.
+                // It was originally specified as part of the Itanium C++ ABI:
+                // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw.
+                // On Linux it is
+                // documented as part of the LSB:
+                // https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib--unwind-raiseexception.html
+                // Basically every other UNIX uses the exact same api though. Arm also references
+                // back to the Itanium C++ ABI for the definition of `_Unwind_RaiseException` for
+                // arm64:
+                // https://github.com/ARM-software/abi-aa/blob/main/cppabi64/cppabi64.rst#toc-entry-35
+                // For arm32 they did something custom, but similar enough that the same
+                // `_Unwind_RaiseException` impl in miri should work:
+                // https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst
+                if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android" | "macos") {
+                    throw_unsup_format!(
+                        "`_Unwind_RaiseException` is not supported on {}",
+                        this.tcx.sess.target.os
+                    );
+                }
+                // This function looks and behaves excatly like miri_start_unwind.
+                let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?;
+                this.handle_miri_start_unwind(payload)?;
+                return Ok(EmulateItemResult::NeedsUnwind);
+            }
 
             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
             // These shims are enabled only when the caller is in the standard library.
@@ -718,8 +733,9 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 this.write_int(super::UID, dest)?;
             }
 
-            "getpwuid_r"
+            "getpwuid_r" | "__posix_getpwuid_r"
             if this.frame_in_std() => {
+                // getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish
                 let [uid, pwd, buf, buflen, result] =
                     this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
                 this.check_no_isolation("`getpwuid_r`")?;
@@ -759,6 +775,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             _ => {
                 let target_os = &*this.tcx.sess.target.os;
                 return match target_os {
+                    "android" => android::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
                     "freebsd" => freebsd::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
                     "linux" => linux::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
                     "macos" => macos::EvalContextExt::emulate_foreign_item_inner(this, link_name, abi, args, dest),
@@ -768,6 +785,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             }
         };
 
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
index e70cd35dda6..8a7f7e9d1fd 100644
--- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
@@ -86,6 +86,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index eb241556a56..82bc49536b0 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -9,7 +9,6 @@ use std::path::{Path, PathBuf};
 use std::time::SystemTime;
 
 use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::ty::TyCtxt;
 use rustc_target::abi::Size;
 
 use crate::shims::os_str::bytes_to_os_str;
@@ -34,7 +33,7 @@ impl FileDescription for FileHandle {
         &mut self,
         communicate_allowed: bool,
         bytes: &mut [u8],
-        _tcx: TyCtxt<'tcx>,
+        _ecx: &mut MiriInterpCx<'_, 'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         assert!(communicate_allowed, "isolation should have prevented even opening a file");
         Ok(self.file.read(bytes))
@@ -44,7 +43,7 @@ impl FileDescription for FileHandle {
         &mut self,
         communicate_allowed: bool,
         bytes: &[u8],
-        _tcx: TyCtxt<'tcx>,
+        _ecx: &mut MiriInterpCx<'_, 'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         assert!(communicate_allowed, "isolation should have prevented even opening a file");
         Ok(self.file.write(bytes))
diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs
index a865f2efff9..f31c2bb84ae 100644
--- a/src/tools/miri/src/shims/unix/linux/eventfd.rs
+++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs
@@ -2,7 +2,6 @@
 //! Currently just a stub.
 use std::io;
 
-use rustc_middle::ty::TyCtxt;
 use rustc_target::abi::Endian;
 
 use crate::shims::unix::*;
@@ -52,11 +51,11 @@ impl FileDescription for Event {
         &mut self,
         _communicate_allowed: bool,
         bytes: &[u8],
-        tcx: TyCtxt<'tcx>,
+        ecx: &mut MiriInterpCx<'_, 'tcx>,
     ) -> InterpResult<'tcx, io::Result<usize>> {
         let bytes: [u8; 8] = bytes.try_into().unwrap(); // FIXME fail gracefully when this has the wrong size
         // Convert from target endianness to host endianness.
-        let num = match tcx.sess.target.endian {
+        let num = match ecx.tcx.sess.target.endian {
             Endian::Little => u64::from_le_bytes(bytes),
             Endian::Big => u64::from_be_bytes(bytes),
         };
diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
index 7cd749a4107..b2666101ff2 100644
--- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
@@ -203,6 +203,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             _ => return Ok(EmulateItemResult::NotSupported),
         };
 
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
index 912623b7225..2b9ce746a56 100644
--- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
@@ -177,6 +177,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             _ => return Ok(EmulateItemResult::NotSupported),
         };
 
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs
index 0254735ac13..5f454e7799e 100644
--- a/src/tools/miri/src/shims/unix/mem.rs
+++ b/src/tools/miri/src/shims/unix/mem.rs
@@ -11,7 +11,7 @@
 //! calls to munmap, but for a very different reason. In principle, according to the man pages, it
 //! is possible to unmap arbitrary regions of address space. But in a high-level language like Rust
 //! this amounts to partial deallocation, which LLVM does not support. So any attempt to call our
-//! munmap shim which would partily unmap a region of address space previously mapped by mmap will
+//! munmap shim which would partially unmap a region of address space previously mapped by mmap will
 //! report UB.
 
 use crate::*;
@@ -78,7 +78,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         // * The implementation does not support the combination of accesses requested in the
         // prot argument.
         //
-        // Miri doesn't support MAP_FIXED or any any protections other than PROT_READ|PROT_WRITE.
+        // Miri doesn't support MAP_FIXED or any protections other than PROT_READ|PROT_WRITE.
         if flags & map_fixed != 0 || prot != prot_read | prot_write {
             this.set_last_error(this.eval_libc("ENOTSUP"))?;
             return Ok(this.eval_libc("MAP_FAILED"));
diff --git a/src/tools/miri/src/shims/unix/mod.rs b/src/tools/miri/src/shims/unix/mod.rs
index 6dee30d895c..dc9068fddde 100644
--- a/src/tools/miri/src/shims/unix/mod.rs
+++ b/src/tools/miri/src/shims/unix/mod.rs
@@ -8,6 +8,7 @@ mod socket;
 mod sync;
 mod thread;
 
+mod android;
 mod freebsd;
 mod linux;
 mod macos;
diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
index c216d8afd77..6c5155618c9 100644
--- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
@@ -45,6 +45,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs
index f24f279ab0f..0ba03d4ab78 100644
--- a/src/tools/miri/src/shims/unix/sync.rs
+++ b/src/tools/miri/src/shims/unix/sync.rs
@@ -390,7 +390,7 @@ fn reacquire_cond_mutex<'mir, 'tcx: 'mir>(
     Ok(())
 }
 
-/// After a thread waiting on a condvar was signalled:
+/// After a thread waiting on a condvar was signaled:
 /// Reacquire the conditional variable and remove the timeout callback if any
 /// was registered.
 fn post_cond_signal<'mir, 'tcx: 'mir>(
diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs
new file mode 100644
index 00000000000..774a5e72025
--- /dev/null
+++ b/src/tools/miri/src/shims/wasi/foreign_items.rs
@@ -0,0 +1,40 @@
+use rustc_span::Symbol;
+use rustc_target::spec::abi::Abi;
+
+use crate::shims::alloc::EvalContextExt as _;
+use crate::*;
+
+pub fn is_dyn_sym(_name: &str) -> bool {
+    false
+}
+
+impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
+pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
+    fn emulate_foreign_item_inner(
+        &mut self,
+        link_name: Symbol,
+        abi: Abi,
+        args: &[OpTy<'tcx, Provenance>],
+        dest: &MPlaceTy<'tcx, Provenance>,
+    ) -> InterpResult<'tcx, EmulateItemResult> {
+        let this = self.eval_context_mut();
+        match link_name.as_str() {
+            // Allocation
+            "posix_memalign" => {
+                let [memptr, align, size] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let result = this.posix_memalign(memptr, align, size)?;
+                this.write_scalar(result, dest)?;
+            }
+            "aligned_alloc" => {
+                let [align, size] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let res = this.aligned_alloc(align, size)?;
+                this.write_pointer(res, dest)?;
+            }
+
+            _ => return Ok(EmulateItemResult::NotSupported),
+        }
+        Ok(EmulateItemResult::NeedsReturn)
+    }
+}
diff --git a/src/tools/miri/src/shims/wasi/mod.rs b/src/tools/miri/src/shims/wasi/mod.rs
new file mode 100644
index 00000000000..09c6507b24f
--- /dev/null
+++ b/src/tools/miri/src/shims/wasi/mod.rs
@@ -0,0 +1 @@
+pub mod foreign_items;
diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs
index 086abf19c5c..91def80227d 100644
--- a/src/tools/miri/src/shims/windows/foreign_items.rs
+++ b/src/tools/miri/src/shims/windows/foreign_items.rs
@@ -762,6 +762,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
             _ => return Ok(EmulateItemResult::NotSupported),
         }
 
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs
index 8dc3748a12d..3a66c431506 100644
--- a/src/tools/miri/src/shims/x86/aesni.rs
+++ b/src/tools/miri/src/shims/x86/aesni.rs
@@ -127,7 +127,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
             // with an external crate.
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
 
diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs
index 86ef180a448..b1c61c8b3b2 100644
--- a/src/tools/miri/src/shims/x86/avx.rs
+++ b/src/tools/miri/src/shims/x86/avx.rs
@@ -344,6 +344,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
             }
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs
index bbde5b49588..e0bd2298ab8 100644
--- a/src/tools/miri/src/shims/x86/avx2.rs
+++ b/src/tools/miri/src/shims/x86/avx2.rs
@@ -81,7 +81,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
 
                 let scale = this.read_scalar(scale)?.to_i8()?;
                 if !matches!(scale, 1 | 2 | 4 | 8) {
-                    throw_unsup_format!("invalid gather scale {scale}");
+                    panic!("invalid gather scale {scale}");
                 }
                 let scale = i64::from(scale);
 
@@ -440,6 +440,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
             }
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs
index e519fa5508d..48b7222917b 100644
--- a/src/tools/miri/src/shims/x86/mod.rs
+++ b/src/tools/miri/src/shims/x86/mod.rs
@@ -144,7 +144,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
 
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
 
@@ -200,7 +200,7 @@ impl FloatBinOp {
     ) -> InterpResult<'tcx, Self> {
         // Only bits 0..=4 are used, remaining should be zero.
         if imm & !0b1_1111 != 0 {
-            throw_unsup_format!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}");
+            panic!("invalid `imm` parameter of {intrinsic}: 0x{imm:x}");
         }
         // Bit 4 specifies whether the operation is quiet or signaling, which
         // we do not care in Miri.
@@ -683,7 +683,7 @@ fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::R
         // SSE status register. Since we do not support modifying it from
         // Miri (or Rust), we assume it to be at its default mode (round-to-nearest).
         0b100..=0b111 => Ok(rustc_apfloat::Round::NearestTiesToEven),
-        rounding => throw_unsup_format!("unsupported rounding mode 0x{rounding:02x}"),
+        rounding => panic!("invalid rounding mode 0x{rounding:02x}"),
     }
 }
 
@@ -757,7 +757,7 @@ fn int_abs<'tcx>(
     Ok(())
 }
 
-/// Splits `op` (which must be a SIMD vector) into 128-bit chuncks.
+/// Splits `op` (which must be a SIMD vector) into 128-bit chunks.
 ///
 /// Returns a tuple where:
 /// * The first element is the number of 128-bit chunks (let's call it `N`).
@@ -788,7 +788,7 @@ fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>(
     Ok((num_chunks, items_per_chunk, chunked_op))
 }
 
-/// Horizontaly performs `which` operation on adjacent values of
+/// Horizontally performs `which` operation on adjacent values of
 /// `left` and `right` SIMD vectors and stores the result in `dest`.
 /// "Horizontal" means that the i-th output element is calculated
 /// from the elements 2*i and 2*i+1 of the concatenation of `left` and
@@ -1256,7 +1256,7 @@ fn packusdw<'tcx>(
 
 /// Negates elements from `left` when the corresponding element in
 /// `right` is negative. If an element from `right` is zero, zero
-/// is writen to the corresponding output element.
+/// is written to the corresponding output element.
 /// In other words, multiplies `left` with `right.signum()`.
 fn psign<'tcx>(
     this: &mut crate::MiriInterpCx<'_, 'tcx>,
diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs
index ea967a8cf72..3636fb2f3fb 100644
--- a/src/tools/miri/src/shims/x86/sse.rs
+++ b/src/tools/miri/src/shims/x86/sse.rs
@@ -212,6 +212,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
             }
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs
index 31fa66a496f..54d1e0c803b 100644
--- a/src/tools/miri/src/shims/x86/sse2.rs
+++ b/src/tools/miri/src/shims/x86/sse2.rs
@@ -388,6 +388,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
             }
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/x86/sse3.rs b/src/tools/miri/src/shims/x86/sse3.rs
index 8de339eb9e5..fa1dd07e90b 100644
--- a/src/tools/miri/src/shims/x86/sse3.rs
+++ b/src/tools/miri/src/shims/x86/sse3.rs
@@ -51,6 +51,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
             }
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs
index 011a7a16c68..cd82108678d 100644
--- a/src/tools/miri/src/shims/x86/sse41.rs
+++ b/src/tools/miri/src/shims/x86/sse41.rs
@@ -176,6 +176,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
             }
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs
index d30de430088..ec625da68c2 100644
--- a/src/tools/miri/src/shims/x86/ssse3.rs
+++ b/src/tools/miri/src/shims/x86/ssse3.rs
@@ -137,6 +137,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>:
             }
             _ => return Ok(EmulateItemResult::NotSupported),
         }
-        Ok(EmulateItemResult::NeedsJumping)
+        Ok(EmulateItemResult::NeedsReturn)
     }
 }
diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml
index 1894f53ce49..e40dd50a444 100644
--- a/src/tools/miri/test_dependencies/Cargo.toml
+++ b/src/tools/miri/test_dependencies/Cargo.toml
@@ -11,12 +11,12 @@ edition = "2021"
 # all dependencies (and their transitive ones) listed here can be used in `tests/`.
 libc = "0.2"
 num_cpus = "1.10.1"
-tempfile = "3"
 
 getrandom_01 = { package = "getrandom", version = "0.1" }
 getrandom_02 = { package = "getrandom", version = "0.2", features = ["js"] }
 
 [target.'cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))'.dependencies]
+tempfile = "3"
 page_size = "0.6"
 tokio = { version = "1.24", features = ["macros", "rt-multi-thread", "time", "net"] }
 
diff --git a/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs
new file mode 100644
index 00000000000..9a33cdccd27
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.rs
@@ -0,0 +1,15 @@
+//@ignore-target-windows: Windows does not support the standard C11 aligned_alloc.
+
+fn main() {
+    // libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689),
+    // so we declare it ourselves.
+    extern "C" {
+        fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
+    }
+
+    // Make sure even zero-sized allocations need to be freed.
+
+    unsafe {
+        aligned_alloc(2, 0); //~ERROR: memory leaked
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr
new file mode 100644
index 00000000000..b0756d57212
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/aligned_alloc_size_zero_leak.stderr
@@ -0,0 +1,15 @@
+error: memory leaked: ALLOC (C heap, size: 0, align: 2), allocated here:
+  --> $DIR/aligned_alloc_size_zero_leak.rs:LL:CC
+   |
+LL |         aligned_alloc(2, 0);
+   |         ^^^^^^^^^^^^^^^^^^^
+   |
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/aligned_alloc_size_zero_leak.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs
new file mode 100644
index 00000000000..b6b7b007f2b
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.rs
@@ -0,0 +1,14 @@
+//@ignore-target-windows: No posix_memalign on Windows
+
+use std::ptr;
+
+fn main() {
+    let mut ptr: *mut libc::c_void = ptr::null_mut();
+    let align = 64;
+    let size = 0;
+    unsafe {
+        let _ = libc::posix_memalign(&mut ptr, align, size);
+        libc::free(ptr);
+        libc::free(ptr); //~ERROR: dangling
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr
new file mode 100644
index 00000000000..3ed117c5a0a
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_double_free.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling
+  --> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC
+   |
+LL |         libc::free(ptr);
+   |         ^^^^^^^^^^^^^^^ memory access failed: ALLOC has been freed, so this pointer is dangling
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+help: ALLOC was allocated here:
+  --> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC
+   |
+LL |         let _ = libc::posix_memalign(&mut ptr, align, size);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: ALLOC was deallocated here:
+  --> $DIR/posix_memalign_size_zero_double_free.rs:LL:CC
+   |
+LL |         libc::free(ptr);
+   |         ^^^^^^^^^^^^^^^
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at $DIR/posix_memalign_size_zero_double_free.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/posix_memalign_size_zero_leak.rs b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.rs
new file mode 100644
index 00000000000..1a4c9605fe0
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.rs
@@ -0,0 +1,10 @@
+//@ignore-target-windows: No posix_memalign on Windows
+
+use std::ptr;
+
+fn main() {
+    let mut ptr: *mut libc::c_void = ptr::null_mut();
+    let align = 64;
+    let size = 0;
+    let _ = unsafe { libc::posix_memalign(&mut ptr, align, size) }; //~ERROR: memory leak
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr
new file mode 100644
index 00000000000..7ea0fa31469
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/posix_memalign_size_zero_leak.stderr
@@ -0,0 +1,15 @@
+error: memory leaked: ALLOC (C heap, size: 0, align: 64), allocated here:
+  --> $DIR/posix_memalign_size_zero_leak.rs:LL:CC
+   |
+LL |     let _ = unsafe { libc::posix_memalign(&mut ptr, align, size) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/posix_memalign_size_zero_leak.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+note: the evaluated program leaked memory, pass `-Zmiri-ignore-leaks` to disable this check
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/environ-gets-deallocated.rs b/src/tools/miri/tests/fail/environ-gets-deallocated.rs
index 6dcf1fdb1c7..5391a9176d0 100644
--- a/src/tools/miri/tests/fail/environ-gets-deallocated.rs
+++ b/src/tools/miri/tests/fail/environ-gets-deallocated.rs
@@ -1,6 +1,5 @@
 //@ignore-target-windows: Windows does not have a global environ list that the program can access directly
 
-#[cfg(any(target_os = "linux", target_os = "freebsd"))]
 fn get_environ() -> *const *const u8 {
     extern "C" {
         static mut environ: *const *const u8;
@@ -8,14 +7,6 @@ fn get_environ() -> *const *const u8 {
     unsafe { environ }
 }
 
-#[cfg(target_os = "macos")]
-fn get_environ() -> *const *const u8 {
-    extern "C" {
-        fn _NSGetEnviron() -> *mut *const *const u8;
-    }
-    unsafe { *_NSGetEnviron() }
-}
-
 fn main() {
     let pointer = get_environ();
     let _x = unsafe { *pointer };
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
index 5df3ace7496..aa383a99bc2 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
@@ -148,54 +148,55 @@ fn test_calloc() {
 
 #[cfg(not(target_os = "windows"))]
 fn test_memalign() {
-    // A normal allocation.
-    unsafe {
-        let mut ptr: *mut libc::c_void = ptr::null_mut();
-        let align = 8;
-        let size = 64;
-        assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
-        assert!(!ptr.is_null());
-        assert!(ptr.is_aligned_to(align));
-        ptr.cast::<u8>().write_bytes(1, size);
-        libc::free(ptr);
-    }
+    for _ in 0..16 {
+        // A normal allocation.
+        unsafe {
+            let mut ptr: *mut libc::c_void = ptr::null_mut();
+            let align = 8;
+            let size = 64;
+            assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
+            assert!(!ptr.is_null());
+            assert!(ptr.is_aligned_to(align));
+            ptr.cast::<u8>().write_bytes(1, size);
+            libc::free(ptr);
+        }
 
-    // Align > size.
-    unsafe {
-        let mut ptr: *mut libc::c_void = ptr::null_mut();
-        let align = 64;
-        let size = 8;
-        assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
-        assert!(!ptr.is_null());
-        assert!(ptr.is_aligned_to(align));
-        ptr.cast::<u8>().write_bytes(1, size);
-        libc::free(ptr);
-    }
+        // Align > size.
+        unsafe {
+            let mut ptr: *mut libc::c_void = ptr::null_mut();
+            let align = 64;
+            let size = 8;
+            assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
+            assert!(!ptr.is_null());
+            assert!(ptr.is_aligned_to(align));
+            ptr.cast::<u8>().write_bytes(1, size);
+            libc::free(ptr);
+        }
 
-    // Size not multiple of align
-    unsafe {
-        let mut ptr: *mut libc::c_void = ptr::null_mut();
-        let align = 16;
-        let size = 31;
-        assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
-        assert!(!ptr.is_null());
-        assert!(ptr.is_aligned_to(align));
-        ptr.cast::<u8>().write_bytes(1, size);
-        libc::free(ptr);
-    }
+        // Size not multiple of align
+        unsafe {
+            let mut ptr: *mut libc::c_void = ptr::null_mut();
+            let align = 16;
+            let size = 31;
+            assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
+            assert!(!ptr.is_null());
+            assert!(ptr.is_aligned_to(align));
+            ptr.cast::<u8>().write_bytes(1, size);
+            libc::free(ptr);
+        }
 
-    // Size == 0
-    unsafe {
-        let mut ptr: *mut libc::c_void = ptr::null_mut();
-        let align = 64;
-        let size = 0;
-        assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
-        // We are not required to return null if size == 0, but we currently do.
-        // It's fine to remove this assert if we start returning non-null pointers.
-        assert!(ptr.is_null());
-        assert!(ptr.is_aligned_to(align));
-        // Regardless of what we return, it must be `free`able.
-        libc::free(ptr);
+        // Size == 0
+        unsafe {
+            let mut ptr: *mut libc::c_void = ptr::null_mut();
+            let align = 64;
+            let size = 0;
+            assert_eq!(libc::posix_memalign(&mut ptr, align, size), 0);
+            // Non-null pointer is returned if size == 0.
+            // (This is not a guarantee, it just reflects our current behavior.)
+            assert!(!ptr.is_null());
+            assert!(ptr.is_aligned_to(align));
+            libc::free(ptr);
+        }
     }
 
     // Non-power of 2 align
@@ -227,7 +228,8 @@ fn test_memalign() {
     target_os = "windows",
     target_os = "macos",
     target_os = "illumos",
-    target_os = "solaris"
+    target_os = "solaris",
+    target_os = "wasi",
 )))]
 fn test_reallocarray() {
     unsafe {
@@ -241,6 +243,44 @@ fn test_reallocarray() {
     }
 }
 
+#[cfg(not(target_os = "windows"))]
+fn test_aligned_alloc() {
+    // libc doesn't have this function (https://github.com/rust-lang/libc/issues/3689),
+    // so we declare it ourselves.
+    extern "C" {
+        fn aligned_alloc(alignment: libc::size_t, size: libc::size_t) -> *mut libc::c_void;
+    }
+    // size not a multiple of the alignment
+    unsafe {
+        let p = aligned_alloc(16, 3);
+        assert_eq!(p, ptr::null_mut());
+    }
+
+    // alignment not power of 2
+    unsafe {
+        let p = aligned_alloc(63, 8);
+        assert_eq!(p, ptr::null_mut());
+    }
+
+    // repeated tests on correct alignment/size
+    for _ in 0..16 {
+        // alignment 1, size 4 should succeed and actually must align to 4 (because C says so...)
+        unsafe {
+            let p = aligned_alloc(1, 4);
+            assert!(!p.is_null());
+            assert!(p.is_aligned_to(4));
+            libc::free(p);
+        }
+
+        unsafe {
+            let p = aligned_alloc(64, 64);
+            assert!(!p.is_null());
+            assert!(p.is_aligned_to(64));
+            libc::free(p);
+        }
+    }
+}
+
 fn main() {
     test_malloc();
     test_calloc();
@@ -250,9 +290,12 @@ fn main() {
         target_os = "windows",
         target_os = "macos",
         target_os = "illumos",
-        target_os = "solaris"
+        target_os = "solaris",
+        target_os = "wasi",
     )))]
     test_reallocarray();
+    #[cfg(not(target_os = "windows"))]
+    test_aligned_alloc();
 
     test_memcpy();
     test_strcpy();
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
index 736e0bf8eb7..f7e1d9faa6a 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs
@@ -10,9 +10,11 @@ use std::mem::transmute;
 fn test_thread_local_errno() {
     #[cfg(any(target_os = "illumos", target_os = "solaris"))]
     use libc::___errno as __errno_location;
+    #[cfg(target_os = "android")]
+    use libc::__errno as __errno_location;
     #[cfg(target_os = "linux")]
     use libc::__errno_location;
-    #[cfg(any(target_os = "macos", target_os = "freebsd"))]
+    #[cfg(any(target_os = "freebsd", target_os = "macos"))]
     use libc::__error as __errno_location;
 
     unsafe {
@@ -28,6 +30,21 @@ fn test_thread_local_errno() {
     }
 }
 
+fn test_environ() {
+    // Just a smoke test for now, checking that the extern static exists.
+    extern "C" {
+        static mut environ: *const *const libc::c_char;
+    }
+
+    unsafe {
+        let mut e = environ;
+        // Iterate to the end.
+        while !(*e).is_null() {
+            e = e.add(1);
+        }
+    }
+}
+
 #[cfg(target_os = "linux")]
 fn test_sigrt() {
     let min = libc::SIGRTMIN();
@@ -60,6 +77,7 @@ fn test_dlsym() {
 
 fn main() {
     test_thread_local_errno();
+    test_environ();
 
     test_dlsym();
 
diff --git a/src/tools/miri/tests/pass/empty_main.rs b/src/tools/miri/tests/pass/empty_main.rs
new file mode 100644
index 00000000000..d081b6db670
--- /dev/null
+++ b/src/tools/miri/tests/pass/empty_main.rs
@@ -0,0 +1,3 @@
+// This may look trivial, but a bunch of code runs in std before
+// `main` is called, so we are ensuring that that all works.
+fn main() {}
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 1bb44d56bf6..8aea9b3e6f9 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -1,5 +1,6 @@
 #![feature(stmt_expr_attributes)]
 #![feature(float_gamma)]
+#![feature(core_intrinsics)]
 #![allow(arithmetic_overflow)]
 
 use std::fmt::Debug;
@@ -22,6 +23,8 @@ fn main() {
     rounding();
     mul_add();
     libm();
+    test_fast();
+    test_algebraic();
 }
 
 // Helper function to avoid promotion so that this tests "run-time" casts, not CTFE.
@@ -751,3 +754,67 @@ pub fn libm() {
     assert_approx_eq!(val, (2.0 * f64::consts::PI.sqrt()).ln());
     assert_eq!(sign, -1);
 }
+
+fn test_fast() {
+    use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast};
+
+    #[inline(never)]
+    pub fn test_operations_f64(a: f64, b: f64) {
+        // make sure they all map to the correct operation
+        unsafe {
+            assert_eq!(fadd_fast(a, b), a + b);
+            assert_eq!(fsub_fast(a, b), a - b);
+            assert_eq!(fmul_fast(a, b), a * b);
+            assert_eq!(fdiv_fast(a, b), a / b);
+            assert_eq!(frem_fast(a, b), a % b);
+        }
+    }
+
+    #[inline(never)]
+    pub fn test_operations_f32(a: f32, b: f32) {
+        // make sure they all map to the correct operation
+        unsafe {
+            assert_eq!(fadd_fast(a, b), a + b);
+            assert_eq!(fsub_fast(a, b), a - b);
+            assert_eq!(fmul_fast(a, b), a * b);
+            assert_eq!(fdiv_fast(a, b), a / b);
+            assert_eq!(frem_fast(a, b), a % b);
+        }
+    }
+
+    test_operations_f64(1., 2.);
+    test_operations_f64(10., 5.);
+    test_operations_f32(11., 2.);
+    test_operations_f32(10., 15.);
+}
+
+fn test_algebraic() {
+    use std::intrinsics::{
+        fadd_algebraic, fdiv_algebraic, fmul_algebraic, frem_algebraic, fsub_algebraic,
+    };
+
+    #[inline(never)]
+    pub fn test_operations_f64(a: f64, b: f64) {
+        // make sure they all map to the correct operation
+        assert_eq!(fadd_algebraic(a, b), a + b);
+        assert_eq!(fsub_algebraic(a, b), a - b);
+        assert_eq!(fmul_algebraic(a, b), a * b);
+        assert_eq!(fdiv_algebraic(a, b), a / b);
+        assert_eq!(frem_algebraic(a, b), a % b);
+    }
+
+    #[inline(never)]
+    pub fn test_operations_f32(a: f32, b: f32) {
+        // make sure they all map to the correct operation
+        assert_eq!(fadd_algebraic(a, b), a + b);
+        assert_eq!(fsub_algebraic(a, b), a - b);
+        assert_eq!(fmul_algebraic(a, b), a * b);
+        assert_eq!(fdiv_algebraic(a, b), a / b);
+        assert_eq!(frem_algebraic(a, b), a % b);
+    }
+
+    test_operations_f64(1., 2.);
+    test_operations_f64(10., 5.);
+    test_operations_f32(11., 2.);
+    test_operations_f32(10., 15.);
+}
diff --git a/src/tools/miri/tests/pass/float_fast_math.rs b/src/tools/miri/tests/pass/float_fast_math.rs
deleted file mode 100644
index 52d985667df..00000000000
--- a/src/tools/miri/tests/pass/float_fast_math.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-#![feature(core_intrinsics)]
-
-use std::intrinsics::{fadd_fast, fdiv_fast, fmul_fast, frem_fast, fsub_fast};
-
-#[inline(never)]
-pub fn test_operations_f64(a: f64, b: f64) {
-    // make sure they all map to the correct operation
-    unsafe {
-        assert_eq!(fadd_fast(a, b), a + b);
-        assert_eq!(fsub_fast(a, b), a - b);
-        assert_eq!(fmul_fast(a, b), a * b);
-        assert_eq!(fdiv_fast(a, b), a / b);
-        assert_eq!(frem_fast(a, b), a % b);
-    }
-}
-
-#[inline(never)]
-pub fn test_operations_f32(a: f32, b: f32) {
-    // make sure they all map to the correct operation
-    unsafe {
-        assert_eq!(fadd_fast(a, b), a + b);
-        assert_eq!(fsub_fast(a, b), a - b);
-        assert_eq!(fmul_fast(a, b), a * b);
-        assert_eq!(fdiv_fast(a, b), a / b);
-        assert_eq!(frem_fast(a, b), a % b);
-    }
-}
-
-fn main() {
-    test_operations_f64(1., 2.);
-    test_operations_f64(10., 5.);
-    test_operations_f32(11., 2.);
-    test_operations_f32(10., 15.);
-}
diff --git a/src/tools/miri/tests/pass/integer-ops.rs b/src/tools/miri/tests/pass/integer-ops.rs
index 0ec1f8e9c69..3f8ac34e7d1 100644
--- a/src/tools/miri/tests/pass/integer-ops.rs
+++ b/src/tools/miri/tests/pass/integer-ops.rs
@@ -1,7 +1,64 @@
 //@compile-flags: -Coverflow-checks=off
 #![allow(arithmetic_overflow)]
 
+fn basic() {
+    fn ret() -> i64 {
+        1
+    }
+
+    fn neg() -> i64 {
+        -1
+    }
+
+    fn add() -> i64 {
+        1 + 2
+    }
+
+    fn indirect_add() -> i64 {
+        let x = 1;
+        let y = 2;
+        x + y
+    }
+
+    fn arith() -> i32 {
+        3 * 3 + 4 * 4
+    }
+
+    fn match_int() -> i16 {
+        let n = 2;
+        match n {
+            0 => 0,
+            1 => 10,
+            2 => 20,
+            3 => 30,
+            _ => 100,
+        }
+    }
+
+    fn match_int_range() -> i64 {
+        let n = 42;
+        match n {
+            0..=9 => 0,
+            10..=19 => 1,
+            20..=29 => 2,
+            30..=39 => 3,
+            40..=42 => 4,
+            _ => 5,
+        }
+    }
+
+    assert_eq!(ret(), 1);
+    assert_eq!(neg(), -1);
+    assert_eq!(add(), 3);
+    assert_eq!(indirect_add(), 3);
+    assert_eq!(arith(), 5 * 5);
+    assert_eq!(match_int(), 20);
+    assert_eq!(match_int_range(), 4);
+}
+
 pub fn main() {
+    basic();
+
     // This tests that we do (not) do sign extension properly when loading integers
     assert_eq!(u32::MAX as i64, 4294967295);
     assert_eq!(i32::MIN as i64, -2147483648);
@@ -152,6 +209,10 @@ pub fn main() {
 
     assert_eq!(5i32.overflowing_mul(2), (10, false));
     assert_eq!(1_000_000_000i32.overflowing_mul(10), (1410065408, true));
+    assert_eq!(i64::MIN.overflowing_mul(-1), (i64::MIN, true));
+    assert_eq!(i32::MIN.overflowing_mul(-1), (i32::MIN, true));
+    assert_eq!(i16::MIN.overflowing_mul(-1), (i16::MIN, true));
+    assert_eq!(i8::MIN.overflowing_mul(-1), (i8::MIN, true));
 
     assert_eq!(5i32.overflowing_div(2), (2, false));
     assert_eq!(i32::MIN.overflowing_div(-1), (i32::MIN, true));
diff --git a/src/tools/miri/tests/pass/integers.rs b/src/tools/miri/tests/pass/integers.rs
deleted file mode 100644
index c04c6921f3c..00000000000
--- a/src/tools/miri/tests/pass/integers.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-fn ret() -> i64 {
-    1
-}
-
-fn neg() -> i64 {
-    -1
-}
-
-fn add() -> i64 {
-    1 + 2
-}
-
-fn indirect_add() -> i64 {
-    let x = 1;
-    let y = 2;
-    x + y
-}
-
-fn arith() -> i32 {
-    3 * 3 + 4 * 4
-}
-
-fn match_int() -> i16 {
-    let n = 2;
-    match n {
-        0 => 0,
-        1 => 10,
-        2 => 20,
-        3 => 30,
-        _ => 100,
-    }
-}
-
-fn match_int_range() -> i64 {
-    let n = 42;
-    match n {
-        0..=9 => 0,
-        10..=19 => 1,
-        20..=29 => 2,
-        30..=39 => 3,
-        40..=42 => 4,
-        _ => 5,
-    }
-}
-
-fn main() {
-    assert_eq!(ret(), 1);
-    assert_eq!(neg(), -1);
-    assert_eq!(add(), 3);
-    assert_eq!(indirect_add(), 3);
-    assert_eq!(arith(), 5 * 5);
-    assert_eq!(match_int(), 20);
-    assert_eq!(match_int_range(), 4);
-    assert_eq!(i64::MIN.overflowing_mul(-1), (i64::MIN, true));
-    assert_eq!(i32::MIN.overflowing_mul(-1), (i32::MIN, true));
-    assert_eq!(i16::MIN.overflowing_mul(-1), (i16::MIN, true));
-    assert_eq!(i8::MIN.overflowing_mul(-1), (i8::MIN, true));
-}
diff --git a/src/tools/miri/tests/pass/panic/unwind_dwarf.rs b/src/tools/miri/tests/pass/panic/unwind_dwarf.rs
new file mode 100644
index 00000000000..f690be471ba
--- /dev/null
+++ b/src/tools/miri/tests/pass/panic/unwind_dwarf.rs
@@ -0,0 +1,96 @@
+//@ignore-target-windows: Windows uses a different unwinding mechanism
+#![feature(core_intrinsics, panic_unwind, rustc_attrs)]
+#![allow(internal_features)]
+
+//! Unwinding using `_Unwind_RaiseException`
+
+extern crate unwind as uw;
+
+use std::any::Any;
+use std::ptr;
+
+#[repr(C)]
+struct Exception {
+    _uwe: uw::_Unwind_Exception,
+    cause: Box<dyn Any + Send>,
+}
+
+pub fn panic(data: Box<dyn Any + Send>) -> u32 {
+    extern "C" fn exception_cleanup(
+        _unwind_code: uw::_Unwind_Reason_Code,
+        _exception: *mut uw::_Unwind_Exception,
+    ) {
+        std::process::abort();
+    }
+
+    let exception = Box::new(Exception {
+        _uwe: uw::_Unwind_Exception {
+            exception_class: miri_exception_class(),
+            exception_cleanup: Some(exception_cleanup),
+            private: [core::ptr::null(); uw::unwinder_private_data_size],
+        },
+        cause: data,
+    });
+    let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception;
+    return unsafe { uw::_Unwind_RaiseException(exception_param) as u32 };
+}
+
+pub unsafe fn rust_panic_cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
+    let exception = ptr as *mut uw::_Unwind_Exception;
+    if (*exception).exception_class != miri_exception_class() {
+        std::process::abort();
+    }
+
+    let exception = exception.cast::<Exception>();
+
+    let exception = Box::from_raw(exception as *mut Exception);
+    exception.cause
+}
+
+fn miri_exception_class() -> uw::_Unwind_Exception_Class {
+    // M O Z \0  M I R I -- vendor, language
+    // (Miri's own exception class is just used for testing)
+    0x4d4f5a_00_4d495249
+}
+
+pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
+    struct Data<F, R> {
+        f: Option<F>,
+        r: Option<R>,
+        p: Option<Box<dyn Any + Send>>,
+    }
+
+    let mut data = Data { f: Some(f), r: None, p: None };
+
+    let data_ptr = ptr::addr_of_mut!(data) as *mut u8;
+    unsafe {
+        return if std::intrinsics::catch_unwind(do_call::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
+            Ok(data.r.take().unwrap())
+        } else {
+            Err(data.p.take().unwrap())
+        };
+    }
+
+    fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
+        unsafe {
+            let data = &mut *data.cast::<Data<F, R>>();
+            let f = data.f.take().unwrap();
+            data.r = Some(f());
+        }
+    }
+
+    #[rustc_nounwind]
+    fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
+        unsafe {
+            let obj = rust_panic_cleanup(payload);
+            (*data.cast::<Data<F, R>>()).p = Some(obj);
+        }
+    }
+}
+
+fn main() {
+    assert_eq!(
+        catch_unwind(|| panic(Box::new(42))).unwrap_err().downcast::<i32>().unwrap(),
+        Box::new(42)
+    );
+}
diff --git a/src/tools/miri/tests/pass/shims/env/home.rs b/src/tools/miri/tests/pass/shims/env/home.rs
index c237f9ed9ff..8b4b907a51d 100644
--- a/src/tools/miri/tests/pass/shims/env/home.rs
+++ b/src/tools/miri/tests/pass/shims/env/home.rs
@@ -2,8 +2,12 @@
 use std::env;
 
 fn main() {
-    env::remove_var("HOME"); // make sure we enter the interesting codepath
-    env::remove_var("USERPROFILE"); // Windows also looks as this env var
+    // Remove the env vars to hit the underlying shim -- except
+    // on android where the env var is all we have.
+    #[cfg(not(target_os = "android"))]
+    env::remove_var("HOME");
+    env::remove_var("USERPROFILE");
+
     #[allow(deprecated)]
     env::home_dir().unwrap();
 }
diff --git a/tests/ui/associated-types/associated-types-eq-2.rs b/tests/ui/associated-types/associated-types-eq-2.rs
index d71697e9a83..43b0208461e 100644
--- a/tests/ui/associated-types/associated-types-eq-2.rs
+++ b/tests/ui/associated-types/associated-types-eq-2.rs
@@ -4,7 +4,7 @@
 struct Bar;
 struct Qux;
 
-// Tests for a a non generic trait
+// Tests for a non generic trait
 pub trait Tr1 {
     type A;
     fn boo(&self) -> <Self as Tr1>::A;
diff --git a/tests/ui/coherence/associated-type2.rs b/tests/ui/coherence/associated-type2.rs
index 2aadfb04af0..68e98b62953 100644
--- a/tests/ui/coherence/associated-type2.rs
+++ b/tests/ui/coherence/associated-type2.rs
@@ -1,6 +1,6 @@
 //! A regression test for #120343. The overlap error was previously
 //! silenced in coherence because projecting `<() as ToUnit>::Unit`
-//! failed. Then then silenced the missing items error in the `ToUnit`
+//! failed. Then silenced the missing items error in the `ToUnit`
 //! impl, causing us to not emit any errors and ICEing due to a
 //! `span_delay_bug`.
 
diff --git a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs
index 2f53bd019b7..e0d2e44e6e7 100644
--- a/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs
+++ b/tests/ui/higher-ranked/leak-check/candidate-from-env-universe-err-project.rs
@@ -49,7 +49,7 @@ fn function3<T: Trait<'static, Assoc = usize>>() {
     // Trying to normalize the type `for<'a> fn(<T as Trait<'a>>::Assoc)`
     // only gets to `<T as Trait<'a>>::Assoc` once `'a` has been already
     // instantiated, causing us to prefer the where-bound over the impl
-    // resulting in a placeholder error. Even if were were to also use the
+    // resulting in a placeholder error. Even if we were to also use the
     // leak check during candidate selection for normalization, this
     // case would still not compile.
     let _higher_ranked_norm: for<'a> fn(<T as Trait<'a>>::Assoc) = |_| ();
diff --git a/tests/ui/parser/label-is-actually-char.rs b/tests/ui/parser/label-is-actually-char.rs
index 74df898d191..915ce86d269 100644
--- a/tests/ui/parser/label-is-actually-char.rs
+++ b/tests/ui/parser/label-is-actually-char.rs
@@ -1,4 +1,4 @@
-// Note: it's ok to interpret 'a as 'a', but but not ok to interpret 'abc as
+// Note: it's ok to interpret 'a as 'a', but not ok to interpret 'abc as
 // 'abc' because 'abc' is not a valid char literal.
 
 fn main() {
diff --git a/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs b/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs
index 0e1d44e7bb3..75a4fbdb5d6 100644
--- a/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs
+++ b/tests/ui/type-alias-impl-trait/defined-by-user-annotation.rs
@@ -1,4 +1,4 @@
-// User type annotation in fn bodies is a a valid defining site for opaque types.
+// User type annotation in fn bodies is a valid defining site for opaque types.
 //@ check-pass
 #![feature(type_alias_impl_trait)]
 
diff --git a/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs b/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs
index 59ba2694a76..b209b4bc89d 100644
--- a/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs
+++ b/tests/ui/type-alias-impl-trait/equal-lifetime-params-not-ok.rs
@@ -25,7 +25,7 @@ mod mod3 {
 }
 
 // This is similar to the previous cases in that 'a is equal to 'static,
-// which is is some sense an implicit parameter to `Opaque`.
+// which is some sense an implicit parameter to `Opaque`.
 // For example, given a defining use `Opaque<'a> := &'a ()`,
 // it is ambiguous whether `Opaque<'a> := &'a ()` or `Opaque<'a> := &'static ()`
 mod mod4 {