diff --git a/Cargo.lock b/Cargo.lock
index b5da9de5d6248..96eda77abb279 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5406,9 +5406,9 @@ checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81"
 
 [[package]]
 name = "ui_test"
-version = "0.11.6"
+version = "0.11.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24a2e70adc9d18b9b4dd80ea57aeec447103c6fbb354a07c080adad451c645e1"
+checksum = "c21899b59f53717dfad29e4f46e5b21a200a1b6888ab86532a07cfc8b48dd78c"
 dependencies = [
  "bstr",
  "cargo-platform",
diff --git a/config.example.toml b/config.example.toml
index 0c65b25fe1389..367f95b156fed 100644
--- a/config.example.toml
+++ b/config.example.toml
@@ -400,10 +400,20 @@ changelog-seen = 2
 # =============================================================================
 [rust]
 
-# Whether or not to optimize the compiler and standard library.
+# Whether or not to optimize when compiling the compiler and standard library,
+# and what level of optimization to use.
 # WARNING: Building with optimize = false is NOT SUPPORTED. Due to bootstrapping,
 # building without optimizations takes much longer than optimizing. Further, some platforms
 # fail to build without this optimization (c.f. #65352).
+# The valid options are:
+# true - Enable optimizations.
+# false - Disable optimizations.
+# 0 - Disable optimizations.
+# 1 - Basic optimizations.
+# 2 - Some optimizations.
+# 3 - All optimizations.
+# "s" - Optimize for binary size.
+# "z" - Optimize for binary size, but also turn off loop vectorization.
 #optimize = true
 
 # Indicates that the build should be configured for debugging Rust. A
@@ -757,7 +767,7 @@ changelog-seen = 2
 # This option will override the same option under [build] section.
 #profiler = build.profiler (bool)
 
-# This option supports enable `rpath` in each target independently, 
+# This option supports enable `rpath` in each target independently,
 # and will override the same option under [rust] section. It only works on Unix platforms
 #rpath = rust.rpath (bool)
 
diff --git a/library/core/src/default.rs b/library/core/src/default.rs
index 1f7be85d38ac7..5242e97eb9aed 100644
--- a/library/core/src/default.rs
+++ b/library/core/src/default.rs
@@ -133,51 +133,6 @@ pub trait Default: Sized {
     fn default() -> Self;
 }
 
-/// Return the default value of a type according to the `Default` trait.
-///
-/// The type to return is inferred from context; this is equivalent to
-/// `Default::default()` but shorter to type.
-///
-/// For example:
-/// ```
-/// #![feature(default_free_fn)]
-///
-/// use std::default::default;
-///
-/// #[derive(Default)]
-/// struct AppConfig {
-///     foo: FooConfig,
-///     bar: BarConfig,
-/// }
-///
-/// #[derive(Default)]
-/// struct FooConfig {
-///     foo: i32,
-/// }
-///
-/// #[derive(Default)]
-/// struct BarConfig {
-///     bar: f32,
-///     baz: u8,
-/// }
-///
-/// fn main() {
-///     let options = AppConfig {
-///         foo: default(),
-///         bar: BarConfig {
-///             bar: 10.1,
-///             ..default()
-///         },
-///     };
-/// }
-/// ```
-#[unstable(feature = "default_free_fn", issue = "73014")]
-#[must_use]
-#[inline]
-pub fn default<T: Default>() -> T {
-    Default::default()
-}
-
 /// Derive macro generating an impl of the trait `Default`.
 #[rustc_builtin_macro(Default, attributes(default))]
 #[stable(feature = "builtin_macro_prelude", since = "1.38.0")]
diff --git a/library/std/src/io/copy.rs b/library/std/src/io/copy.rs
index ef1f4031ef202..3322940d2452f 100644
--- a/library/std/src/io/copy.rs
+++ b/library/std/src/io/copy.rs
@@ -1,4 +1,8 @@
 use super::{BorrowedBuf, BufReader, BufWriter, ErrorKind, Read, Result, Write, DEFAULT_BUF_SIZE};
+use crate::alloc::Allocator;
+use crate::cmp;
+use crate::collections::VecDeque;
+use crate::io::IoSlice;
 use crate::mem::MaybeUninit;
 
 #[cfg(test)]
@@ -86,7 +90,7 @@ where
 
 /// Specialization of the read-write loop that reuses the internal
 /// buffer of a BufReader. If there's no buffer then the writer side
-/// should be used intead.
+/// should be used instead.
 trait BufferedReaderSpec {
     fn buffer_size(&self) -> usize;
 
@@ -104,7 +108,39 @@ where
     }
 
     default fn copy_to(&mut self, _to: &mut (impl Write + ?Sized)) -> Result<u64> {
-        unimplemented!("only called from specializations");
+        unreachable!("only called from specializations")
+    }
+}
+
+impl BufferedReaderSpec for &[u8] {
+    fn buffer_size(&self) -> usize {
+        // prefer this specialization since the source "buffer" is all we'll ever need,
+        // even if it's small
+        usize::MAX
+    }
+
+    fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
+        let len = self.len();
+        to.write_all(self)?;
+        *self = &self[len..];
+        Ok(len as u64)
+    }
+}
+
+impl<A: Allocator> BufferedReaderSpec for VecDeque<u8, A> {
+    fn buffer_size(&self) -> usize {
+        // prefer this specialization since the source "buffer" is all we'll ever need,
+        // even if it's small
+        usize::MAX
+    }
+
+    fn copy_to(&mut self, to: &mut (impl Write + ?Sized)) -> Result<u64> {
+        let len = self.len();
+        let (front, back) = self.as_slices();
+        let bufs = &mut [IoSlice::new(front), IoSlice::new(back)];
+        to.write_all_vectored(bufs)?;
+        self.clear();
+        Ok(len as u64)
     }
 }
 
@@ -218,6 +254,47 @@ impl<I: Write + ?Sized> BufferedWriterSpec for BufWriter<I> {
     }
 }
 
+impl<A: Allocator> BufferedWriterSpec for Vec<u8, A> {
+    fn buffer_size(&self) -> usize {
+        cmp::max(DEFAULT_BUF_SIZE, self.capacity() - self.len())
+    }
+
+    fn copy_from<R: Read + ?Sized>(&mut self, reader: &mut R) -> Result<u64> {
+        let mut bytes = 0;
+
+        // avoid allocating before we have determined that there's anything to read
+        if self.capacity() == 0 {
+            bytes = stack_buffer_copy(&mut reader.take(DEFAULT_BUF_SIZE as u64), self)?;
+            if bytes == 0 {
+                return Ok(0);
+            }
+        }
+
+        loop {
+            self.reserve(DEFAULT_BUF_SIZE);
+            let mut buf: BorrowedBuf<'_> = self.spare_capacity_mut().into();
+            match reader.read_buf(buf.unfilled()) {
+                Ok(()) => {}
+                Err(e) if e.kind() == ErrorKind::Interrupted => continue,
+                Err(e) => return Err(e),
+            };
+
+            let read = buf.filled().len();
+            if read == 0 {
+                break;
+            }
+
+            // SAFETY: BorrowedBuf guarantees all of its filled bytes are init
+            // and the number of read bytes can't exceed the spare capacity since
+            // that's what the buffer is borrowing from.
+            unsafe { self.set_len(self.len() + read) };
+            bytes += read as u64;
+        }
+
+        Ok(bytes)
+    }
+}
+
 fn stack_buffer_copy<R: Read + ?Sized, W: Write + ?Sized>(
     reader: &mut R,
     writer: &mut W,
diff --git a/library/std/src/io/copy/tests.rs b/library/std/src/io/copy/tests.rs
index 8c816af15b808..af137eaf856f5 100644
--- a/library/std/src/io/copy/tests.rs
+++ b/library/std/src/io/copy/tests.rs
@@ -1,4 +1,6 @@
 use crate::cmp::{max, min};
+use crate::collections::VecDeque;
+use crate::io;
 use crate::io::*;
 
 #[test]
@@ -19,7 +21,7 @@ struct ShortReader {
 
 impl Read for ShortReader {
     fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
-        let bytes = min(self.cap, self.read_size);
+        let bytes = min(self.cap, self.read_size).min(buf.len());
         self.cap -= bytes;
         self.observed_buffer = max(self.observed_buffer, buf.len());
         Ok(bytes)
@@ -78,6 +80,40 @@ fn copy_specializes_bufreader() {
     );
 }
 
+#[test]
+fn copy_specializes_to_vec() {
+    let cap = 123456;
+    let mut source = ShortReader { cap, observed_buffer: 0, read_size: 1337 };
+    let mut sink = Vec::new();
+    assert_eq!(cap as u64, io::copy(&mut source, &mut sink).unwrap());
+    assert!(
+        source.observed_buffer > DEFAULT_BUF_SIZE,
+        "expected a large buffer to be provided to the reader"
+    );
+}
+
+#[test]
+fn copy_specializes_from_vecdeque() {
+    let mut source = VecDeque::with_capacity(100 * 1024);
+    for _ in 0..20 * 1024 {
+        source.push_front(0);
+    }
+    for _ in 0..20 * 1024 {
+        source.push_back(0);
+    }
+    let mut sink = WriteObserver { observed_buffer: 0 };
+    assert_eq!(40 * 1024u64, io::copy(&mut source, &mut sink).unwrap());
+    assert_eq!(20 * 1024, sink.observed_buffer);
+}
+
+#[test]
+fn copy_specializes_from_slice() {
+    let mut source = [1; 60 * 1024].as_slice();
+    let mut sink = WriteObserver { observed_buffer: 0 };
+    assert_eq!(60 * 1024u64, io::copy(&mut source, &mut sink).unwrap());
+    assert_eq!(60 * 1024, sink.observed_buffer);
+}
+
 #[cfg(unix)]
 mod io_benches {
     use crate::fs::File;
diff --git a/library/std/src/os/android/raw.rs b/library/std/src/os/android/raw.rs
index a255d03208623..0182810ff271e 100644
--- a/library/std/src/os/android/raw.rs
+++ b/library/std/src/os/android/raw.rs
@@ -21,26 +21,26 @@ pub use self::arch::{blkcnt_t, blksize_t, dev_t, ino_t, mode_t, nlink_t, off_t,
 
 #[cfg(any(target_arch = "arm", target_arch = "x86"))]
 mod arch {
-    use crate::os::raw::{c_longlong, c_uchar, c_uint, c_ulong, c_ulonglong};
+    use crate::os::raw::{c_long, c_longlong, c_uchar, c_uint, c_ulong, c_ulonglong};
     use crate::os::unix::raw::{gid_t, uid_t};
 
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type dev_t = u64;
+    pub type dev_t = u32;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type mode_t = u32;
+    pub type mode_t = c_uint;
 
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type blkcnt_t = u64;
+    pub type blkcnt_t = c_ulong;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type blksize_t = u64;
+    pub type blksize_t = c_ulong;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type ino_t = u64;
+    pub type ino_t = c_ulong;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type nlink_t = u64;
+    pub type nlink_t = u32;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type off_t = u64;
+    pub type off_t = i32;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type time_t = i64;
+    pub type time_t = c_long;
 
     #[repr(C)]
     #[derive(Clone)]
@@ -70,18 +70,20 @@ mod arch {
         pub st_blksize: u32,
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_blocks: c_ulonglong,
+
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_atime: c_ulong,
+        pub st_atime: time_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_atime_nsec: c_ulong,
+        pub st_atime_nsec: c_long,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_mtime: c_ulong,
+        pub st_mtime: time_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_mtime_nsec: c_ulong,
+        pub st_mtime_nsec: c_long,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_ctime: c_ulong,
+        pub st_ctime: time_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_ctime_nsec: c_ulong,
+        pub st_ctime_nsec: c_long,
+
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_ino: c_ulonglong,
     }
@@ -89,26 +91,26 @@ mod arch {
 
 #[cfg(target_arch = "aarch64")]
 mod arch {
-    use crate::os::raw::{c_uchar, c_ulong};
+    use crate::os::raw::{c_int, c_long, c_uchar, c_uint, c_ulong};
     use crate::os::unix::raw::{gid_t, uid_t};
 
     #[stable(feature = "raw_ext", since = "1.1.0")]
     pub type dev_t = u64;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type mode_t = u32;
+    pub type mode_t = c_uint;
 
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type blkcnt_t = u64;
+    pub type blkcnt_t = c_ulong;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type blksize_t = u64;
+    pub type blksize_t = c_ulong;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type ino_t = u64;
+    pub type ino_t = c_ulong;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type nlink_t = u64;
+    pub type nlink_t = u32;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type off_t = u64;
+    pub type off_t = i64;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type time_t = i64;
+    pub type time_t = c_long;
 
     #[repr(C)]
     #[derive(Clone)]
@@ -117,9 +119,7 @@ mod arch {
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_dev: dev_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub __pad0: [c_uchar; 4],
-        #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub __st_ino: ino_t,
+        pub st_ino: ino_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_mode: mode_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
@@ -131,27 +131,33 @@ mod arch {
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_rdev: dev_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub __pad3: [c_uchar; 4],
+        pub __pad1: c_ulong,
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_size: off_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_blksize: blksize_t,
+        pub st_blksize: c_int,
+        #[stable(feature = "raw_ext", since = "1.1.0")]
+        pub __pad2: c_int,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_blocks: blkcnt_t,
+        pub st_blocks: c_long,
+
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_atime: time_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_atime_nsec: c_ulong,
+        pub st_atime_nsec: c_long,
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_mtime: time_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_mtime_nsec: c_ulong,
+        pub st_mtime_nsec: c_long,
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_ctime: time_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_ctime_nsec: c_ulong,
+        pub st_ctime_nsec: c_long,
+
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_ino: ino_t,
+        pub __unused4: c_uint,
+        #[stable(feature = "raw_ext", since = "1.1.0")]
+        pub __unused5: c_uint,
     }
 }
 
@@ -163,20 +169,20 @@ mod arch {
     #[stable(feature = "raw_ext", since = "1.1.0")]
     pub type dev_t = u64;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type mode_t = u32;
+    pub type mode_t = c_uint;
 
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type blkcnt_t = u64;
+    pub type blkcnt_t = c_ulong;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type blksize_t = u64;
+    pub type blksize_t = c_ulong;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type ino_t = u64;
+    pub type ino_t = c_ulong;
     #[stable(feature = "raw_ext", since = "1.1.0")]
     pub type nlink_t = u32;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type off_t = u64;
+    pub type off_t = i64;
     #[stable(feature = "raw_ext", since = "1.1.0")]
-    pub type time_t = i64;
+    pub type time_t = c_long;
 
     #[repr(C)]
     #[derive(Clone)]
@@ -195,25 +201,30 @@ mod arch {
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_gid: gid_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
+        pub __pad0: c_uint,
+        #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_rdev: dev_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_size: i64,
+        pub st_size: off_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_blksize: c_long,
         #[stable(feature = "raw_ext", since = "1.1.0")]
         pub st_blocks: c_long,
+
+        #[stable(feature = "raw_ext", since = "1.1.0")]
+        pub st_atime: time_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_atime: c_ulong,
+        pub st_atime_nsec: c_long,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_atime_nsec: c_ulong,
+        pub st_mtime: time_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_mtime: c_ulong,
+        pub st_mtime_nsec: c_long,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_mtime_nsec: c_ulong,
+        pub st_ctime: time_t,
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_ctime: c_ulong,
+        pub st_ctime_nsec: c_long,
+
         #[stable(feature = "raw_ext", since = "1.1.0")]
-        pub st_ctime_nsec: c_ulong,
-        __unused: [c_long; 3],
+        pub __pad3: [c_long; 3],
     }
 }
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index 5f5f7ea25fb95..28ae46efefe7a 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -875,11 +875,10 @@ impl Default for StringOrBool {
     }
 }
 
-#[derive(Clone, Debug, Deserialize, PartialEq, Eq)]
-#[serde(untagged)]
+#[derive(Clone, Debug, PartialEq, Eq)]
 pub enum RustOptimize {
-    #[serde(deserialize_with = "deserialize_and_validate_opt_level")]
     String(String),
+    Int(u8),
     Bool(bool),
 }
 
@@ -889,26 +888,74 @@ impl Default for RustOptimize {
     }
 }
 
-fn deserialize_and_validate_opt_level<'de, D>(d: D) -> Result<String, D::Error>
-where
-    D: serde::de::Deserializer<'de>,
-{
-    let v = String::deserialize(d)?;
-    if ["0", "1", "2", "3", "s", "z"].iter().find(|x| **x == v).is_some() {
-        Ok(v)
-    } else {
-        Err(format!(r#"unrecognized option for rust optimize: "{}", expected one of "0", "1", "2", "3", "s", "z""#, v)).map_err(serde::de::Error::custom)
+impl<'de> Deserialize<'de> for RustOptimize {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        deserializer.deserialize_any(OptimizeVisitor)
+    }
+}
+
+struct OptimizeVisitor;
+
+impl<'de> serde::de::Visitor<'de> for OptimizeVisitor {
+    type Value = RustOptimize;
+
+    fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        formatter.write_str(r#"one of: 0, 1, 2, 3, "s", "z", true, false"#)
+    }
+
+    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
+    where
+        E: serde::de::Error,
+    {
+        if ["s", "z"].iter().find(|x| **x == value).is_some() {
+            Ok(RustOptimize::String(value.to_string()))
+        } else {
+            Err(format_optimize_error_msg(value)).map_err(serde::de::Error::custom)
+        }
+    }
+
+    fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
+    where
+        E: serde::de::Error,
+    {
+        if matches!(value, 0..=3) {
+            Ok(RustOptimize::Int(value as u8))
+        } else {
+            Err(format_optimize_error_msg(value)).map_err(serde::de::Error::custom)
+        }
+    }
+
+    fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E>
+    where
+        E: serde::de::Error,
+    {
+        Ok(RustOptimize::Bool(value))
     }
 }
 
+fn format_optimize_error_msg(v: impl std::fmt::Display) -> String {
+    format!(
+        r#"unrecognized option for rust optimize: "{}", expected one of 0, 1, 2, 3, "s", "z", true, false"#,
+        v
+    )
+}
+
 impl RustOptimize {
     pub(crate) fn is_release(&self) -> bool {
-        if let RustOptimize::Bool(true) | RustOptimize::String(_) = &self { true } else { false }
+        match &self {
+            RustOptimize::Bool(true) | RustOptimize::String(_) => true,
+            RustOptimize::Int(i) => *i > 0,
+            RustOptimize::Bool(false) => false,
+        }
     }
 
     pub(crate) fn get_opt_level(&self) -> Option<String> {
         match &self {
             RustOptimize::String(s) => Some(s.clone()),
+            RustOptimize::Int(i) => Some(i.to_string()),
             RustOptimize::Bool(_) => None,
         }
     }
diff --git a/src/bootstrap/config/tests.rs b/src/bootstrap/config/tests.rs
index 732df54cdacbd..c340bb2982a2b 100644
--- a/src/bootstrap/config/tests.rs
+++ b/src/bootstrap/config/tests.rs
@@ -184,7 +184,10 @@ fn rust_optimize() {
     assert_eq!(parse("").rust_optimize.is_release(), true);
     assert_eq!(parse("rust.optimize = false").rust_optimize.is_release(), false);
     assert_eq!(parse("rust.optimize = true").rust_optimize.is_release(), true);
-    assert_eq!(parse("rust.optimize = \"1\"").rust_optimize.get_opt_level(), Some("1".to_string()));
+    assert_eq!(parse("rust.optimize = 0").rust_optimize.is_release(), false);
+    assert_eq!(parse("rust.optimize = 1").rust_optimize.is_release(), true);
+    assert_eq!(parse("rust.optimize = 1").rust_optimize.get_opt_level(), Some("1".to_string()));
+    assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.is_release(), true);
     assert_eq!(parse("rust.optimize = \"s\"").rust_optimize.get_opt_level(), Some("s".to_string()));
 }
 
diff --git a/src/ci/docker/host-x86_64/test-various/Dockerfile b/src/ci/docker/host-x86_64/test-various/Dockerfile
index 1dc7b79872438..a1d06ab18443c 100644
--- a/src/ci/docker/host-x86_64/test-various/Dockerfile
+++ b/src/ci/docker/host-x86_64/test-various/Dockerfile
@@ -24,7 +24,7 @@ RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-ins
   qemu-system-x86 \
   && rm -rf /var/lib/apt/lists/*
 
-RUN curl -sL https://nodejs.org/dist/v15.14.0/node-v15.14.0-linux-x64.tar.xz | \
+RUN curl -sL https://nodejs.org/dist/v18.12.0/node-v18.12.0-linux-x64.tar.xz | \
   tar -xJ
 
 # Install 32-bit OVMF files for the i686-unknown-uefi test. This package
@@ -42,7 +42,7 @@ RUN sh /scripts/sccache.sh
 
 ENV RUST_CONFIGURE_ARGS \
   --musl-root-x86_64=/usr/local/x86_64-linux-musl \
-  --set build.nodejs=/node-v15.14.0-linux-x64/bin/node \
+  --set build.nodejs=/node-v18.12.0-linux-x64/bin/node \
   --set rust.lld
 
 # Some run-make tests have assertions about code size, and enabling debug
@@ -58,6 +58,8 @@ ENV WASM_SCRIPT python3 /checkout/x.py --stage 2 test --host='' --target $WASM_T
   tests/ui \
   tests/mir-opt \
   tests/codegen-units \
+  tests/codegen \
+  tests/assembly \
   library/core
 
 ENV NVPTX_TARGETS=nvptx64-nvidia-cuda
diff --git a/src/doc/unstable-book/src/library-features/default-free-fn.md b/src/doc/unstable-book/src/library-features/default-free-fn.md
deleted file mode 100644
index bafc9ac4d0d96..0000000000000
--- a/src/doc/unstable-book/src/library-features/default-free-fn.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# `default_free_fn`
-
-The tracking issue for this feature is: [#73014]
-
-[#73014]: https://github.com/rust-lang/rust/issues/73014
-
-------------------------
-
-Adds a free `default()` function to the `std::default` module.  This function
-just forwards to [`Default::default()`], but may remove repetition of the word
-"default" from the call site.
-
-[`Default::default()`]: ../../std/default/trait.Default.html#tymethod.default
-
-Here is an example:
-
-```rust
-#![feature(default_free_fn)]
-use std::default::default;
-
-#[derive(Default)]
-struct AppConfig {
-    foo: FooConfig,
-    bar: BarConfig,
-}
-
-#[derive(Default)]
-struct FooConfig {
-    foo: i32,
-}
-
-#[derive(Default)]
-struct BarConfig {
-    bar: f32,
-    baz: u8,
-}
-
-fn main() {
-    let options = AppConfig {
-        foo: default(),
-        bar: BarConfig {
-            bar: 10.1,
-            ..default()
-        },
-    };
-}
-```
diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml
index 8068db1cf4f0d..c87b3e4232361 100644
--- a/src/tools/miri/.github/workflows/ci.yml
+++ b/src/tools/miri/.github/workflows/ci.yml
@@ -15,6 +15,10 @@ on:
 env:
   CARGO_UNSTABLE_SPARSE_REGISTRY: 'true'
 
+defaults:
+  run:
+    shell: bash
+
 jobs:
   build:
     runs-on: ${{ matrix.os }}
@@ -59,12 +63,9 @@ jobs:
 
       - name: Install rustup-toolchain-install-master
         if: ${{ steps.cache.outputs.cache-hit != 'true' }}
-        shell: bash
-        run: |
-          cargo install -f rustup-toolchain-install-master
+        run: cargo install -f rustup-toolchain-install-master
 
       - name: Install "master" toolchain
-        shell: bash
         run: |
           if [[ ${{ github.event_name }} == 'schedule' ]]; then
             echo "Building against latest rustc git version"
@@ -79,7 +80,7 @@ jobs:
           cargo -V
 
       - name: Test
-        run: bash ./ci.sh
+        run: ./ci.sh
 
   style:
     name: style checks
@@ -111,14 +112,10 @@ jobs:
 
       - name: Install rustup-toolchain-install-master
         if: ${{ steps.cache.outputs.cache-hit != 'true' }}
-        shell: bash
-        run: |
-          cargo install -f rustup-toolchain-install-master
+        run: cargo install -f rustup-toolchain-install-master
 
       - name: Install "master" toolchain
-        shell: bash
-        run: |
-          ./miri toolchain
+        run: ./miri toolchain
 
       - name: Show Rust version
         run: |
@@ -138,7 +135,6 @@ jobs:
   # workflow is successful listening to webhooks only.
   #
   # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
-  # (`fmt` is deliberately not listed, we want bors to ignore it.)
   end-success:
     name: bors build finished
     runs-on: ubuntu-latest
@@ -166,12 +162,12 @@ jobs:
       - name: Install zulip-send
         run: pip3 install zulip
       - name: Send Zulip notification
-        shell: bash
         env:
           ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }}
           ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }}
         run: |
-          ~/.local/bin/zulip-send --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \
+          ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \
+            --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \
             --message 'Dear @*T-miri*,
 
           It would appear that the [Miri cron job build]('"https://github.com/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"') failed.
@@ -183,9 +179,12 @@ jobs:
 
           Thanks in advance!
           Sincerely,
-          The Miri Cronjobs Bot' \
-            --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com
+          The Miri Cronjobs Bot'
+
       # Attempt to auto-sync with rustc
+      - uses: actions/checkout@v3
+        with:
+          fetch-depth: 256 # get a bit more of the history
       - name: install josh-proxy
         run: cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r22.12.06
       - name: start josh-proxy
@@ -196,16 +195,24 @@ jobs:
           git config --global user.email 'miri@cron.bot'
       - name: get changes from rustc
         run: ./miri rustc-pull
+      - name: Install rustup-toolchain-install-master
+        run: cargo install -f rustup-toolchain-install-master
       - name: format changes (if any)
         run: |
           ./miri toolchain
           ./miri fmt --check || (./miri fmt && git commit -am "fmt")
       - name: Push changes to a branch
         run: |
-          git switch -c "rustup$(date -u +%Y-%m)"
-          git push
+          BRANCH="rustup$(date -u +%Y-%m-%d)"
+          git switch -c $BRANCH
+          git push -u origin $BRANCH
       - name: Create Pull Request
-        run: gh pr create -B master --title 'Automatic sync from rustc' --body ''
+        run: |
+          PR=$(gh pr create -B master --title 'Automatic sync from rustc' --body '')
+          ~/.local/bin/zulip-send --user $ZULIP_BOT_EMAIL --api-key $ZULIP_API_TOKEN --site https://rust-lang.zulipchat.com \
+            --stream miri --subject "Cron Job Failure (miri, $(date -u +%Y-%m))" \
+            --message "A PR doing a rustc-pull [has been automatically created]($PR) for your convenience."
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
+          ZULIP_BOT_EMAIL: ${{ secrets.ZULIP_BOT_EMAIL }}
+          ZULIP_API_TOKEN: ${{ secrets.ZULIP_API_TOKEN }}
diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock
index edb3ee48a4eaf..4232d7fda78f1 100644
--- a/src/tools/miri/Cargo.lock
+++ b/src/tools/miri/Cargo.lock
@@ -842,9 +842,9 @@ dependencies = [
 
 [[package]]
 name = "ui_test"
-version = "0.11.6"
+version = "0.11.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "24a2e70adc9d18b9b4dd80ea57aeec447103c6fbb354a07c080adad451c645e1"
+checksum = "c21899b59f53717dfad29e4f46e5b21a200a1b6888ab86532a07cfc8b48dd78c"
 dependencies = [
  "bstr",
  "cargo-platform",
diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml
index 50864334fc51e..a625e1696e143 100644
--- a/src/tools/miri/Cargo.toml
+++ b/src/tools/miri/Cargo.toml
@@ -36,7 +36,7 @@ libloading = "0.7"
 
 [dev-dependencies]
 colored = "2"
-ui_test = "0.11.6"
+ui_test = "0.11.7"
 rustc_version = "0.4"
 # Features chosen to match those required by env_logger, to avoid rebuilds
 regex = { version = "1.5.5", default-features = false, features = ["perf", "std"] }
diff --git a/src/tools/miri/cargo-miri/src/main.rs b/src/tools/miri/cargo-miri/src/main.rs
index 85c9cdad7dfd2..6178670b4f034 100644
--- a/src/tools/miri/cargo-miri/src/main.rs
+++ b/src/tools/miri/cargo-miri/src/main.rs
@@ -80,7 +80,11 @@ fn main() {
     match first.as_str() {
         "miri" => phase_cargo_miri(args),
         "runner" => phase_runner(args, RunnerPhase::Cargo),
-        arg if arg == env::var("RUSTC").unwrap() => {
+        arg if arg == env::var("RUSTC").unwrap_or_else(|_| {
+            show_error!(
+                "`cargo-miri` called without RUSTC set; please only invoke this binary through `cargo miri`"
+            )
+        }) => {
             // If the first arg is equal to the RUSTC env variable (which should be set at this
             // point), then we need to behave as rustc. This is the somewhat counter-intuitive
             // behavior of having both RUSTC and RUSTC_WRAPPER set
diff --git a/src/tools/miri/cargo-miri/src/util.rs b/src/tools/miri/cargo-miri/src/util.rs
index 60f39cb36abaa..4c19ed97fb810 100644
--- a/src/tools/miri/cargo-miri/src/util.rs
+++ b/src/tools/miri/cargo-miri/src/util.rs
@@ -82,7 +82,7 @@ pub enum MiriCommand {
 pub fn escape_for_toml(s: &str) -> String {
     // We want to surround this string in quotes `"`. So we first escape all quotes,
     // and also all backslashes (that are used to escape quotes).
-    let s = s.replace('\\', r#"\\"#).replace('"', r#"\""#);
+    let s = s.replace('\\', r"\\").replace('"', r#"\""#);
     format!("\"{s}\"")
 }
 
@@ -130,7 +130,7 @@ pub fn exec(mut cmd: Command) -> ! {
     {
         use std::os::unix::process::CommandExt;
         let error = cmd.exec();
-        Err(error).expect("failed to run command")
+        panic!("failed to run command: {error}")
     }
 }
 
diff --git a/src/tools/miri/miri b/src/tools/miri/miri
index 7cda995879cfb..ace3d17ae2afb 100755
--- a/src/tools/miri/miri
+++ b/src/tools/miri/miri
@@ -124,7 +124,7 @@ rustc-pull)
     git commit rust-version -m "Preparing for merge from rustc" || (echo "FAILED to commit rust-version file, something went wrong"; exit 1)
     # Fetch given rustc commit and note down which one that was
     git fetch http://localhost:8000/rust-lang/rust.git@$FETCH_COMMIT$JOSH_FILTER.git || (echo "FAILED to fetch new commits, something went wrong"; exit 1)
-    git merge FETCH_HEAD --no-ff -m "Merge from rustc" || (echo "FAILED to merge new commits, something went wrong"; exit 1)
+    git merge FETCH_HEAD --no-ff -m "Merge from rustc" || (echo "FAILED to merge new commits ($(git rev-parse FETCH_HEAD)), something went wrong"; exit 1)
     exit 0
     ;;
 rustc-push)
@@ -325,6 +325,7 @@ run|run-dep)
         MIRIFLAGS="$MIRIFLAGS --target $MIRI_TEST_TARGET"
     fi
 
+    CARGO="$CARGO --quiet"
     # First build and get a sysroot.
     $CARGO build $CARGO_EXTRA_FLAGS --manifest-path "$MIRIDIR"/Cargo.toml
     find_sysroot
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 6790454bdb72b..02b0dd16f91c1 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-75726cae37317c7262b69d3e9fd11a3496a88d04
+d4096e0412ac5de785d739a0aa2b1c1c7b9d3b7d
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 2dc33334417e3..1ec4cbc4de71d 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -67,7 +67,7 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
             if tcx.sess.compile_status().is_err() {
                 tcx.sess.fatal("miri cannot be run on programs that fail compilation");
             }
-;
+
             init_late_loggers(handler, tcx);
             if !tcx.sess.crate_types().contains(&CrateType::Executable) {
                 tcx.sess.fatal("miri only makes sense on bin crates");
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
index c9674e0a2fe2c..de307a3c5f5be 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
@@ -221,7 +221,10 @@ impl AllocHistory {
 impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
     pub fn start_grant(&mut self, perm: Permission) {
         let Operation::Retag(op) = &mut self.operation else {
-            unreachable!("start_grant must only be called during a retag, this is: {:?}", self.operation)
+            unreachable!(
+                "start_grant must only be called during a retag, this is: {:?}",
+                self.operation
+            )
         };
         op.permission = Some(perm);
 
@@ -286,7 +289,8 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
         tag: BorTag,
         protector_tag: Option<BorTag>,
     ) -> Option<TagHistory> {
-        let Some(created) = self.history
+        let Some(created) = self
+            .history
             .creations
             .iter()
             .rev()
@@ -315,22 +319,27 @@ impl<'history, 'ecx, 'mir, 'tcx> DiagnosticCx<'history, 'ecx, 'mir, 'tcx> {
                         None
                     }
                 })
-            }).or_else(|| {
+            })
+            .or_else(|| {
                 // If we didn't find a retag that created this tag, it might be the base tag of
                 // this allocation.
                 if self.history.base.0.tag() == tag {
                     Some((
-                        format!("{tag:?} was created here, as the base tag for {:?}", self.history.id),
-                        self.history.base.1.data()
+                        format!(
+                            "{tag:?} was created here, as the base tag for {:?}",
+                            self.history.id
+                        ),
+                        self.history.base.1.data(),
                     ))
                 } else {
                     None
                 }
-            }) else {
-                // But if we don't have a creation event, this is related to a wildcard, and there
-                // is really nothing we can do to help.
-                return None;
-            };
+            })
+        else {
+            // But if we don't have a creation event, this is related to a wildcard, and there
+            // is really nothing we can do to help.
+            return None;
+        };
 
         let invalidated = self.history.invalidations.iter().rev().find_map(|event| {
             if event.tag == tag { Some(event.generate_diagnostic()) } else { None }
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
index 581327e54f1bc..ca0f69450c931 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
@@ -430,12 +430,15 @@ impl<'tcx> Stack {
                 .find_granting(AccessKind::Write, derived_from, exposed_tags)
                 .map_err(|()| dcx.grant_error(self))?;
 
-            let (Some(granting_idx), ProvenanceExtra::Concrete(_)) = (granting_idx, derived_from) else {
+            let (Some(granting_idx), ProvenanceExtra::Concrete(_)) = (granting_idx, derived_from)
+            else {
                 // The parent is a wildcard pointer or matched the unknown bottom.
                 // This is approximate. Nobody knows what happened, so forget everything.
                 // The new thing is SRW anyway, so we cannot push it "on top of the unknown part"
                 // (for all we know, it might join an SRW group inside the unknown).
-                trace!("reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown");
+                trace!(
+                    "reborrow: forgetting stack entirely due to SharedReadWrite reborrow from wildcard or unknown"
+                );
                 self.set_unknown_bottom(global.next_ptr_tag);
                 return Ok(());
             };
@@ -1008,7 +1011,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
         // We have to turn the place into a pointer to use the existing code.
         // (The pointer type does not matter, so we use a raw pointer.)
-        let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx,return_place.layout.ty))?;
+        let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, return_place.layout.ty))?;
         let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
         // Reborrow it. With protection! That is part of the point.
         let new_perm = NewPermission::Uniform {
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 a1e949183ad40..291807c25eeb7 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
@@ -196,19 +196,19 @@ impl<'tcx> Stack {
         let ProvenanceExtra::Concrete(tag) = tag else {
             // Handle the wildcard case.
             // Go search the stack for an exposed tag.
-            if let Some(idx) =
-                self.borrows
-                    .iter()
-                    .enumerate() // we also need to know *where* in the stack
-                    .rev() // search top-to-bottom
-                    .find_map(|(idx, item)| {
-                        // If the item fits and *might* be this wildcard, use it.
-                        if item.perm().grants(access) && exposed_tags.contains(&item.tag()) {
-                            Some(idx)
-                        } else {
-                            None
-                        }
-                    })
+            if let Some(idx) = self
+                .borrows
+                .iter()
+                .enumerate() // we also need to know *where* in the stack
+                .rev() // search top-to-bottom
+                .find_map(|(idx, item)| {
+                    // If the item fits and *might* be this wildcard, use it.
+                    if item.perm().grants(access) && exposed_tags.contains(&item.tag()) {
+                        Some(idx)
+                    } else {
+                        None
+                    }
+                })
             {
                 return Ok(Some(idx));
             }
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 a87a4bbddadea..3b2d6f9608ee4 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
@@ -570,9 +570,13 @@ impl DisplayRepr {
         extraction_aux(tree, tree.root, show_unnamed, &mut v);
         let Some(root) = v.pop() else {
             if show_unnamed {
-                unreachable!("This allocation contains no tags, not even a root. This should not happen.");
+                unreachable!(
+                    "This allocation contains no tags, not even a root. This should not happen."
+                );
             }
-            eprintln!("This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags.");
+            eprintln!(
+                "This allocation does not contain named tags. Use `miri_print_borrow_state(_, true)` to also print unnamed tags."
+            );
             return None;
         };
         assert!(v.is_empty());
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
index 4c0690c585cd5..e134b73988800 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
@@ -256,7 +256,9 @@ trait EvalContextPrivExt<'mir: 'ecx, 'tcx: 'mir, 'ecx>: crate::MiriInterpCxExt<'
             ptr_size.bytes()
         );
 
-        let Some(new_perm) = new_perm else { return Ok(Some((alloc_id, orig_tag))); };
+        let Some(new_perm) = new_perm else {
+            return Ok(Some((alloc_id, orig_tag)));
+        };
 
         if let Some(protect) = new_perm.protector {
             // We register the protection in two different places.
@@ -509,7 +511,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
         // We have to turn the place into a pointer to use the existing code.
         // (The pointer type does not matter, so we use a raw pointer.)
-        let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx,return_place.layout.ty))?;
+        let ptr_layout = this.layout_of(Ty::new_mut_ptr(this.tcx.tcx, return_place.layout.ty))?;
         let val = ImmTy::from_immediate(return_place.to_ref(this), ptr_layout);
         // Reborrow it. With protection! That is part of the point.
         // FIXME: do we truly want a 2phase borrow here?
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 56de9c007d0e9..aba7dd5a9fee0 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -305,7 +305,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
         for arg in config.args.iter() {
             // Make space for `0` terminator.
             let size = u64::try_from(arg.len()).unwrap().checked_add(1).unwrap();
-            let arg_type = Ty::new_array(tcx,tcx.types.u8, size);
+            let arg_type = Ty::new_array(tcx, tcx.types.u8, size);
             let arg_place =
                 ecx.allocate(ecx.layout_of(arg_type)?, MiriMemoryKind::Machine.into())?;
             ecx.write_os_str_to_c_str(OsStr::new(arg), arg_place.ptr, size)?;
@@ -313,9 +313,11 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
             argvs.push(arg_place.to_ref(&ecx));
         }
         // Make an array with all these pointers, in the Miri memory.
-        let argvs_layout = ecx.layout_of(
-            Ty::new_array(tcx,Ty::new_imm_ptr(tcx,tcx.types.u8), u64::try_from(argvs.len()).unwrap()),
-        )?;
+        let argvs_layout = ecx.layout_of(Ty::new_array(
+            tcx,
+            Ty::new_imm_ptr(tcx, tcx.types.u8),
+            u64::try_from(argvs.len()).unwrap(),
+        ))?;
         let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Machine.into())?;
         for (idx, arg) in argvs.into_iter().enumerate() {
             let place = ecx.mplace_field(&argvs_place, idx)?;
@@ -333,7 +335,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
             ecx.machine.argc = Some(*argc_place);
 
             let argv_place = ecx.allocate(
-                ecx.layout_of(Ty::new_imm_ptr(tcx,tcx.types.unit))?,
+                ecx.layout_of(Ty::new_imm_ptr(tcx, tcx.types.unit))?,
                 MiriMemoryKind::Machine.into(),
             )?;
             ecx.write_immediate(argv, &argv_place.into())?;
@@ -345,7 +347,8 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
             // Construct a command string with all the arguments.
             let cmd_utf16: Vec<u16> = args_to_utf16_command_string(config.args.iter());
 
-            let cmd_type = Ty::new_array(tcx,tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap());
+            let cmd_type =
+                Ty::new_array(tcx, tcx.types.u16, u64::try_from(cmd_utf16.len()).unwrap());
             let cmd_place =
                 ecx.allocate(ecx.layout_of(cmd_type)?, MiriMemoryKind::Machine.into())?;
             ecx.machine.cmd_line = Some(*cmd_place);
@@ -366,7 +369,11 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
 
     match entry_type {
         EntryFnType::Main { .. } => {
-            let start_id = tcx.lang_items().start_fn().unwrap();
+            let start_id = tcx.lang_items().start_fn().unwrap_or_else(|| {
+                tcx.sess.fatal(
+                    "could not find start function. Make sure the entry point is marked with `#[start]`."
+                );
+            });
             let main_ret_ty = tcx.fn_sig(entry_id).no_bound_vars().unwrap().output();
             let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
             let start_instance = ty::Instance::resolve(
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index e79bb47c78b18..7e92dc7a0c743 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -9,6 +9,7 @@
 #![feature(local_key_cell_methods)]
 #![feature(round_ties_even)]
 #![feature(os_str_bytes)]
+#![feature(lint_reasons)]
 // Configure clippy and other lints
 #![allow(
     clippy::collapsible_else_if,
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 5fcf3905199d3..ac2bad221199c 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -313,10 +313,12 @@ pub struct PrimitiveLayouts<'tcx> {
 impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
     fn new(layout_cx: LayoutCx<'tcx, TyCtxt<'tcx>>) -> Result<Self, &'tcx LayoutError<'tcx>> {
         let tcx = layout_cx.tcx;
-        let mut_raw_ptr = Ty::new_ptr(tcx,TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut });
-        let const_raw_ptr = Ty::new_ptr(tcx,TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not });
+        let mut_raw_ptr =
+            Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Mut });
+        let const_raw_ptr =
+            Ty::new_ptr(tcx, TypeAndMut { ty: tcx.types.unit, mutbl: Mutability::Not });
         Ok(Self {
-            unit: layout_cx.layout_of(Ty::new_unit(tcx,))?,
+            unit: layout_cx.layout_of(Ty::new_unit(tcx))?,
             i8: layout_cx.layout_of(tcx.types.i8)?,
             i16: layout_cx.layout_of(tcx.types.i16)?,
             i32: layout_cx.layout_of(tcx.types.i32)?,
diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs
index eaa66f1f87469..adf9a35d5c3a3 100644
--- a/src/tools/miri/src/shims/backtrace.rs
+++ b/src/tools/miri/src/shims/backtrace.rs
@@ -71,7 +71,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let len: u64 = ptrs.len().try_into().unwrap();
 
         let ptr_ty = this.machine.layouts.mut_raw_ptr.ty;
-        let array_layout = this.layout_of(Ty::new_array(tcx.tcx,ptr_ty, len)).unwrap();
+        let array_layout = this.layout_of(Ty::new_array(tcx.tcx, ptr_ty, len)).unwrap();
 
         match flags {
             // storage for pointers is allocated by miri
diff --git a/src/tools/miri/src/shims/env.rs b/src/tools/miri/src/shims/env.rs
index 3d4967f1f834e..1dcb877a83f0d 100644
--- a/src/tools/miri/src/shims/env.rs
+++ b/src/tools/miri/src/shims/env.rs
@@ -5,8 +5,8 @@ use std::mem;
 
 use rustc_const_eval::interpret::Pointer;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::Ty;
 use rustc_target::abi::Size;
 
 use crate::helpers::target_os_is_unix;
@@ -449,9 +449,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         vars.push(Pointer::null());
         // Make an array with all these pointers inside Miri.
         let tcx = this.tcx;
-        let vars_layout = this.layout_of(
-            Ty::new_array(tcx.tcx,this.machine.layouts.mut_raw_ptr.ty, u64::try_from(vars.len()).unwrap()),
-        )?;
+        let vars_layout = this.layout_of(Ty::new_array(
+            tcx.tcx,
+            this.machine.layouts.mut_raw_ptr.ty,
+            u64::try_from(vars.len()).unwrap(),
+        ))?;
         let vars_place = this.allocate(vars_layout, MiriMemoryKind::Runtime.into())?;
         for (idx, var) in vars.into_iter().enumerate() {
             let place = this.mplace_field(&vars_place, idx)?;
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index f4e91c30d9f1a..6915c396d616c 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -763,6 +763,12 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
                 let ptr_dest = this.read_pointer(ptr_dest)?;
                 let ptr_src = this.read_pointer(ptr_src)?;
                 let n = this.read_target_usize(n)?;
+
+                // C requires that this must always be a valid pointer, even if `n` is zero, so we better check that.
+                // (This is more than Rust requires, so `mem_copy` is not sufficient.)
+                this.ptr_get_alloc_id(ptr_dest)?;
+                this.ptr_get_alloc_id(ptr_src)?;
+
                 this.mem_copy(
                     ptr_src,
                     Align::ONE,
diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs
index 1995db715e8da..94f8cfbfb1c3d 100644
--- a/src/tools/miri/src/shims/intrinsics/simd.rs
+++ b/src/tools/miri/src/shims/intrinsics/simd.rs
@@ -483,7 +483,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
 
                 // `index` is an array, not a SIMD type
                 let ty::Array(_, index_len) = index.layout.ty.kind() else {
-                    span_bug!(this.cur_span(), "simd_shuffle index argument has non-array type {}", index.layout.ty)
+                    span_bug!(
+                        this.cur_span(),
+                        "simd_shuffle index argument has non-array type {}",
+                        index.layout.ty
+                    )
                 };
                 let index_len = index_len.eval_target_usize(*this.tcx, this.param_env());
 
@@ -622,9 +626,7 @@ fn fmax_op<'tcx>(
     right: &ImmTy<'tcx, Provenance>,
 ) -> InterpResult<'tcx, Scalar<Provenance>> {
     assert_eq!(left.layout.ty, right.layout.ty);
-    let ty::Float(float_ty) = left.layout.ty.kind() else {
-        bug!("fmax operand is not a float")
-    };
+    let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmax operand is not a float") };
     let left = left.to_scalar();
     let right = right.to_scalar();
     Ok(match float_ty {
@@ -638,9 +640,7 @@ fn fmin_op<'tcx>(
     right: &ImmTy<'tcx, Provenance>,
 ) -> InterpResult<'tcx, Scalar<Provenance>> {
     assert_eq!(left.layout.ty, right.layout.ty);
-    let ty::Float(float_ty) = left.layout.ty.kind() else {
-        bug!("fmin operand is not a float")
-    };
+    let ty::Float(float_ty) = left.layout.ty.kind() else { bug!("fmin operand is not a float") };
     let left = left.to_scalar();
     let right = right.to_scalar();
     Ok(match float_ty {
diff --git a/src/tools/miri/src/shims/os_str.rs b/src/tools/miri/src/shims/os_str.rs
index 4cdd54fa21b77..f08f0aad5e764 100644
--- a/src/tools/miri/src/shims/os_str.rs
+++ b/src/tools/miri/src/shims/os_str.rs
@@ -7,8 +7,8 @@ use std::os::unix::ffi::{OsStrExt, OsStringExt};
 #[cfg(windows)]
 use std::os::windows::ffi::{OsStrExt, OsStringExt};
 
-use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::Ty;
 
 use crate::*;
 
@@ -141,7 +141,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator.
         let this = self.eval_context_mut();
 
-        let arg_type = Ty::new_array(this.tcx.tcx,this.tcx.types.u8, size);
+        let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u8, size);
         let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
         let (written, _) = self.write_os_str_to_c_str(os_str, arg_place.ptr, size).unwrap();
         assert!(written);
@@ -157,7 +157,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
         let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0x0000` terminator.
         let this = self.eval_context_mut();
 
-        let arg_type = Ty::new_array(this.tcx.tcx,this.tcx.types.u16, size);
+        let arg_type = Ty::new_array(this.tcx.tcx, this.tcx.types.u16, size);
         let arg_place = this.allocate(this.layout_of(arg_type).unwrap(), memkind)?;
         let (written, _) =
             self.write_os_str_to_wide_str(os_str, arg_place.ptr, size, /*truncate*/ false).unwrap();
diff --git a/src/tools/miri/src/shims/unix/linux/fd.rs b/src/tools/miri/src/shims/unix/linux/fd.rs
index dc395d39ce11a..87e887000c523 100644
--- a/src/tools/miri/src/shims/unix/linux/fd.rs
+++ b/src/tools/miri/src/shims/unix/linux/fd.rs
@@ -181,6 +181,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
     /// `EFD_SEMAPHORE` - miri does not support semaphore-like semantics.
     ///
     /// <https://linux.die.net/man/2/eventfd>
+    #[expect(clippy::needless_if)]
     fn eventfd(
         &mut self,
         val: &OpTy<'tcx, Provenance>,
diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs
index 73671e716efe5..59143550253d6 100644
--- a/src/tools/miri/tests/compiletest.rs
+++ b/src/tools/miri/tests/compiletest.rs
@@ -145,7 +145,8 @@ fn run_tests(mode: Mode, path: &str, target: &str, with_dependencies: bool) -> R
         // The files we're actually interested in (all `.rs` files).
         |path| {
             path.extension().is_some_and(|ext| ext == "rs")
-                && (filters.is_empty() || filters.iter().any(|f| path.starts_with(f)))
+                && (filters.is_empty()
+                    || filters.iter().any(|f| path.display().to_string().contains(f)))
         },
         // This could be used to overwrite the `Config` on a per-test basis.
         |_, _| None,
@@ -274,13 +275,13 @@ fn main() -> Result<()> {
 fn run_dep_mode(target: String, mut args: impl Iterator<Item = OsString>) -> Result<()> {
     let path = args.next().expect("./miri run-dep must be followed by a file name");
     let mut config = test_config(&target, "", Mode::Yolo, /* with dependencies */ true);
-    config.program.args.remove(0); // remove the `--error-format=json` argument
-    config.program.args.push("--color".into());
-    config.program.args.push("always".into());
-    let mut cmd = ui_test::test_command(config, Path::new(&path))?;
-    // Separate the arguments to the `cargo miri` invocation from
-    // the arguments to the interpreted prog
-    cmd.arg("--");
+    config.program.args.clear(); // We want to give the user full control over flags
+    config.build_dependencies_and_link_them()?;
+
+    let mut cmd = config.program.build(&config.out_dir);
+
+    cmd.arg(path);
+
     cmd.args(args);
     if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) }
 }
diff --git a/src/tools/miri/tests/fail/shims/memchr_null.rs b/src/tools/miri/tests/fail/shims/memchr_null.rs
new file mode 100644
index 0000000000000..6bc7af7e6bf77
--- /dev/null
+++ b/src/tools/miri/tests/fail/shims/memchr_null.rs
@@ -0,0 +1,10 @@
+//@ignore-target-windows: No libc on Windows
+
+use std::ptr;
+
+// null is explicitly called out as UB in the C docs.
+fn main() {
+    unsafe {
+        libc::memchr(ptr::null(), 0, 0); //~ERROR: dangling
+    }
+}
diff --git a/src/tools/miri/tests/fail/shims/memchr_null.stderr b/src/tools/miri/tests/fail/shims/memchr_null.stderr
new file mode 100644
index 0000000000000..d48606f34ad9e
--- /dev/null
+++ b/src/tools/miri/tests/fail/shims/memchr_null.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
+  --> $DIR/memchr_null.rs:LL:CC
+   |
+LL |         libc::memchr(ptr::null(), 0, 0);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
+   |
+   = 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
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/memchr_null.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/shims/memcmp_null.rs b/src/tools/miri/tests/fail/shims/memcmp_null.rs
new file mode 100644
index 0000000000000..a4e0034c40bdc
--- /dev/null
+++ b/src/tools/miri/tests/fail/shims/memcmp_null.rs
@@ -0,0 +1,10 @@
+//@ignore-target-windows: No libc on Windows
+
+use std::ptr;
+
+// null is explicitly called out as UB in the C docs.
+fn main() {
+    unsafe {
+        libc::memcmp(ptr::null(), ptr::null(), 0); //~ERROR: dangling
+    }
+}
diff --git a/src/tools/miri/tests/fail/shims/memcmp_null.stderr b/src/tools/miri/tests/fail/shims/memcmp_null.stderr
new file mode 100644
index 0000000000000..7a09c77989455
--- /dev/null
+++ b/src/tools/miri/tests/fail/shims/memcmp_null.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
+  --> $DIR/memcmp_null.rs:LL:CC
+   |
+LL |         libc::memcmp(ptr::null(), ptr::null(), 0);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
+   |
+   = 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
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/memcmp_null.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/shims/memcpy_zero.rs b/src/tools/miri/tests/fail/shims/memcpy_zero.rs
new file mode 100644
index 0000000000000..5283fea4cb989
--- /dev/null
+++ b/src/tools/miri/tests/fail/shims/memcpy_zero.rs
@@ -0,0 +1,12 @@
+//@ignore-target-windows: No libc on Windows
+//@compile-flags: -Zmiri-permissive-provenance
+// C's memcpy is 0 bytes is UB for some pointers that are allowed in Rust's `copy_nonoverlapping`.
+
+fn main() {
+    let from = 42 as *const u8;
+    let to = 23 as *mut u8;
+    unsafe {
+        to.copy_from(from, 0); // this is fine
+        libc::memcpy(to.cast(), from.cast(), 0); //~ERROR: dangling
+    }
+}
diff --git a/src/tools/miri/tests/fail/shims/memcpy_zero.stderr b/src/tools/miri/tests/fail/shims/memcpy_zero.stderr
new file mode 100644
index 0000000000000..7c1c3fe20c4e5
--- /dev/null
+++ b/src/tools/miri/tests/fail/shims/memcpy_zero.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: out-of-bounds pointer use: 0x17[noalloc] is a dangling pointer (it has no provenance)
+  --> $DIR/memcpy_zero.rs:LL:CC
+   |
+LL |         libc::memcpy(to.cast(), from.cast(), 0);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ out-of-bounds pointer use: 0x17[noalloc] is a dangling pointer (it has no provenance)
+   |
+   = 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
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/memcpy_zero.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/fail/shims/memrchr_null.rs b/src/tools/miri/tests/fail/shims/memrchr_null.rs
new file mode 100644
index 0000000000000..b6707d558d8f6
--- /dev/null
+++ b/src/tools/miri/tests/fail/shims/memrchr_null.rs
@@ -0,0 +1,11 @@
+//@ignore-target-windows: No libc on Windows
+//@ignore-target-apple: No `memrchr` on some apple targets
+
+use std::ptr;
+
+// null is explicitly called out as UB in the C docs.
+fn main() {
+    unsafe {
+        libc::memrchr(ptr::null(), 0, 0); //~ERROR: dangling
+    }
+}
diff --git a/src/tools/miri/tests/fail/shims/memrchr_null.stderr b/src/tools/miri/tests/fail/shims/memrchr_null.stderr
new file mode 100644
index 0000000000000..b5b7630e7fd0d
--- /dev/null
+++ b/src/tools/miri/tests/fail/shims/memrchr_null.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: memory access failed: null pointer is a dangling pointer (it has no provenance)
+  --> $DIR/memrchr_null.rs:LL:CC
+   |
+LL |         libc::memrchr(ptr::null(), 0, 0);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: null pointer is a dangling pointer (it has no provenance)
+   |
+   = 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
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/memrchr_null.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to previous error
+
diff --git a/src/tools/miri/tests/pass/vec.rs b/src/tools/miri/tests/pass/vec.rs
index 048f7d1c3512a..4ab2bcb7f2281 100644
--- a/src/tools/miri/tests/pass/vec.rs
+++ b/src/tools/miri/tests/pass/vec.rs
@@ -100,7 +100,7 @@ fn vec_push_ptr_stable() {
     v.push(0);
     let v0 = unsafe { &mut *(&mut v[0] as *mut _) }; // laundering the lifetime -- we take care that `v` does not reallocate, so that's okay.
     v.push(1);
-    let _val = *v0;
+    *v0 = *v0;
 }
 
 fn vec_extend_ptr_stable() {
@@ -109,23 +109,23 @@ fn vec_extend_ptr_stable() {
     let v0 = unsafe { &mut *(&mut v[0] as *mut _) }; // laundering the lifetime -- we take care that `v` does not reallocate, so that's okay.
     // `slice::Iter` (with `T: Copy`) specialization
     v.extend(&[1]);
-    let _val = *v0;
+    *v0 = *v0;
     // `vec::IntoIter` specialization
     v.extend(vec![2]);
-    let _val = *v0;
+    *v0 = *v0;
     // `TrustedLen` specialization
     v.extend(std::iter::once(3));
-    let _val = *v0;
+    *v0 = *v0;
     // base case
     v.extend(std::iter::once(3).filter(|_| true));
-    let _val = *v0;
+    *v0 = *v0;
 }
 
 fn vec_truncate_ptr_stable() {
     let mut v = vec![0; 10];
     let v0 = unsafe { &mut *(&mut v[0] as *mut _) }; // laundering the lifetime -- we take care that `v` does not reallocate, so that's okay.
     v.truncate(5);
-    let _val = *v0;
+    *v0 = *v0;
 }
 
 fn push_str_ptr_stable() {
diff --git a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
index 011a253c6ff61..a7c9e4845c70a 100644
--- a/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
+++ b/tests/assembly/stack-protector/stack-protector-heuristics-effect.rs
@@ -3,6 +3,7 @@
 // ignore-macos slightly different policy on stack protection of arrays
 // ignore-windows stack check code uses different function names
 // ignore-nvptx64 stack protector is not supported
+// ignore-wasm32-bare
 // [all] compile-flags: -Z stack-protector=all
 // [strong] compile-flags: -Z stack-protector=strong
 // [basic] compile-flags: -Z stack-protector=basic
diff --git a/tests/assembly/wasm_exceptions.rs b/tests/assembly/wasm_exceptions.rs
new file mode 100644
index 0000000000000..b7d20881b624f
--- /dev/null
+++ b/tests/assembly/wasm_exceptions.rs
@@ -0,0 +1,60 @@
+// only-wasm32-bare
+// assembly-output: emit-asm
+// compile-flags: -C target-feature=+exception-handling
+// compile-flags: -C panic=unwind
+// compile-flags: -C llvm-args=-wasm-enable-eh
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+#![feature(rustc_attrs)]
+
+extern {
+    fn may_panic();
+
+    #[rustc_nounwind]
+    fn log_number(number: usize);
+}
+
+struct LogOnDrop;
+
+impl Drop for LogOnDrop {
+    fn drop(&mut self) {
+        unsafe { log_number(0); }
+    }
+}
+
+// CHECK-LABEL: test_cleanup:
+#[no_mangle]
+pub fn test_cleanup() {
+    let _log_on_drop = LogOnDrop;
+    unsafe { may_panic(); }
+
+    // CHECK-NOT: call
+    // CHECK: try
+    // CHECK: call may_panic
+    // CHECK: catch_all
+    // CHECK: rethrow
+    // CHECK: end_try
+}
+
+// CHECK-LABEL: test_rtry:
+#[no_mangle]
+pub fn test_rtry() {
+    unsafe {
+        core::intrinsics::r#try(|_| {
+            may_panic();
+        }, core::ptr::null_mut(), |data, exception| {
+            log_number(data as usize);
+            log_number(exception as usize);
+        });
+    }
+
+    // CHECK-NOT: call
+    // CHECK: try
+    // CHECK: call may_panic
+    // CHECK: catch
+    // CHECK: call log_number
+    // CHECK: call log_number
+    // CHECK-NOT: rethrow
+    // CHECK: end_try
+}
diff --git a/tests/codegen/repr-transparent-aggregates-1.rs b/tests/codegen/repr-transparent-aggregates-1.rs
index 9c4b0e58e7187..ba3ba272efbb8 100644
--- a/tests/codegen/repr-transparent-aggregates-1.rs
+++ b/tests/codegen/repr-transparent-aggregates-1.rs
@@ -11,6 +11,7 @@
 // ignore-s390x
 // ignore-windows
 // ignore-loongarch64
+// ignore-wasm32-bare
 // See repr-transparent.rs
 
 #![feature(transparent_unions)]
diff --git a/tests/codegen/wasm_exceptions.rs b/tests/codegen/wasm_exceptions.rs
new file mode 100644
index 0000000000000..2b2359f5b6caa
--- /dev/null
+++ b/tests/codegen/wasm_exceptions.rs
@@ -0,0 +1,51 @@
+// only-wasm32-bare
+// compile-flags: -C panic=unwind
+
+#![crate_type = "lib"]
+#![feature(core_intrinsics)]
+#![feature(rustc_attrs)]
+
+extern {
+    fn may_panic();
+
+    #[rustc_nounwind]
+    fn log_number(number: usize);
+}
+
+struct LogOnDrop;
+
+impl Drop for LogOnDrop {
+    fn drop(&mut self) {
+        unsafe { log_number(0); }
+    }
+}
+
+// CHECK-LABEL: @test_cleanup() {{.*}} @__gxx_wasm_personality_v0
+#[no_mangle]
+pub fn test_cleanup() {
+    let _log_on_drop = LogOnDrop;
+    unsafe { may_panic(); }
+
+    // CHECK-NOT: call
+    // CHECK: invoke void @may_panic()
+    // CHECK: %cleanuppad = cleanuppad within none []
+}
+
+// CHECK-LABEL: @test_rtry() {{.*}} @__gxx_wasm_personality_v0
+#[no_mangle]
+pub fn test_rtry() {
+    unsafe {
+        core::intrinsics::r#try(|_| {
+            may_panic();
+        }, core::ptr::null_mut(), |data, exception| {
+            log_number(data as usize);
+            log_number(exception as usize);
+        });
+    }
+
+    // CHECK-NOT: call
+    // CHECK: invoke void @may_panic()
+    // CHECK: {{.*}} = catchswitch within none [label {{.*}}] unwind to caller
+    // CHECK: {{.*}} = catchpad within {{.*}} [ptr null]
+    // CHECK: catchret
+}
diff --git a/tests/run-make/wasm-exceptions-nostd/Makefile b/tests/run-make/wasm-exceptions-nostd/Makefile
new file mode 100644
index 0000000000000..34755ec14b745
--- /dev/null
+++ b/tests/run-make/wasm-exceptions-nostd/Makefile
@@ -0,0 +1,12 @@
+include ../tools.mk
+
+# only-wasm32-bare
+
+# Add a few command line args to make exceptions work
+RUSTC := $(RUSTC) -C llvm-args=-wasm-enable-eh
+RUSTC := $(RUSTC) -C target-feature=+exception-handling
+RUSTC := $(RUSTC) -C panic=unwind
+
+all:
+	$(RUSTC) src/lib.rs --target wasm32-unknown-unknown
+	$(NODE) verify.mjs $(TMPDIR)/lib.wasm
diff --git a/tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs b/tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs
new file mode 100644
index 0000000000000..572d253309cee
--- /dev/null
+++ b/tests/run-make/wasm-exceptions-nostd/src/arena_alloc.rs
@@ -0,0 +1,67 @@
+use core::alloc::{GlobalAlloc, Layout};
+use core::cell::UnsafeCell;
+
+#[global_allocator]
+static ALLOCATOR: ArenaAllocator = ArenaAllocator::new();
+
+/// Very simple allocator which never deallocates memory
+///
+/// Based on the example from
+/// https://doc.rust-lang.org/stable/std/alloc/trait.GlobalAlloc.html
+pub struct ArenaAllocator {
+    arena: UnsafeCell<Arena>,
+}
+
+impl ArenaAllocator {
+    pub const fn new() -> Self {
+        Self {
+            arena: UnsafeCell::new(Arena::new()),
+        }
+    }
+}
+
+/// Safe because we are singlethreaded
+unsafe impl Sync for ArenaAllocator {}
+
+unsafe impl GlobalAlloc for ArenaAllocator {
+    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+        let arena = &mut *self.arena.get();
+        arena.alloc(layout)
+    }
+
+    unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {}
+}
+
+const ARENA_SIZE: usize = 64 * 1024; // more than enough
+
+#[repr(C, align(4096))]
+struct Arena {
+    buf: [u8; ARENA_SIZE], // aligned at 4096
+    allocated: usize,
+}
+
+impl Arena {
+    pub const fn new() -> Self {
+        Self {
+            buf: [0x55; ARENA_SIZE],
+            allocated: 0,
+        }
+    }
+
+    pub unsafe fn alloc(&mut self, layout: Layout) -> *mut u8 {
+        if layout.align() > 4096 || layout.size() > ARENA_SIZE {
+            return core::ptr::null_mut();
+        }
+
+        let align_minus_one = layout.align() - 1;
+        let start = (self.allocated + align_minus_one) & !align_minus_one; // round up
+        let new_cursor = start + layout.size();
+
+        if new_cursor >= ARENA_SIZE {
+            return core::ptr::null_mut();
+        }
+
+        self.allocated = new_cursor;
+        self.buf.as_mut_ptr().add(start)
+    }
+}
diff --git a/tests/run-make/wasm-exceptions-nostd/src/lib.rs b/tests/run-make/wasm-exceptions-nostd/src/lib.rs
new file mode 100644
index 0000000000000..7049d2fd9e0d1
--- /dev/null
+++ b/tests/run-make/wasm-exceptions-nostd/src/lib.rs
@@ -0,0 +1,60 @@
+#![no_std]
+#![crate_type = "cdylib"]
+
+// Allow a few unstable features because we create a panic
+// runtime for native wasm exceptions from scratch
+
+#![feature(core_intrinsics)]
+#![feature(lang_items)]
+#![feature(link_llvm_intrinsics)]
+#![feature(panic_info_message)]
+
+extern crate alloc;
+
+/// This module allows us to use `Box`, `String`, ... even in no-std
+mod arena_alloc;
+
+/// This module allows logging text, even in no-std
+mod logging;
+
+/// This module allows exceptions, even in no-std
+#[cfg(target_arch = "wasm32")]
+mod panicking;
+
+use alloc::boxed::Box;
+use alloc::string::String;
+
+struct LogOnDrop;
+
+impl Drop for LogOnDrop {
+    fn drop(&mut self) {
+        logging::log_str("Dropped");
+    }
+}
+
+#[allow(unreachable_code)]
+#[allow(unconditional_panic)]
+#[no_mangle]
+pub extern "C" fn start() -> usize {
+    let data = 0x1234usize as *mut u8; // Something to recognize
+
+    unsafe {
+        core::intrinsics::r#try(|data: *mut u8| {
+            let _log_on_drop = LogOnDrop;
+
+            logging::log_str(&alloc::format!("`r#try` called with ptr {:?}", data));
+            let x = [12];
+            let _ = x[4]; // should panic
+
+            logging::log_str("This line should not be visible! :(");
+        }, data, |data, exception| {
+            let exception = *Box::from_raw(exception as *mut String);
+            logging::log_str("Caught something!");
+            logging::log_str(&alloc::format!("  data     : {:?}", data));
+            logging::log_str(&alloc::format!("  exception: {:?}", exception));
+        });
+    }
+
+    logging::log_str("This program terminates correctly.");
+    0
+}
diff --git a/tests/run-make/wasm-exceptions-nostd/src/logging.rs b/tests/run-make/wasm-exceptions-nostd/src/logging.rs
new file mode 100644
index 0000000000000..569d03ec82f57
--- /dev/null
+++ b/tests/run-make/wasm-exceptions-nostd/src/logging.rs
@@ -0,0 +1,9 @@
+extern "C" {
+    fn __log_utf8(ptr: *const u8, size: usize);
+}
+
+pub fn log_str(text: &str) {
+    unsafe {
+        __log_utf8(text.as_ptr(), text.len());
+    }
+}
diff --git a/tests/run-make/wasm-exceptions-nostd/src/panicking.rs b/tests/run-make/wasm-exceptions-nostd/src/panicking.rs
new file mode 100644
index 0000000000000..4a8923fd43db6
--- /dev/null
+++ b/tests/run-make/wasm-exceptions-nostd/src/panicking.rs
@@ -0,0 +1,29 @@
+#[lang = "eh_personality"]
+fn eh_personality() {}
+
+mod internal {
+    extern "C" {
+        #[link_name = "llvm.wasm.throw"]
+        pub fn wasm_throw(tag: i32, ptr: *mut u8) -> !;
+    }
+}
+
+unsafe fn wasm_throw(ptr: *mut u8) -> ! {
+    internal::wasm_throw(0, ptr);
+}
+
+#[panic_handler]
+fn panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
+    use alloc::boxed::Box;
+    use alloc::string::ToString;
+
+    let msg = info
+        .message()
+        .map(|msg| msg.to_string())
+        .unwrap_or("(no message)".to_string());
+    let exception = Box::new(msg.to_string());
+    unsafe {
+        let exception_raw = Box::into_raw(exception);
+        wasm_throw(exception_raw as *mut u8);
+    }
+}
diff --git a/tests/run-make/wasm-exceptions-nostd/verify.mjs b/tests/run-make/wasm-exceptions-nostd/verify.mjs
new file mode 100644
index 0000000000000..e6c44d89d331d
--- /dev/null
+++ b/tests/run-make/wasm-exceptions-nostd/verify.mjs
@@ -0,0 +1,75 @@
+import fs from 'fs';
+
+const dec = new TextDecoder("utf-8");
+
+if (process.argv.length != 3) {
+    console.log("Usage: node verify.mjs <wasm-file>");
+    process.exit(0);
+}
+
+const wasmfile = process.argv[2];
+if (!fs.existsSync(wasmfile)) {
+    console.log("Error: File not found:", wasmfile);
+    process.exit(1);
+}
+
+const wasmBuffer = fs.readFileSync(wasmfile);
+
+async function main() {
+
+    let memory = new ArrayBuffer(0) // will be changed after instantiate
+
+    const captured_output = [];
+
+    const imports = {
+        env: {
+            __log_utf8: (ptr, size) => {
+                const str = dec.decode(new DataView(memory, ptr, size));
+                captured_output.push(str);
+                console.log(str);
+            }
+        }
+    };
+
+    const wasmModule = await WebAssembly.instantiate(wasmBuffer, imports);
+    memory = wasmModule.instance.exports.memory.buffer;
+
+    const start = wasmModule.instance.exports.start;
+    const return_code = start();
+
+    console.log("Return-Code:", return_code);
+
+    if (return_code !== 0) {
+        console.error("Expected return code 0");
+        process.exit(return_code);
+    }
+
+    const expected_output = [
+        '`r#try` called with ptr 0x1234',
+        'Dropped',
+        'Caught something!',
+        '  data     : 0x1234',
+        '  exception: "index out of bounds: the len is 1 but the index is 4"',
+        'This program terminates correctly.',
+    ];
+    
+    assert_equal(captured_output, expected_output);
+}
+
+function assert_equal(captured_output, expected_output) {
+    if (captured_output.length != expected_output.length) {
+        console.error("Unexpected number of output lines. Got", captured_output.length, "but expected", expected_output.length);
+        process.exit(1); // exit with error
+    }
+
+    for (let idx = 0; idx < expected_output.length; ++idx) {
+        if (captured_output[idx] !== expected_output[idx]) {
+            console.error("Unexpected output");
+            console.error("[got]     ", captured_output[idx]);
+            console.error("[expected]", expected_output[idx]);
+            process.exit(2); // exit with error
+        }
+    }
+}
+
+await main();
\ No newline at end of file
diff --git a/tests/ui/resolve/issue-2356.stderr b/tests/ui/resolve/issue-2356.stderr
index 313b3e30dd956..30f5f05952664 100644
--- a/tests/ui/resolve/issue-2356.stderr
+++ b/tests/ui/resolve/issue-2356.stderr
@@ -1,18 +1,3 @@
-error[E0425]: cannot find function `default` in this scope
-  --> $DIR/issue-2356.rs:31:5
-   |
-LL |     default();
-   |     ^^^^^^^
-   |
-help: you might have meant to call the associated function
-   |
-LL |     Self::default();
-   |     ~~~~~~~~~~~~~
-help: consider importing this function
-   |
-LL + use std::default::default;
-   |
-
 error[E0425]: cannot find value `whiskers` in this scope
   --> $DIR/issue-2356.rs:39:5
    |
@@ -64,6 +49,12 @@ error[E0425]: cannot find function `clone` in this scope
 LL |     clone();
    |     ^^^^^ help: you might have meant to call the method: `self.clone`
 
+error[E0425]: cannot find function `default` in this scope
+  --> $DIR/issue-2356.rs:31:5
+   |
+LL |     default();
+   |     ^^^^^^^ help: you might have meant to call the associated function: `Self::default`
+
 error[E0425]: cannot find function `shave` in this scope
   --> $DIR/issue-2356.rs:41:5
    |