about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorThe Miri Cronjob Bot <miri@cron.bot>2025-06-10 05:02:33 +0000
committerThe Miri Cronjob Bot <miri@cron.bot>2025-06-10 05:02:33 +0000
commitc601681d95b0f984850d84ab60a47de7e1d673a8 (patch)
tree83928fd3e3f9c2f023d470342462daf721cf3aab /src
parent5eb5f2a2269c1f83a65cde57fb0ce0562d931be3 (diff)
parentc6768de2d63de7a41124a0fb8fc78f9e26111c01 (diff)
Merge from rustc
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/bootstrap.py1
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs63
-rw-r--r--src/bootstrap/src/core/sanity.rs2
-rw-r--r--src/bootstrap/src/lib.rs38
-rw-r--r--src/ci/citool/src/jobs.rs6
-rw-r--r--src/ci/citool/src/main.rs1
-rw-r--r--src/ci/citool/tests/jobs.rs24
-rwxr-xr-xsrc/ci/docker/scripts/rfl-build.sh3
-rw-r--r--src/ci/github-actions/jobs.yml9
-rwxr-xr-xsrc/ci/run.sh8
-rwxr-xr-xsrc/ci/scripts/checkout-submodules.sh4
-rw-r--r--src/doc/rustc-dev-guide/rust-version2
-rw-r--r--src/doc/rustc-dev-guide/src/SUMMARY.md4
-rw-r--r--src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md2
-rw-r--r--src/doc/rustc-dev-guide/src/getting-started.md4
-rw-r--r--src/doc/rustc-dev-guide/src/notification-groups/about.md14
-rw-r--r--src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md90
-rw-r--r--src/doc/rustc-dev-guide/src/notification-groups/llvm.md38
-rw-r--r--src/doc/rustc-dev-guide/src/rustdoc-internals.md29
-rw-r--r--src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md14
-rw-r--r--src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md3
-rw-r--r--src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md191
-rw-r--r--src/doc/rustc-dev-guide/src/rustdoc.md42
-rw-r--r--src/doc/rustc-dev-guide/src/tests/compiletest.md34
-rw-r--r--src/doc/rustc-dev-guide/src/tests/directives.md13
-rw-r--r--src/doc/rustc-dev-guide/src/tests/ui.md8
-rw-r--r--src/doc/rustc-dev-guide/triagebot.toml17
-rw-r--r--src/doc/rustc/src/platform-support.md2
-rw-r--r--src/doc/rustc/src/platform-support/loongarch-none.md43
-rw-r--r--src/librustdoc/clean/cfg.rs1
-rw-r--r--src/tools/build-manifest/src/main.rs2
-rw-r--r--src/tools/compiletest/src/common.rs1
-rw-r--r--src/tools/compiletest/src/directive-list.rs2
-rw-r--r--src/tools/miri/cargo-miri/Cargo.lock43
-rw-r--r--src/tools/miri/cargo-miri/Cargo.toml2
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs262
-rw-r--r--src/tools/miri/src/math.rs14
-rw-r--r--src/tools/miri/src/shims/alloc.rs5
-rw-r--r--src/tools/miri/tests/pass/float.rs115
39 files changed, 772 insertions, 384 deletions
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index c60c6b8db64..d8c6be78247 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -394,6 +394,7 @@ def default_build_triple(verbose):
         "i686": "i686",
         "i686-AT386": "i686",
         "i786": "i686",
+        "loongarch32": "loongarch32",
         "loongarch64": "loongarch64",
         "m68k": "m68k",
         "csky": "csky",
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index 1e9af68a92d..cf7f962d026 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -11,7 +11,7 @@ use crate::utils::build_stamp;
 use crate::utils::helpers::{self, LldThreads, check_cfg_arg, linker_args, linker_flags};
 use crate::{
     BootstrapCommand, CLang, Compiler, Config, DocTests, DryRun, EXTRA_CHECK_CFGS, GitRepo, Mode,
-    TargetSelection, command, prepare_behaviour_dump_dir, t,
+    RemapScheme, TargetSelection, command, prepare_behaviour_dump_dir, t,
 };
 
 /// Represents flag values in `String` form with whitespace delimiter to pass it to the compiler
@@ -636,6 +636,15 @@ impl Builder<'_> {
         for (restricted_mode, name, values) in EXTRA_CHECK_CFGS {
             if restricted_mode.is_none() || *restricted_mode == Some(mode) {
                 rustflags.arg(&check_cfg_arg(name, *values));
+
+                if *name == "bootstrap" {
+                    // Cargo doesn't pass RUSTFLAGS to proc_macros:
+                    // https://github.com/rust-lang/cargo/issues/4423
+                    // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`.
+                    // We also declare that the flag is expected, which we need to do to not
+                    // get warnings about it being unexpected.
+                    hostflags.arg(check_cfg_arg(name, *values));
+                }
             }
         }
 
@@ -645,13 +654,6 @@ impl Builder<'_> {
         if stage == 0 {
             hostflags.arg("--cfg=bootstrap");
         }
-        // Cargo doesn't pass RUSTFLAGS to proc_macros:
-        // https://github.com/rust-lang/cargo/issues/4423
-        // Thus, if we are on stage 0, we explicitly set `--cfg=bootstrap`.
-        // We also declare that the flag is expected, which we need to do to not
-        // get warnings about it being unexpected.
-        hostflags.arg("-Zunstable-options");
-        hostflags.arg("--check-cfg=cfg(bootstrap)");
 
         // FIXME: It might be better to use the same value for both `RUSTFLAGS` and `RUSTDOCFLAGS`,
         // but this breaks CI. At the very least, stage0 `rustdoc` needs `--cfg bootstrap`. See
@@ -920,13 +922,46 @@ impl Builder<'_> {
             hostflags.arg(format!("-Ctarget-feature={sign}crt-static"));
         }
 
-        if let Some(map_to) = self.build.debuginfo_map_to(GitRepo::Rustc) {
-            let map = format!("{}={}", self.build.src.display(), map_to);
-            cargo.env("RUSTC_DEBUGINFO_MAP", map);
+        // `rustc` needs to know the remapping scheme, in order to know how to reverse it (unremap)
+        // later. Two env vars are set and made available to the compiler
+        //
+        // - `CFG_VIRTUAL_RUST_SOURCE_BASE_DIR`: `rust-src` remap scheme (`NonCompiler`)
+        // - `CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR`: `rustc-dev` remap scheme (`Compiler`)
+        //
+        // Keep this scheme in sync with `rustc_metadata::rmeta::decoder`'s
+        // `try_to_translate_virtual_to_real`.
+        //
+        // `RUSTC_DEBUGINFO_MAP` is used to pass through to the underlying rustc
+        // `--remap-path-prefix`.
+        match mode {
+            Mode::Rustc | Mode::Codegen => {
+                if let Some(ref map_to) =
+                    self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler)
+                {
+                    cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to);
+                }
 
-            // `rustc` needs to know the virtual `/rustc/$hash` we're mapping to,
-            // in order to opportunistically reverse it later.
-            cargo.env("CFG_VIRTUAL_RUST_SOURCE_BASE_DIR", map_to);
+                if let Some(ref map_to) =
+                    self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::Compiler)
+                {
+                    // When building compiler sources, we want to apply the compiler remap scheme.
+                    cargo.env(
+                        "RUSTC_DEBUGINFO_MAP",
+                        format!("{}={}", self.build.src.display(), map_to),
+                    );
+                    cargo.env("CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR", map_to);
+                }
+            }
+            Mode::Std | Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd => {
+                if let Some(ref map_to) =
+                    self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler)
+                {
+                    cargo.env(
+                        "RUSTC_DEBUGINFO_MAP",
+                        format!("{}={}", self.build.src.display(), map_to),
+                    );
+                }
+            }
         }
 
         if self.config.rust_remap_debuginfo {
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index af4ec679d08..59ae303e21e 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -34,6 +34,8 @@ pub struct Finder {
 // Targets can be removed from this list once they are present in the stage0 compiler (usually by updating the beta compiler of the bootstrap).
 const STAGE0_MISSING_TARGETS: &[&str] = &[
     // just a dummy comment so the list doesn't get onelined
+    "loongarch32-unknown-none",
+    "loongarch32-unknown-none-softfloat",
 ];
 
 /// Minimum version threshold for libstdc++ required when using prebuilt LLVM
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 07772b8932d..7db57889009 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -81,7 +81,10 @@ const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
 /// (Mode restriction, config name, config values (if any))
 #[expect(clippy::type_complexity)] // It's fine for hard-coded list and type is explained above.
 const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
-    (None, "bootstrap", None),
+    (Some(Mode::Rustc), "bootstrap", None),
+    (Some(Mode::Codegen), "bootstrap", None),
+    (Some(Mode::ToolRustc), "bootstrap", None),
+    (Some(Mode::ToolStd), "bootstrap", None),
     (Some(Mode::Rustc), "llvm_enzyme", None),
     (Some(Mode::Codegen), "llvm_enzyme", None),
     (Some(Mode::ToolRustc), "llvm_enzyme", None),
@@ -272,6 +275,16 @@ impl Mode {
     }
 }
 
+/// When `rust.rust_remap_debuginfo` is requested, the compiler needs to know how to
+/// opportunistically unremap compiler vs non-compiler sources. We use two schemes,
+/// [`RemapScheme::Compiler`] and [`RemapScheme::NonCompiler`].
+pub enum RemapScheme {
+    /// The [`RemapScheme::Compiler`] scheme will remap to `/rustc-dev/{hash}`.
+    Compiler,
+    /// The [`RemapScheme::NonCompiler`] scheme will remap to `/rustc/{hash}`.
+    NonCompiler,
+}
+
 #[derive(Debug, Hash, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
 pub enum CLang {
     C,
@@ -1217,7 +1230,7 @@ Executed at: {executed_at}"#,
         })
     }
 
-    fn debuginfo_map_to(&self, which: GitRepo) -> Option<String> {
+    fn debuginfo_map_to(&self, which: GitRepo, remap_scheme: RemapScheme) -> Option<String> {
         if !self.config.rust_remap_debuginfo {
             return None;
         }
@@ -1225,7 +1238,24 @@ Executed at: {executed_at}"#,
         match which {
             GitRepo::Rustc => {
                 let sha = self.rust_sha().unwrap_or(&self.version);
-                Some(format!("/rustc/{sha}"))
+
+                match remap_scheme {
+                    RemapScheme::Compiler => {
+                        // For compiler sources, remap via `/rustc-dev/{sha}` to allow
+                        // distinguishing between compiler sources vs library sources, since
+                        // `rustc-dev` dist component places them under
+                        // `$sysroot/lib/rustlib/rustc-src/rust` as opposed to `rust-src`'s
+                        // `$sysroot/lib/rustlib/src/rust`.
+                        //
+                        // Keep this scheme in sync with `rustc_metadata::rmeta::decoder`'s
+                        // `try_to_translate_virtual_to_real`.
+                        Some(format!("/rustc-dev/{sha}"))
+                    }
+                    RemapScheme::NonCompiler => {
+                        // For non-compiler sources, use `/rustc/{sha}` remapping scheme.
+                        Some(format!("/rustc/{sha}"))
+                    }
+                }
             }
             GitRepo::Llvm => Some(String::from("/rustc/llvm")),
         }
@@ -1292,7 +1322,7 @@ Executed at: {executed_at}"#,
             base.push("-fno-omit-frame-pointer".into());
         }
 
-        if let Some(map_to) = self.debuginfo_map_to(which) {
+        if let Some(map_to) = self.debuginfo_map_to(which, RemapScheme::NonCompiler) {
             let map = format!("{}={}", self.src.display(), map_to);
             let cc = self.cc(target);
             if cc.ends_with("clang") || cc.ends_with("gcc") {
diff --git a/src/ci/citool/src/jobs.rs b/src/ci/citool/src/jobs.rs
index 2884ae08ea8..81e002edb15 100644
--- a/src/ci/citool/src/jobs.rs
+++ b/src/ci/citool/src/jobs.rs
@@ -161,6 +161,8 @@ pub enum RunType {
     TryJob { job_patterns: Option<Vec<String>> },
     /// Merge attempt workflow
     AutoJob,
+    /// Fake job only used for sharing Github Actions cache.
+    MasterJob,
 }
 
 /// Maximum number of custom try jobs that can be requested in a single
@@ -210,6 +212,7 @@ fn calculate_jobs(
             (jobs, "try", &db.envs.try_env)
         }
         RunType::AutoJob => (db.auto_jobs.clone(), "auto", &db.envs.auto_env),
+        RunType::MasterJob => return Ok(vec![]),
     };
     let jobs = substitute_github_vars(jobs.clone())
         .context("Failed to substitute GitHub context variables in jobs")?;
@@ -262,7 +265,7 @@ pub fn calculate_job_matrix(
     eprintln!("Run type: {run_type:?}");
 
     let jobs = calculate_jobs(&run_type, &db, channel)?;
-    if jobs.is_empty() {
+    if jobs.is_empty() && !matches!(run_type, RunType::MasterJob) {
         return Err(anyhow::anyhow!("Computed job list is empty"));
     }
 
@@ -270,6 +273,7 @@ pub fn calculate_job_matrix(
         RunType::PullRequest => "pr",
         RunType::TryJob { .. } => "try",
         RunType::AutoJob => "auto",
+        RunType::MasterJob => "master",
     };
 
     eprintln!("Output");
diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs
index bb73a5ef909..fe1b36673a1 100644
--- a/src/ci/citool/src/main.rs
+++ b/src/ci/citool/src/main.rs
@@ -47,6 +47,7 @@ impl GitHubContext {
                 Some(RunType::TryJob { job_patterns: patterns })
             }
             ("push", "refs/heads/auto") => Some(RunType::AutoJob),
+            ("push", "refs/heads/master") => Some(RunType::MasterJob),
             _ => None,
         }
     }
diff --git a/src/ci/citool/tests/jobs.rs b/src/ci/citool/tests/jobs.rs
index fcdca899e06..83f2fc0ed1f 100644
--- a/src/ci/citool/tests/jobs.rs
+++ b/src/ci/citool/tests/jobs.rs
@@ -45,17 +45,31 @@ fn pr_jobs() {
     "#);
 }
 
+#[test]
+fn master_jobs() {
+    let stdout = get_matrix("push", "commit", "refs/heads/master");
+    insta::assert_snapshot!(stdout, @r#"
+    jobs=[]
+    run_type=master
+    "#);
+}
+
 fn get_matrix(event_name: &str, commit_msg: &str, branch_ref: &str) -> String {
-    let output = Command::new("cargo")
-        .args(["run", "-q", "calculate-job-matrix", "--jobs-file", TEST_JOBS_YML_PATH])
+    let path = std::env::var("PATH");
+    let mut cmd = Command::new("cargo");
+    cmd.args(["run", "-q", "calculate-job-matrix", "--jobs-file", TEST_JOBS_YML_PATH])
+        .env_clear()
         .env("GITHUB_EVENT_NAME", event_name)
         .env("COMMIT_MESSAGE", commit_msg)
         .env("GITHUB_REF", branch_ref)
         .env("GITHUB_RUN_ID", "123")
         .env("GITHUB_RUN_ATTEMPT", "1")
-        .stdout(Stdio::piped())
-        .output()
-        .expect("Failed to execute command");
+        .stdout(Stdio::piped());
+    if let Ok(path) = path {
+        cmd.env("PATH", path);
+    }
+
+    let output = cmd.output().expect("Failed to execute command");
 
     let stdout = String::from_utf8(output.stdout).unwrap();
     let stderr = String::from_utf8(output.stderr).unwrap();
diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh
index fa18f67583f..c5992891398 100755
--- a/src/ci/docker/scripts/rfl-build.sh
+++ b/src/ci/docker/scripts/rfl-build.sh
@@ -2,8 +2,7 @@
 
 set -euo pipefail
 
-# https://github.com/Rust-for-Linux/linux/issues/1163
-LINUX_VERSION=3ca02fc80cc4fdac63aaa6796642f1e07be591d6
+LINUX_VERSION=v6.16-rc1
 
 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt
 ../x.py build --stage 2 library rustdoc clippy rustfmt
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index b6b2792d0ec..43c77d1ddf7 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -128,6 +128,11 @@ pr:
     <<: *job-linux-4c
   - name: mingw-check-tidy
     continue_on_error: true
+    free_disk: false
+    env:
+      # This submodule is expensive to checkout, and it should not be needed for
+      # tidy. This speeds up the PR CI job by ~1 minute.
+      SKIP_SUBMODULES: src/gcc
     <<: *job-linux-4c
   - name: x86_64-gnu-llvm-19
     env:
@@ -305,6 +310,10 @@ auto:
   - name: mingw-check-2
     <<: *job-linux-4c
 
+  - name: mingw-check-tidy
+    free_disk: false
+    <<: *job-linux-4c
+
   - name: test-various
     <<: *job-linux-4c
 
diff --git a/src/ci/run.sh b/src/ci/run.sh
index b6143af632d..e0c00dc1925 100755
--- a/src/ci/run.sh
+++ b/src/ci/run.sh
@@ -186,9 +186,11 @@ else
   # Download GCC from CI on test builders
   RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set gcc.download-ci-gcc=true"
 
-  if [ "$NO_DOWNLOAD_CI_RUSTC" = "" ]; then
-    RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.download-rustc=if-unchanged"
-  fi
+  # download-rustc seems to be broken on CI after the stage0 redesign
+  # Disable it until these issues are debugged and resolved
+#  if [ "$NO_DOWNLOAD_CI_RUSTC" = "" ]; then
+#    RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.download-rustc=if-unchanged"
+#  fi
 fi
 
 if [ "$ENABLE_GCC_CODEGEN" = "1" ]; then
diff --git a/src/ci/scripts/checkout-submodules.sh b/src/ci/scripts/checkout-submodules.sh
index 5bb343241ae..3b646587dc2 100755
--- a/src/ci/scripts/checkout-submodules.sh
+++ b/src/ci/scripts/checkout-submodules.sh
@@ -55,7 +55,11 @@ for i in ${!modules[@]}; do
         bg_pids[${i}]=$!
         continue
     else
+      # Submodule paths contained in SKIP_SUBMODULES (comma-separated list) will not be
+      # checked out.
+      if [ -z "${SKIP_SUBMODULES:-}" ] || [[ ! ",$SKIP_SUBMODULES," = *",$module,"* ]]; then
         use_git="$use_git $module"
+      fi
     fi
 done
 retry sh -c "git submodule deinit -f $use_git && \
diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version
index 8b48bd518bd..c8721bb3600 100644
--- a/src/doc/rustc-dev-guide/rust-version
+++ b/src/doc/rustc-dev-guide/rust-version
@@ -1 +1 @@
-c68032fd4c442d275f4daa571ba19c076106b490
+c31cccb7b5cc098b1a8c1794ed38d7fdbec0ccb0
diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md
index a7b76233d19..cba8eac617d 100644
--- a/src/doc/rustc-dev-guide/src/SUMMARY.md
+++ b/src/doc/rustc-dev-guide/src/SUMMARY.md
@@ -63,10 +63,8 @@
 - [Notification groups](notification-groups/about.md)
     - [Apple](notification-groups/apple.md)
     - [ARM](notification-groups/arm.md)
-    - [Cleanup Crew](notification-groups/cleanup-crew.md)
     - [Emscripten](notification-groups/emscripten.md)
     - [Fuchsia](notification-groups/fuchsia.md)
-    - [LLVM](notification-groups/llvm.md)
     - [RISC-V](notification-groups/risc-v.md)
     - [Rust for Linux](notification-groups/rust-for-linux.md)
     - [WASI](notification-groups/wasi.md)
@@ -101,6 +99,8 @@
 - [Rustdoc internals](./rustdoc-internals.md)
     - [Search](./rustdoc-internals/search.md)
 	- [The `rustdoc` test suite](./rustdoc-internals/rustdoc-test-suite.md)
+	- [The `rustdoc-gui` test suite](./rustdoc-internals/rustdoc-gui-test-suite.md)
+	- [The `rustdoc-json` test suite](./rustdoc-internals/rustdoc-json-test-suite.md)
 - [Autodiff internals](./autodiff/internals.md)
     - [Installation](./autodiff/installation.md)
     - [How to debug](./autodiff/debugging.md)
diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
index 35d33ebdb0e..ed267850401 100644
--- a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
+++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
@@ -55,7 +55,7 @@ Bootstrap will conditionally build `tracing` support and enable `tracing` output
 
 Example basic usage[^just-trace]:
 
-[^just-trace]: It is not recommend to use *just* `BOOTSTRAP_TRACING=TRACE` because that will dump *everything* at `TRACE` level, including logs intentionally gated behind custom targets as they are too verbose even for `TRACE` level by default.
+[^just-trace]: It is not recommended to use *just* `BOOTSTRAP_TRACING=TRACE` because that will dump *everything* at `TRACE` level, including logs intentionally gated behind custom targets as they are too verbose even for `TRACE` level by default.
 
 ```bash
 $ BOOTSTRAP_TRACING=bootstrap=TRACE ./x build library --stage 1
diff --git a/src/doc/rustc-dev-guide/src/getting-started.md b/src/doc/rustc-dev-guide/src/getting-started.md
index 435202ca6c8..d6c5c3ac852 100644
--- a/src/doc/rustc-dev-guide/src/getting-started.md
+++ b/src/doc/rustc-dev-guide/src/getting-started.md
@@ -158,9 +158,6 @@ feel comfortable jumping straight into the large `rust-lang/rust` codebase.
 The following tasks are doable without much background knowledge but are
 incredibly helpful:
 
-- [Cleanup crew][iceb]: find minimal reproductions of ICEs, bisect
-  regressions, etc. This is a way of helping that saves a ton of time for
-  others to fix an error later.
 - [Writing documentation][wd]: if you are feeling a bit more intrepid, you could try
   to read a part of the code and write doc comments for it. This will help you
   to learn some part of the compiler while also producing a useful artifact!
@@ -179,7 +176,6 @@ incredibly helpful:
 [users]: https://users.rust-lang.org/
 [so]: http://stackoverflow.com/questions/tagged/rust
 [community-library]: https://github.com/rust-lang/rfcs/labels/A-community-library
-[iceb]: ./notification-groups/cleanup-crew.md
 [wd]: ./contributing.md#writing-documentation
 [wg]: https://rust-lang.github.io/compiler-team/working-groups/
 [triage]: ./contributing.md#issue-triage
diff --git a/src/doc/rustc-dev-guide/src/notification-groups/about.md b/src/doc/rustc-dev-guide/src/notification-groups/about.md
index af305f0103a..d75891ecf7b 100644
--- a/src/doc/rustc-dev-guide/src/notification-groups/about.md
+++ b/src/doc/rustc-dev-guide/src/notification-groups/about.md
@@ -21,9 +21,7 @@ search for existing issues that haven't been claimed yet.
 Here's the list of the notification groups:
 - [Apple](./apple.md)
 - [ARM](./arm.md)
-- [Cleanup Crew](./cleanup-crew.md)
 - [Emscripten](./emscripten.md)
-- [LLVM Icebreakers](./llvm.md)
 - [RISC-V](./risc-v.md)
 - [WASI](./wasi.md)
 - [WebAssembly](./wasm.md)
@@ -64,9 +62,7 @@ Example PRs:
 
 * [Example of adding yourself to the Apple group.](https://github.com/rust-lang/team/pull/1434)
 * [Example of adding yourself to the ARM group.](https://github.com/rust-lang/team/pull/358)
-* [Example of adding yourself to the Cleanup Crew.](https://github.com/rust-lang/team/pull/221)
 * [Example of adding yourself to the Emscripten group.](https://github.com/rust-lang/team/pull/1579)
-* [Example of adding yourself to the LLVM group.](https://github.com/rust-lang/team/pull/140)
 * [Example of adding yourself to the RISC-V group.](https://github.com/rust-lang/team/pull/394)
 * [Example of adding yourself to the WASI group.](https://github.com/rust-lang/team/pull/1580)
 * [Example of adding yourself to the WebAssembly group.](https://github.com/rust-lang/team/pull/1581)
@@ -81,9 +77,7 @@ group. For example:
 ```text
 @rustbot ping apple
 @rustbot ping arm
-@rustbot ping cleanup-crew
 @rustbot ping emscripten
-@rustbot ping icebreakers-llvm
 @rustbot ping risc-v
 @rustbot ping wasi
 @rustbot ping wasm
@@ -92,12 +86,12 @@ group. For example:
 
 To make some commands shorter and easier to remember, there are aliases,
 defined in the [`triagebot.toml`] file. For example, all of these commands
-are equivalent and will ping the Cleanup Crew:
+are equivalent and will ping the Apple group:
 
 ```text
-@rustbot ping cleanup
-@rustbot ping bisect
-@rustbot ping reduce
+@rustbot ping apple
+@rustbot ping macos
+@rustbot ping ios
 ```
 
 Keep in mind that these aliases are meant to make humans' life easier.
diff --git a/src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md b/src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md
deleted file mode 100644
index 9cf4e512cbd..00000000000
--- a/src/doc/rustc-dev-guide/src/notification-groups/cleanup-crew.md
+++ /dev/null
@@ -1,90 +0,0 @@
-# Cleanup Crew
-
-**Github Label:** [ICEBreaker-Cleanup-Crew] <br>
-**Ping command:** `@rustbot ping cleanup-crew`
-
-[ICEBreaker-Cleanup-Crew]: https://github.com/rust-lang/rust/labels/ICEBreaker-Cleanup-Crew
-
-The "Cleanup Crew" are focused on improving bug reports. Specifically,
-the goal is to try to ensure that every bug report has all the
-information that will be needed for someone to fix it:
-
-* a minimal, standalone example that shows the problem
-* links to duplicates or related bugs
-* if the bug is a regression (something that used to work, but no longer does),
-  then a bisection to the PR or nightly that caused the regression
-
-This kind of cleanup is invaluable in getting bugs fixed. Better
-still, it can be done by anybody who knows Rust, without any
-particularly deep knowledge of the compiler.
-
-Let's look a bit at the workflow for doing "cleanup crew" actions.
-
-## Finding a minimal, standalone example
-
-Here the ultimate goal is to produce an example that reproduces the same
-problem but without relying on any external crates. Such a test ought to contain
-as little code as possible, as well. This will make it much easier to isolate the problem.
-
-However, even if the "ultimate minimal test" cannot be achieved, it's
-still useful to post incremental minimizations. For example, if you
-can eliminate some of the external dependencies, that is helpful, and
-so forth. 
-
-It's particularly useful to reduce to an example that works
-in the [Rust playground](https://play.rust-lang.org/), rather than
-requiring people to checkout a cargo build.
-
-There are many resources for how to produce minimized test cases. Here
-are a few:
-
-* The [rust-reduce](https://github.com/jethrogb/rust-reduce) tool can try to reduce
-  code automatically.
-  * The [C-reduce](https://github.com/csmith-project/creduce) tool also works
-    on Rust code, though it requires that you start from a single
-    file. (A post explaining how to do it can be found [here](https://insaneinside.net/2017/09/12/whole-crate-bug-reduction-with-creduce.html).)
-* pnkfelix's [Rust Bug Minimization Patterns] blog post
-  * This post focuses on "heavy bore" techniques, where you are
-    starting with a large, complex cargo project that you wish to
-    narrow down to something standalone.
-
-[Rust Bug Minimization Patterns]: http://blog.pnkfx.org/blog/2019/11/18/rust-bug-minimization-patterns/
-
-## Links to duplicate or related bugs
-
-If you are on the "Cleanup Crew", you will sometimes see multiple bug
-reports that seem very similar. You can link one to the other just by
-mentioning the other bug number in a Github comment. Sometimes it is
-useful to close duplicate bugs. But if you do so, you should always
-copy any test case from the bug you are closing to the other bug that
-remains open, as sometimes duplicate-looking bugs will expose
-different facets of the same problem.
-
-## Bisecting regressions
-
-For regressions (something that used to work, but no longer does), it
-is super useful if we can figure out precisely when the code stopped
-working.  The gold standard is to be able to identify the precise
-**PR** that broke the code, so we can ping the author, but even
-narrowing it down to a nightly build is helpful, especially as that
-then gives us a range of PRs. (One other challenge is that we
-sometimes land "rollup" PRs, which combine multiple PRs into one.)
-
-### cargo-bisect-rustc
-
-To help in figuring out the cause of a regression we have a tool
-called [cargo-bisect-rustc]. It will automatically download and test
-various builds of rustc. For recent regressions, it is even able to
-use the builds from our CI to track down the regression to a specific
-PR; for older regressions, it will simply identify a nightly.
-
-To learn to use [cargo-bisect-rustc], check out [this blog post][learn], which
-gives a quick introduction to how it works. Additionally, there is a [Guide]
-which goes into more detail on how to use it. You can also ask questions at
-the Zulip stream [`#t-compiler/cargo-bisect-rustc`][zcbr], or help in
-improving the tool.
-
-[cargo-bisect-rustc]: https://github.com/rust-lang/cargo-bisect-rustc/
-[learn]: https://blog.rust-lang.org/inside-rust/2019/12/18/bisecting-rust-compiler.html
-[zcbr]: https://rust-lang.zulipchat.com/#narrow/stream/217417-t-compiler.2Fcargo-bisect-rustc
-[Guide]: https://rust-lang.github.io/cargo-bisect-rustc/
diff --git a/src/doc/rustc-dev-guide/src/notification-groups/llvm.md b/src/doc/rustc-dev-guide/src/notification-groups/llvm.md
deleted file mode 100644
index 9d008728543..00000000000
--- a/src/doc/rustc-dev-guide/src/notification-groups/llvm.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# LLVM Icebreakers Notification group
-
-**Github Label:** [A-LLVM] <br>
-**Ping command:** `@rustbot ping icebreakers-llvm`
-
-[A-LLVM]: https://github.com/rust-lang/rust/labels/A-LLVM
-
-*Note*: this notification group is *not* the same as the LLVM working group
-(WG-llvm).
-
-The "LLVM Icebreakers Notification Group" are focused on bugs that center around
-LLVM. These bugs often arise because of LLVM optimizations gone awry, or as the
-result of an LLVM upgrade. The goal here is:
-
-- to determine whether the bug is a result of us generating invalid LLVM IR,
-  or LLVM misoptimizing;
-- if the former, to fix our IR;
-- if the latter, to try and file a bug on LLVM (or identify an existing bug).
-
-The group may also be asked to weigh in on other sorts of LLVM-focused
-questions.
-
-## Helpful tips and options
-
-The ["Debugging LLVM"][d] section of the
-rustc-dev-guide gives a step-by-step process for how to help debug bugs
-caused by LLVM. In particular, it discusses how to emit LLVM IR, run
-the LLVM IR optimization pipelines, and so forth. You may also find
-it useful to look at the various codegen options listed under `-C help`
-and the internal options under `-Z help` -- there are a number that
-pertain to LLVM (just search for LLVM).
-
-[d]: ../backend/debugging.md
-
-## If you do narrow to an LLVM bug
-
-The ["Debugging LLVM"][d] section also describes what to do once
-you've identified the bug.
diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals.md b/src/doc/rustc-dev-guide/src/rustdoc-internals.md
index bc91c62d873..0234d4a920e 100644
--- a/src/doc/rustc-dev-guide/src/rustdoc-internals.md
+++ b/src/doc/rustc-dev-guide/src/rustdoc-internals.md
@@ -270,35 +270,6 @@ in `test.rs` is the function `make_test`, which is where hand-written
 Some extra reading about `make_test` can be found
 [here](https://quietmisdreavus.net/code/2018/02/23/how-the-doctests-get-made/).
 
-## Dotting i's And Crossing t's
-
-So that's `rustdoc`'s code in a nutshell, but there's more things in the
-compiler that deal with it. Since we have the full `compiletest` suite at hand,
-there's a set of tests in `tests/rustdoc` that make sure the final `HTML` is
-what we expect in various situations. These tests also use a supplementary
-script, `src/etc/htmldocck.py`, that allows it to look through the final `HTML`
-using `XPath` notation to get a precise look at the output. The full
-description of all the commands available to `rustdoc` tests (e.g. [`@has`] and
-[`@matches`]) is in [`htmldocck.py`].
-
-To use multiple crates in a `rustdoc` test, add `//@ aux-build:filename.rs`
-to the top of the test file. `filename.rs` should be placed in an `auxiliary`
-directory relative to the test file with the comment. If you need to build
-docs for the auxiliary file, use `//@ build-aux-docs`.
-
-In addition, there are separate tests for the search index and `rustdoc`'s
-ability to query it. The files in `tests/rustdoc-js` each contain a
-different search query and the expected results, broken out by search tab.
-These files are processed by a script in `src/tools/rustdoc-js` and the `Node.js`
-runtime. These tests don't have as thorough of a writeup, but a broad example
-that features results in all tabs can be found in `basic.js`. The basic idea is
-that you match a given `QUERY` with a set of `EXPECTED` results, complete with
-the full item path of each item.
-
-[`@has`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py#L39
-[`@matches`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py#L44
-[`htmldocck.py`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py
-
 ## Testing Locally
 
 Some features of the generated `HTML` documentation might require local
diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md
new file mode 100644
index 00000000000..e155f960e3d
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-gui-test-suite.md
@@ -0,0 +1,14 @@
+# The `rustdoc-gui` test suite
+
+> **FIXME**: This section is a stub. Please help us flesh it out!
+
+This page is about the test suite named `rustdoc-gui` used to test the "GUI" of `rustdoc` (i.e., the HTML/JS/CSS as rendered in a browser).
+For other rustdoc-specific test suites, see [Rustdoc test suites].
+
+These use a NodeJS-based tool called [`browser-UI-test`] that uses [puppeteer] to run tests in a headless browser and check rendering and interactivity. For information on how to write this form of test, see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme] as well as [the description of the `.goml` format][goml-script]
+
+[Rustdoc test suites]: ../tests/compiletest.md#rustdoc-test-suites
+[`browser-UI-test`]: https://github.com/GuillaumeGomez/browser-UI-test/
+[puppeteer]: https://pptr.dev/
+[rustdoc-gui-readme]: https://github.com/rust-lang/rust/blob/master/tests/rustdoc-gui/README.md
+[goml-script]: https://github.com/GuillaumeGomez/browser-UI-test/blob/master/goml-script.md
diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md
new file mode 100644
index 00000000000..e08f7709506
--- /dev/null
+++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-json-test-suite.md
@@ -0,0 +1,3 @@
+# The `rustdoc-json` test suite
+
+> **FIXME**: This section is a stub. It will be populated by [PR #2422](https://github.com/rust-lang/rustc-dev-guide/pull/2422/).
diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md
index bad7ac19da2..b05318ce9e6 100644
--- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md
+++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md
@@ -1,112 +1,191 @@
 # The `rustdoc` test suite
 
-This page is specifically about the test suite named `rustdoc`.
-For other test suites used for testing rustdoc, see [Rustdoc tests](../rustdoc.md#tests).
+This page is about the test suite named `rustdoc` used to test the HTML output of `rustdoc`.
+For other rustdoc-specific test suites, see [Rustdoc test suites].
 
-The `rustdoc` test suite is specifically used to test the HTML output of rustdoc.
+Each test file in this test suite is simply a Rust source file `file.rs` sprinkled with
+so-called *directives* located inside normal Rust code comments.
+These come in two flavors: *Compiletest* and *HtmlDocCk*.
 
-This is achieved by means of `htmldocck.py`, a custom checker script that leverages [XPath].
+To learn more about the former, read [Compiletest directives].
+For the latter, continue reading.
 
-[XPath]: https://en.wikipedia.org/wiki/XPath
+Internally, [`compiletest`] invokes the supplementary checker script [`htmldocck.py`].
 
-## Directives
-Directives to htmldocck are similar to those given to `compiletest` in that they take the form of `//@` comments.
+[Rustdoc test suites]: ../tests/compiletest.md#rustdoc-test-suites
+[`compiletest`]: ../tests/compiletest.md
+[`htmldocck.py`]: https://github.com/rust-lang/rust/blob/master/src/etc/htmldocck.py
 
-In addition to the directives listed here,
-`rustdoc` tests also support most
-[compiletest directives](../tests/directives.html).
+## HtmlDocCk Directives
 
-All `PATH`s in directives are relative to the rustdoc output directory (`build/TARGET/test/rustdoc/TESTNAME`),
-so it is conventional to use a `#![crate_name = "foo"]` attribute to avoid
-having to write a long crate name multiple times.
-To avoid repetition, `-` can be used in any `PATH` argument to re-use the previous `PATH` argument.
+Directives to HtmlDocCk are assertions that place constraints on the generated HTML.
+They look similar to those given to `compiletest` in that they take the form of `//@` comments
+but ultimately, they are completey distinct and processed by different programs.
 
-All arguments take the form of quoted strings
-(both single and double quotes are supported),
-with the exception of `COUNT` and the special `-` form of `PATH`.
+[XPath] is used to query parts of the HTML document tree.
+
+**Introductory example**:
+
+```rust,ignore (illustrative)
+//@ has file/type.Alias.html
+//@ has - '//*[@class="rust item-decl"]//code' 'type Alias = Option<i32>;'
+pub type Alias = Option<i32>;
+```
+
+Here, we check that documentation generated for crate `file` contains a page for the
+public type alias `Alias` where the code block that is found at the top contains the
+expected rendering of the item. The `//*[@class="rust item-decl"]//code` is an XPath
+expression.
 
-Directives are assertions that place constraints on the generated HTML.
+Conventionally, you place these directives directly above the thing they are meant to test.
+Technically speaking however, they don't need to be as HtmlDocCk only looks for the directives.
 
-All directives (except `files`) can be negated by putting a `!` in front of their name.
+All directives take a `PATH` argument.
+To avoid repetition, `-` can be passed to it to re-use the previous `PATH` argument.
+Since the path contains the name of the crate, it is conventional to add a
+`#![crate_name = "foo"]` attribute to the crate root to shorten the resulting path.
+
+All arguments take the form of shell-style (single or double) quoted strings,
+with the exception of `COUNT` and the special `-` form of `PATH`.
+
+All directives (except `files`) can be *negated* by putting a `!` in front of their name.
+Before you add negated directives, please read about [their caveats](#caveats).
 
 Similar to shell commands,
 directives can extend across multiple lines if their last char is `\`.
 In this case, the start of the next line should be `//`, with no `@`.
 
-For example, `//@ !has 'foo/struct.Bar.html'` checks that crate `foo` does not have a page for a struct named `Bar` in the crate root.
+Use the special string `{{channel}}` in XPaths, `PATTERN` arguments and [snapshot files](#snapshot)
+if you'd like to refer to the URL `https://doc.rust-lang.org/CHANNEL` where `CHANNEL` refers to the
+current release channel (e.g, `stable` or `nightly`).
+
+Listed below are all possible directives:
+
+[XPath]: https://en.wikipedia.org/wiki/XPath
 
 ### `has`
 
-Usage 1: `//@ has PATH`
-Usage 2: `//@ has PATH XPATH PATTERN`
+> Usage 1: `//@ has PATH`
 
-In the first form, `has` checks that a given file exists.
+Check that the file given by `PATH` exists.
 
-In the second form, `has` is an alias for `matches`,
-except `PATTERN` is a whitespace-normalized[^1] string instead of a regex.
+> Usage 2: `//@ has PATH XPATH PATTERN`
 
-### `matches`
+Checks that the text of each element / attribute / text selected by `XPATH` in the
+whitespace-normalized[^1] file given by `PATH` matches the
+(also whitespace-normalized) string `PATTERN`.
+
+**Tip**: If you'd like to avoid whitespace normalization and/or if you'd like to match with a regex,
+use `matches` instead.
 
-Usage: `//@ matches PATH XPATH PATTERN`
+### `hasraw`
 
-Checks that the text of each element selected by `XPATH` in `PATH` matches the python-flavored regex `PATTERN`.
+> Usage: `//@ hasraw PATH PATTERN`
 
-### `matchesraw`
+Checks that the contents of the whitespace-normalized[^1] file given by `PATH`
+matches the (also whitespace-normalized) string `PATTERN`.
 
-Usage: `//@ matchesraw PATH PATTERN`
+**Tip**: If you'd like to avoid whitespace normalization and / or if you'd like to match with a
+regex, use `matchesraw` instead.
 
-Checks that the contents of the file `PATH` matches the regex `PATTERN`.
+### `matches`
 
-### `hasraw`
+> Usage: `//@ matches PATH XPATH PATTERN`
 
-Usage: `//@ hasraw PATH PATTERN`
+Checks that the text of each element / attribute / text selected by `XPATH` in the
+file given by `PATH` matches the Python-flavored[^2] regex `PATTERN`.
 
-Same as `matchesraw`, except `PATTERN` is a whitespace-normalized[^1] string instead of a regex.
+### `matchesraw`
+
+> Usage: `//@ matchesraw PATH PATTERN`
+
+Checks that the contents of the file given by `PATH` matches the
+Python-flavored[^2] regex `PATTERN`.
 
 ### `count`
 
-Usage: `//@ count PATH XPATH COUNT`
+> Usage: `//@ count PATH XPATH COUNT`
 
-Checks that there are exactly `COUNT` matches for `XPATH` within the file `PATH`.
+Checks that there are exactly `COUNT` matches for `XPATH` within the file given by `PATH`.
 
 ### `snapshot`
 
-Usage: `//@ snapshot NAME PATH XPATH`
+> Usage: `//@ snapshot NAME PATH XPATH`
 
-Creates a snapshot test named NAME.
-A snapshot test captures a subtree of the DOM, at the location
-determined by the XPath, and compares it to a pre-recorded value
-in a file. The file's name is the test's name with the `.rs` extension
-replaced with `.NAME.html`, where NAME is the snapshot's name.
+Checks that the element / text selected by `XPATH` in the file given by `PATH` matches the
+pre-recorded subtree or text (the "snapshot") in file `FILE_STEM.NAME.html` where `FILE_STEM`
+is the file stem of the test file.
 
-htmldocck supports the `--bless` option to accept the current subtree
-as expected, saving it to the file determined by the snapshot's name.
-compiletest's `--bless` flag is forwarded to htmldocck.
+Pass the `--bless` option to `compiletest` to accept the current subtree/text as expected.
+This will overwrite the aforementioned file (or create it if it doesn't exist). It will
+automatically normalize the channel-dependent URL `https://doc.rust-lang.org/CHANNEL` to
+the special string `{{channel}}`.
 
 ### `has-dir`
 
-Usage: `//@ has-dir PATH`
+> Usage: `//@ has-dir PATH`
 
-Checks for the existence of directory `PATH`.
+Checks for the existence of the directory given by `PATH`.
 
 ### `files`
 
-Usage: `//@ files PATH ENTRIES`
+> Usage: `//@ files PATH ENTRIES`
+
+Checks that the directory given by `PATH` contains exactly `ENTRIES`.
+`ENTRIES` is a Python-like list of strings inside a quoted string.
+
+**Example**: `//@ files "foo/bar" '["index.html", "sidebar-items.js"]'`
+
+[^1]: Whitespace normalization means that all spans of consecutive whitespace are replaced with a single space.
+[^2]: They are Unicode aware (flag `UNICODE` is set), match case-sensitively and in single-line mode.
+
+## Compiletest Directives (Brief)
+
+As mentioned in the introduction, you also have access to [compiletest directives].
+Most importantly, they allow you to register auxiliary crates and
+to pass flags to the `rustdoc` binary under test.
+It's *strongly recommended* to read that chapter if you don't know anything about them yet.
+
+Here are some details that are relevant to this test suite specifically:
 
-Checks that the directory `PATH` contains exactly `ENTRIES`.
-`ENTRIES` is a python list of strings inside a quoted string,
-as if it were to be parsed by `eval`.
-(note that the list is actually parsed by `shlex.split`,
-so it cannot contain arbitrary python expressions).
+* While you can use both `//@ compile-flags` and `//@ doc-flags` to pass flags to `rustdoc`,
+  prefer to user the latter to show intent. The former is meant for `rustc`.
+* Add `//@ build-aux-docs` to the test file that has auxiliary crates to not only compile the
+  auxiliaries with `rustc` but to also document them with `rustdoc`.
 
-Example: `//@ files "foo/bar" '["index.html", "sidebar-items.js"]'`
+## Caveats
 
-[^1]: Whitespace normalization means that all spans of consecutive whitespace are replaced with a single space.  The files themselves are also whitespace-normalized.
+Testing for the absence of an element or a piece of text is quite fragile and not very future proof.
+
+It's not unusual that the *shape* of the generated HTML document tree changes from time to time.
+This includes for example renamings of CSS classes.
+
+Whenever that happens, *positive* checks will either continue to match the intended element /
+attribute / text (if their XPath expression is general / loose enough) and
+thus continue to test the correct thing or they won't in which case they would fail thereby
+forcing the author of the change to look at them.
+
+Compare that to *negative* checks (e.g., `//@ !has PATH XPATH PATTERN`) which won't fail if their
+XPath expression "no longer" matches. The author who changed "the shape" thus won't get notified and
+as a result someone else can unintentionally reintroduce `PATTERN` into the generated docs without
+the original negative check failing.
+
+**Note**: Please avoid the use of *negated* checks!
+
+**Tip**: If you can't avoid it, please **always** pair it with an analogous positive check in the
+immediate vicinity, so people changing "the shape" have a chance to notice and to update the
+negated check!
 
 ## Limitations
-`htmldocck.py` uses the xpath implementation from the standard library.
+
+HtmlDocCk uses the XPath implementation from the Python standard library.
 This leads to several limitations:
+
 * All `XPATH` arguments must start with `//` due to a flaw in the implementation.
 * Many XPath features (functions, axies, etc.) are not supported.
 * Only well-formed HTML can be parsed (hopefully rustdoc doesn't output mismatched tags).
 
+Furthmore, compiletest [revisions] are not supported.
+
+[revisions]: ../tests/compiletest.md#revisions
+[compiletest directives]: ../tests/directives.md
diff --git a/src/doc/rustc-dev-guide/src/rustdoc.md b/src/doc/rustc-dev-guide/src/rustdoc.md
index de70ba63823..52ae48c3735 100644
--- a/src/doc/rustc-dev-guide/src/rustdoc.md
+++ b/src/doc/rustc-dev-guide/src/rustdoc.md
@@ -67,43 +67,29 @@ does is call the `main()` that's in this crate's `lib.rs`, though.)
 
 ## Code structure
 
-* All paths in this section are relative to `src/librustdoc` in the rust-lang/rust repository.
+All paths in this section are relative to `src/librustdoc/` in the rust-lang/rust repository.
+
 * Most of the HTML printing code is in `html/format.rs` and `html/render/mod.rs`.
-  It's in a bunch of `fmt::Display` implementations and supplementary
-  functions.
-* The types that got `Display` impls above are defined in `clean/mod.rs`, right
-  next to the custom `Clean` trait used to process them out of the rustc HIR.
+  It's in a bunch of functions returning `impl std::fmt::Display`.
+* The data types that get rendered by the functions mentioned above are defined in `clean/types.rs`.
+  The functions responsible for creating them from the `HIR` and the `rustc_middle::ty` IR
+  live in `clean/mod.rs`.
 * The bits specific to using rustdoc as a test harness are in
   `doctest.rs`.
 * The Markdown renderer is loaded up in `html/markdown.rs`, including functions
   for extracting doctests from a given block of Markdown.
 * Frontend CSS and JavaScript are stored in `html/static/`.
+  * Re. JavaScript, type annotations are written using [TypeScript-flavored JSDoc]
+comments and an external `.d.ts` file.
+    This way, the code itself remains plain, valid JavaScript.
+    We only use `tsc` as a linter.
 
-## Tests
+[TypeScript-flavored JSDoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
 
-* Tests on search engine and index are located in `tests/rustdoc-js` and `tests/rustdoc-js-std`.
-  The format is specified
-  [in the search guide](rustdoc-internals/search.md#testing-the-search-engine).
-* Tests on the "UI" of rustdoc (the terminal output it produces when run) are in
-  `tests/rustdoc-ui`
-* Tests on the "GUI" of rustdoc (the HTML, JS, and CSS as rendered in a browser)
-  are in `tests/rustdoc-gui`. These use a [NodeJS tool called
-  browser-UI-test](https://github.com/GuillaumeGomez/browser-UI-test/) that uses
-  puppeteer to run tests in a headless browser and check rendering and
-  interactivity.  For information on how to write this form of test,
-  see [`tests/rustdoc-gui/README.md`][rustdoc-gui-readme]
-  as well as [the description of the `.goml` format][goml-script]
-* Tests on the structure of rustdoc HTML output are located in `tests/rustdoc`,
-  where they're handled by the test runner of bootstrap and
-  the supplementary script `src/etc/htmldocck.py`.
-  [These tests have several extra directives available to them](./rustdoc-internals/rustdoc-test-suite.md).
-* Additionally, JavaScript type annotations are written using [TypeScript-flavored JSDoc]
-  comments and an external d.ts file. The code itself is plain, valid JavaScript; we only
-  use tsc as a linter.
+## Tests
 
-[TypeScript-flavored JSDoc]: https://www.typescriptlang.org/docs/handbook/jsdoc-supported-types.html
-[rustdoc-gui-readme]: https://github.com/rust-lang/rust/blob/master/tests/rustdoc-gui/README.md
-[goml-script]: https://github.com/GuillaumeGomez/browser-UI-test/blob/master/goml-script.md
+`rustdoc`'s integration tests are split across several test suites.
+See [Rustdoc tests suites](tests/compiletest.md#rustdoc-test-suites) for more details.
 
 ## Constraints
 
diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md
index ee06ca3b698..20dd16c81df 100644
--- a/src/doc/rustc-dev-guide/src/tests/compiletest.md
+++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md
@@ -56,6 +56,9 @@ incremental compilation. The various suites are defined in
 
 The following test suites are available, with links for more information:
 
+[`tests`]: https://github.com/rust-lang/rust/blob/master/tests
+[`src/tools/compiletest/src/common.rs`]: https://github.com/rust-lang/rust/tree/master/src/tools/compiletest/src/common.rs
+
 ### Compiler-specific test suites
 
 | Test suite                                | Purpose                                                                                                             |
@@ -71,6 +74,7 @@ The following test suites are available, with links for more information:
 | [`mir-opt`](#mir-opt-tests)               | Check MIR generation and optimizations                                                                              |
 | [`coverage`](#coverage-tests)             | Check coverage instrumentation                                                                                      |
 | [`coverage-run-rustdoc`](#coverage-tests) | `coverage` tests that also run instrumented doctests                                                                |
+| [`crashes`](#crashes-tests)               | Check that the compiler ICEs/panics/crashes on certain inputs to catch accidental fixes                             |
 
 ### General purpose test suite
 
@@ -78,19 +82,23 @@ The following test suites are available, with links for more information:
 
 ### Rustdoc test suites
 
-See [Rustdoc tests](../rustdoc.md#tests) for more details.
-
-| Test suite       | Purpose                                                                  |
-|------------------|--------------------------------------------------------------------------|
-| `rustdoc`        | Check `rustdoc` generated files contain the expected documentation       |
-| `rustdoc-gui`    | Check `rustdoc`'s GUI using a web browser                                |
-| `rustdoc-js`     | Check `rustdoc` search is working as expected                            |
-| `rustdoc-js-std` | Check rustdoc search is working as expected specifically on the std docs |
-| `rustdoc-json`   | Check JSON output of `rustdoc`                                           |
-| `rustdoc-ui`     | Check terminal output of `rustdoc`                                       |
-
-[`tests`]: https://github.com/rust-lang/rust/blob/master/tests
-[`src/tools/compiletest/src/common.rs`]: https://github.com/rust-lang/rust/tree/master/src/tools/compiletest/src/common.rs
+| Test suite                           | Purpose                                                                  |
+|--------------------------------------|--------------------------------------------------------------------------|
+| [`rustdoc`][rustdoc-html-tests]      | Check HTML output of `rustdoc`                                           |
+| [`rustdoc-gui`][rustdoc-gui-tests]   | Check `rustdoc`'s GUI using a web browser                                |
+| [`rustdoc-js`][rustdoc-js-tests]     | Check `rustdoc`'s search engine and index                                |
+| [`rustdoc-js-std`][rustdoc-js-tests] | Check `rustdoc`'s search engine and index on the std library docs        |
+| [`rustdoc-json`][rustdoc-json-tests] | Check JSON output of `rustdoc`                                           |
+| `rustdoc-ui`                         | Check terminal output of `rustdoc` ([see also](ui.md))                   |
+
+Some rustdoc-specific tests can also be found in `ui/rustdoc/`.
+These check rustdoc-related or -specific lints that (also) run as part of `rustc`, not (only) `rustdoc`.
+Run-make tests pertaining to rustdoc are typically named `run-make/rustdoc-*/`.
+
+[rustdoc-html-tests]: ../rustdoc-internals/rustdoc-test-suite.md
+[rustdoc-gui-tests]: ../rustdoc-internals/rustdoc-gui-test-suite.md
+[rustdoc-js-tests]: ../rustdoc-internals/search.md#testing-the-search-engine
+[rustdoc-json-tests]: ../rustdoc-internals/rustdoc-json-test-suite.md
 
 ### Pretty-printer tests
 
diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md
index 2dff21ed61c..f73a2811d5a 100644
--- a/src/doc/rustc-dev-guide/src/tests/directives.md
+++ b/src/doc/rustc-dev-guide/src/tests/directives.md
@@ -261,7 +261,7 @@ Consider writing the test as a proper incremental test instead.
 
 | Directive   | Explanation                                                  | Supported test suites                    | Possible values           |
 |-------------|--------------------------------------------------------------|------------------------------------------|---------------------------|
-| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `rustdoc-js`, `rustdoc-json` | Any valid `rustdoc` flags |
+| `doc-flags` | Flags passed to `rustdoc` when building the test or aux file | `rustdoc`, `rustdoc-js`, `rustdoc-json`  | Any valid `rustdoc` flags |
 
 <!--
 **FIXME(rustdoc)**: what does `check-test-line-numbers-match` do?
@@ -269,6 +269,17 @@ Asked in
 <https://rust-lang.zulipchat.com/#narrow/stream/266220-t-rustdoc/topic/What.20is.20the.20.60check-test-line-numbers-match.60.20directive.3F>.
 -->
 
+#### Test-suite-specific directives
+
+The test suites [`rustdoc`][rustdoc-html-tests], [`rustdoc-js`/`rustdoc-js-std`][rustdoc-js-tests]
+and [`rustdoc-json`][rustdoc-json-tests] each feature an additional set of directives whose basic
+syntax resembles the one of compiletest directives but which are ultimately read and checked by
+separate tools. For more information, please read their respective chapters as linked above.
+
+[rustdoc-html-tests]: ../rustdoc-internals/rustdoc-test-suite.md
+[rustdoc-js-tests]: ../rustdoc-internals/search.html#testing-the-search-engine
+[rustdoc-json-tests]: ../rustdoc-internals/rustdoc-json-test-suite.md
+
 ### Pretty printing
 
 See [Pretty-printer](compiletest.md#pretty-printer-tests).
diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md
index 3402838da87..25d3efdbb82 100644
--- a/src/doc/rustc-dev-guide/src/tests/ui.md
+++ b/src/doc/rustc-dev-guide/src/tests/ui.md
@@ -220,8 +220,12 @@ negligible (i.e. there is no semantic difference between `//~ ERROR` and
 `//~ERROR` although the former is more common in the codebase).
 
 `~? <diagnostic kind>` (example being `~? ERROR`)
-is used to match diagnostics without line information.
-These can be placed on any line in the test file, but are conventionally placed at the end.
+is used to match diagnostics _without_ line info at all,
+or where the line info is outside the main test file[^main test file].
+These annotations can be placed on any line in the test file.
+
+[^main test file]: This is a file that has the `~?` annotations,
+as distinct from aux files, or sources that we have no control over.
 
 ### Error annotation examples
 
diff --git a/src/doc/rustc-dev-guide/triagebot.toml b/src/doc/rustc-dev-guide/triagebot.toml
index 978802edf3f..b3f4c2d281c 100644
--- a/src/doc/rustc-dev-guide/triagebot.toml
+++ b/src/doc/rustc-dev-guide/triagebot.toml
@@ -72,6 +72,23 @@ days-threshold = 7
 # Documentation at: https://forge.rust-lang.org/triagebot/pr-assignment.html
 [assign]
 
+# NOTE: do not add `[assign.owners]` if we still wish to keep the opt-in
+# reviewer model, as `[assign.owners]` will cause triagebot auto-reviewer
+# assignment to kick in.
+
+# Custom PR welcome message for when no auto reviewer assignment is performed
+# and no explicit manual reviewer selection is made.
+# Documentation at: https://forge.rust-lang.org/triagebot/pr-assignment.html#custom-welcome-messages
+[assign.custom_welcome_messages]
+welcome-message = ""
+welcome-message-no-reviewer = """\
+Thanks for the PR. If you have write access, feel free to merge this PR if it \
+does not need reviews. You can request a review using `r? rustc-dev-guide` or \
+`r? <username>`.
+"""
+
+# Groups for `r? <group>`.
+# Documentation at: https://forge.rust-lang.org/triagebot/pr-assignment.html#usage
 # Keep members alphanumerically sorted.
 [assign.adhoc_groups]
 rustc-dev-guide = [
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index e7dfaaf4fd5..e2e2ad9ac3b 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -324,6 +324,8 @@ target | std | host | notes
 [`i686-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ |   | 32-bit Windows 7 support [^x86_32-floats-return-ABI] [^win32-msvc-alignment]
 [`i686-wrs-vxworks`](platform-support/vxworks.md) | ✓ |  | [^x86_32-floats-return-ABI]
 [`loongarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ✓ |   | LoongArch64 OpenHarmony
+[`loongarch32-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch32 Bare-metal (ILP32D ABI)
+[`loongarch32-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch32 Bare-metal (ILP32S ABI)
 [`m68k-unknown-linux-gnu`](platform-support/m68k-unknown-linux-gnu.md) | ? |  | Motorola 680x0 Linux
 [`m68k-unknown-none-elf`](platform-support/m68k-unknown-none-elf.md) |  |  | Motorola 680x0
 `mips-unknown-linux-gnu` | ✓ | ✓ | MIPS Linux (kernel 4.4, glibc 2.23)
diff --git a/src/doc/rustc/src/platform-support/loongarch-none.md b/src/doc/rustc/src/platform-support/loongarch-none.md
index a2bd6e5734c..fd90b0a2763 100644
--- a/src/doc/rustc/src/platform-support/loongarch-none.md
+++ b/src/doc/rustc/src/platform-support/loongarch-none.md
@@ -1,18 +1,18 @@
 # `loongarch*-unknown-none*`
 
-**Tier: 2**
+Freestanding/bare-metal LoongArch binaries in ELF format: firmware, kernels, etc.
 
-Freestanding/bare-metal LoongArch64 binaries in ELF format: firmware, kernels, etc.
-
-| Target | Description |
-|--------|-------------|
-| `loongarch64-unknown-none` | LoongArch 64-bit, LP64D ABI (freestanding, hard-float) |
-| `loongarch64-unknown-none-softfloat` | LoongArch 64-bit, LP64S ABI (freestanding, soft-float) |
+| Target | Description | Tier |
+|--------|-------------|------|
+| `loongarch32-unknown-none` | LoongArch 32-bit, ILP32D ABI (freestanding, hard-float) | Tier 3 |
+| `loongarch32-unknown-none-softfloat` | LoongArch 32-bit, ILP32S ABI (freestanding, soft-float) | Tier 3 |
+| `loongarch64-unknown-none` | LoongArch 64-bit, LP64D ABI (freestanding, hard-float) | Tier 2 |
+| `loongarch64-unknown-none-softfloat` | LoongArch 64-bit, LP64S ABI (freestanding, soft-float) | Tier 2 |
 
 ## Target maintainers
 
-[@heiher](https://github.com/heiher)
-[@xen0n](https://github.com/xen0n)
+- [@heiher](https://github.com/heiher)
+- [@xen0n](https://github.com/xen0n)
 
 ## Requirements
 
@@ -29,13 +29,13 @@ additional CPU features via the `-C target-feature=` codegen options to rustc, o
 via the `#[target_feature]` mechanism within Rust code.
 
 By default, code generated with the soft-float target should run on any
-LoongArch64 hardware, with the hard-float target additionally requiring an FPU;
+LoongArch hardware, with the hard-float target additionally requiring an FPU;
 enabling additional target features may raise this baseline.
 
 Code generated with the targets will use the `medium` code model by default.
 You can change this using the `-C code-model=` option to rustc.
 
-On `loongarch64-unknown-none*`, `extern "C"` uses the [architecture's standard calling convention][lapcs].
+On `loongarch*-unknown-none*`, `extern "C"` uses the [architecture's standard calling convention][lapcs].
 
 [lapcs]: https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc
 
@@ -52,6 +52,8 @@ list in `bootstrap.toml`:
 [build]
 build-stage = 1
 target = [
+  "loongarch32-unknown-none",
+  "loongarch32-unknown-none-softfloat",
   "loongarch64-unknown-none",
   "loongarch64-unknown-none-softfloat",
 ]
@@ -64,13 +66,28 @@ As the targets support a variety of different environments and do not support
 
 ## Building Rust programs
 
+### loongarch32-unknown-none*
+
+The `loongarch32-unknown-none*` targets are Tier 3, so you must build the Rust
+compiler from source to use them.
+
+```sh
+# target flag may be used with any cargo or rustc command
+cargo build --target loongarch32-unknown-none
+cargo build --target loongarch32-unknown-none-softfloat
+```
+
+### loongarch64-unknown-none*
+
 Starting with Rust 1.74, precompiled artifacts are provided via `rustup`:
 
 ```sh
 # install cross-compile toolchain
 rustup target add loongarch64-unknown-none
+rustup target add loongarch64-unknown-none-softfloat
 # target flag may be used with any cargo or rustc command
 cargo build --target loongarch64-unknown-none
+cargo build --target loongarch64-unknown-none-softfloat
 ```
 
 ## Cross-compilation toolchains and C code
@@ -79,10 +96,10 @@ For cross builds, you will need an appropriate LoongArch C/C++ toolchain for
 linking, or if you want to compile C code along with Rust (such as for Rust
 crates with C dependencies).
 
-Rust *may* be able to use an `loongarch64-unknown-linux-gnu-` toolchain with
+Rust *may* be able to use an `loongarch{32,64}-unknown-linux-{gnu,musl}-` toolchain with
 appropriate standalone flags to build for this toolchain (depending on the assumptions
 of that toolchain, see below), or you may wish to use a separate
-`loongarch64-unknown-none` toolchain.
+`loongarch{32,64}-unknown-none` toolchain.
 
 On some LoongArch hosts that use ELF binaries, you *may* be able to use the host
 C toolchain, if it does not introduce assumptions about the host environment
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index ebc276b38fb..a3762e4117d 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -508,6 +508,7 @@ impl fmt::Display for Display<'_> {
                     (sym::target_arch, Some(arch)) => match arch.as_str() {
                         "aarch64" => "AArch64",
                         "arm" => "ARM",
+                        "loongarch32" => "LoongArch LA32",
                         "loongarch64" => "LoongArch LA64",
                         "m68k" => "M68k",
                         "csky" => "CSKY",
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs
index f8209f2c8cd..4c53ea42793 100644
--- a/src/tools/build-manifest/src/main.rs
+++ b/src/tools/build-manifest/src/main.rs
@@ -113,6 +113,8 @@ static TARGETS: &[&str] = &[
     "i686-unknown-uefi",
     "loongarch64-unknown-linux-gnu",
     "loongarch64-unknown-linux-musl",
+    "loongarch32-unknown-none",
+    "loongarch32-unknown-none-softfloat",
     "loongarch64-unknown-none",
     "loongarch64-unknown-none-softfloat",
     "m68k-unknown-linux-gnu",
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 4f93b498741..9b9d94bbead 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -495,6 +495,7 @@ impl Config {
             "arm64ec",
             "riscv32",
             "riscv64",
+            "loongarch32",
             "loongarch64",
             "s390x",
             // These targets require an additional asm_experimental_arch feature.
diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs
index 5757e422ae2..1406553c9ea 100644
--- a/src/tools/compiletest/src/directive-list.rs
+++ b/src/tools/compiletest/src/directive-list.rs
@@ -73,6 +73,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-linux",
     "ignore-lldb",
     "ignore-llvm-version",
+    "ignore-loongarch32",
     "ignore-loongarch64",
     "ignore-macabi",
     "ignore-macos",
@@ -196,6 +197,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-i686-unknown-linux-gnu",
     "only-ios",
     "only-linux",
+    "only-loongarch32",
     "only-loongarch64",
     "only-loongarch64-unknown-linux-gnu",
     "only-macos",
diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock
index c1915ae617e..d37f8750bde 100644
--- a/src/tools/miri/cargo-miri/Cargo.lock
+++ b/src/tools/miri/cargo-miri/Cargo.lock
@@ -3,6 +3,15 @@
 version = 4
 
 [[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
 name = "anyhow"
 version = "1.0.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -207,12 +216,42 @@ dependencies = [
 ]
 
 [[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
 name = "rustc-build-sysroot"
-version = "0.5.7"
+version = "0.5.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "10edc2e4393515193bd766e2f6c050b0536a68e56f2b6d56c07ababfdc114ff0"
+checksum = "16d115ad7e26e0d1337f64ae6598f758194696afc2e9f34c8a6f24582529c3dc"
 dependencies = [
  "anyhow",
+ "regex",
  "rustc_version",
  "tempfile",
  "walkdir",
diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml
index 5c579b2a77d..e08733959cc 100644
--- a/src/tools/miri/cargo-miri/Cargo.toml
+++ b/src/tools/miri/cargo-miri/Cargo.toml
@@ -18,7 +18,7 @@ directories = "6"
 rustc_version = "0.4"
 serde_json = "1.0.40"
 cargo_metadata = "0.19"
-rustc-build-sysroot = "0.5.7"
+rustc-build-sysroot = "0.5.8"
 
 # 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/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index a4882a20148..9957e351ff1 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -3,17 +3,20 @@
 mod atomic;
 mod simd;
 
+use std::ops::Neg;
+
 use rand::Rng;
 use rustc_abi::Size;
-use rustc_apfloat::{Float, Round};
+use rustc_apfloat::ieee::{IeeeFloat, Semantics};
+use rustc_apfloat::{self, Float, Round};
 use rustc_middle::mir;
-use rustc_middle::ty::{self, FloatTy};
+use rustc_middle::ty::{self, FloatTy, ScalarInt};
 use rustc_span::{Symbol, sym};
 
 use self::atomic::EvalContextExt as _;
 use self::helpers::{ToHost, ToSoft, check_intrinsic_arg_count};
 use self::simd::EvalContextExt as _;
-use crate::math::apply_random_float_error_to_imm;
+use crate::math::{IeeeExt, apply_random_float_error_ulp};
 use crate::*;
 
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -187,31 +190,39 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             => {
                 let [f] = check_intrinsic_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f32()?;
-                // Using host floats (but it's fine, these operations do not have
-                // guaranteed precision).
-                let host = f.to_host();
-                let res = match intrinsic_name {
-                    "sinf32" => host.sin(),
-                    "cosf32" => host.cos(),
-                    "expf32" => host.exp(),
-                    "exp2f32" => host.exp2(),
-                    "logf32" => host.ln(),
-                    "log10f32" => host.log10(),
-                    "log2f32" => host.log2(),
-                    _ => bug!(),
-                };
-                let res = res.to_soft();
-                // Apply a relative error of 16ULP to introduce some non-determinism
-                // simulating imprecise implementations and optimizations.
-                // FIXME: temporarily disabled as it breaks std tests.
-                // let res = apply_random_float_error_ulp(
-                //     this,
-                //     res,
-                //     4, // log2(16)
-                // );
+
+                let res = fixed_float_value(intrinsic_name, &[f]).unwrap_or_else(||{
+                    // Using host floats (but it's fine, these operations do not have
+                    // guaranteed precision).
+                    let host = f.to_host();
+                    let res = match intrinsic_name {
+                        "sinf32" => host.sin(),
+                        "cosf32" => host.cos(),
+                        "expf32" => host.exp(),
+                        "exp2f32" => host.exp2(),
+                        "logf32" => host.ln(),
+                        "log10f32" => host.log10(),
+                        "log2f32" => host.log2(),
+                        _ => bug!(),
+                    };
+                    let res = res.to_soft();
+
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    let res = apply_random_float_error_ulp(
+                        this,
+                        res,
+                        2, // log2(4)
+                    );
+
+                    // Clamp the result to the guaranteed range of this function according to the C standard,
+                    // if any.
+                    clamp_float_value(intrinsic_name, res)
+                });
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
+
             #[rustfmt::skip]
             | "sinf64"
             | "cosf64"
@@ -223,28 +234,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             => {
                 let [f] = check_intrinsic_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f64()?;
-                // Using host floats (but it's fine, these operations do not have
-                // guaranteed precision).
-                let host = f.to_host();
-                let res = match intrinsic_name {
-                    "sinf64" => host.sin(),
-                    "cosf64" => host.cos(),
-                    "expf64" => host.exp(),
-                    "exp2f64" => host.exp2(),
-                    "logf64" => host.ln(),
-                    "log10f64" => host.log10(),
-                    "log2f64" => host.log2(),
-                    _ => bug!(),
-                };
-                let res = res.to_soft();
-                // Apply a relative error of 16ULP to introduce some non-determinism
-                // simulating imprecise implementations and optimizations.
-                // FIXME: temporarily disabled as it breaks std tests.
-                // let res = apply_random_float_error_ulp(
-                //     this,
-                //     res,
-                //     4, // log2(16)
-                // );
+
+                let res = fixed_float_value(intrinsic_name, &[f]).unwrap_or_else(||{
+                    // Using host floats (but it's fine, these operations do not have
+                    // guaranteed precision).
+                    let host = f.to_host();
+                    let res = match intrinsic_name {
+                        "sinf64" => host.sin(),
+                        "cosf64" => host.cos(),
+                        "expf64" => host.exp(),
+                        "exp2f64" => host.exp2(),
+                        "logf64" => host.ln(),
+                        "log10f64" => host.log10(),
+                        "log2f64" => host.log2(),
+                        _ => bug!(),
+                    };
+                    let res = res.to_soft();
+
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    let res = apply_random_float_error_ulp(
+                        this,
+                        res,
+                        2, // log2(4)
+                    );
+
+                    // Clamp the result to the guaranteed range of this function according to the C standard,
+                    // if any.
+                    clamp_float_value(intrinsic_name, res)
+                });
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
@@ -302,43 +320,75 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
 
             "powf32" => {
-                // FIXME: apply random relative error but without altering behaviour of powf
                 let [f1, f2] = check_intrinsic_arg_count(args)?;
                 let f1 = this.read_scalar(f1)?.to_f32()?;
                 let f2 = this.read_scalar(f2)?.to_f32()?;
-                // Using host floats (but it's fine, this operation does not have guaranteed precision).
-                let res = f1.to_host().powf(f2.to_host()).to_soft();
+
+                let res = fixed_float_value(intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
+                    // Using host floats (but it's fine, this operation does not have guaranteed precision).
+                    let res = f1.to_host().powf(f2.to_host()).to_soft();
+
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    apply_random_float_error_ulp(
+                        this, res, 2, // log2(4)
+                    )
+                });
                 let res = this.adjust_nan(res, &[f1, f2]);
                 this.write_scalar(res, dest)?;
             }
             "powf64" => {
-                // FIXME: apply random relative error but without altering behaviour of powf
                 let [f1, f2] = check_intrinsic_arg_count(args)?;
                 let f1 = this.read_scalar(f1)?.to_f64()?;
                 let f2 = this.read_scalar(f2)?.to_f64()?;
-                // Using host floats (but it's fine, this operation does not have guaranteed precision).
-                let res = f1.to_host().powf(f2.to_host()).to_soft();
+
+                let res = fixed_float_value(intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
+                    // Using host floats (but it's fine, this operation does not have guaranteed precision).
+                    let res = f1.to_host().powf(f2.to_host()).to_soft();
+
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    apply_random_float_error_ulp(
+                        this, res, 2, // log2(4)
+                    )
+                });
                 let res = this.adjust_nan(res, &[f1, f2]);
                 this.write_scalar(res, dest)?;
             }
 
             "powif32" => {
-                // FIXME: apply random relative error but without altering behaviour of powi
                 let [f, i] = check_intrinsic_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f32()?;
                 let i = this.read_scalar(i)?.to_i32()?;
-                // Using host floats (but it's fine, this operation does not have guaranteed precision).
-                let res = f.to_host().powi(i).to_soft();
+
+                let res = fixed_powi_float_value(f, i).unwrap_or_else(|| {
+                    // Using host floats (but it's fine, this operation does not have guaranteed precision).
+                    let res = f.to_host().powi(i).to_soft();
+
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    apply_random_float_error_ulp(
+                        this, res, 2, // log2(4)
+                    )
+                });
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
             "powif64" => {
-                // FIXME: apply random relative error but without altering behaviour of powi
                 let [f, i] = check_intrinsic_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f64()?;
                 let i = this.read_scalar(i)?.to_i32()?;
-                // Using host floats (but it's fine, this operation does not have guaranteed precision).
-                let res = f.to_host().powi(i).to_soft();
+
+                let res = fixed_powi_float_value(f, i).unwrap_or_else(|| {
+                    // Using host floats (but it's fine, this operation does not have guaranteed precision).
+                    let res = f.to_host().powi(i).to_soft();
+
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    apply_random_float_error_ulp(
+                        this, res, 2, // log2(4)
+                    )
+                });
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
@@ -425,3 +475,97 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         interp_ok(EmulateItemResult::NeedsReturn)
     }
 }
+
+/// Applies a random ULP floating point error to `val` and returns the new value.
+/// So if you want an X ULP error, `ulp_exponent` should be log2(X).
+///
+/// Will fail if `val` is not a floating point number.
+fn apply_random_float_error_to_imm<'tcx>(
+    ecx: &mut MiriInterpCx<'tcx>,
+    val: ImmTy<'tcx>,
+    ulp_exponent: u32,
+) -> InterpResult<'tcx, ImmTy<'tcx>> {
+    let scalar = val.to_scalar_int()?;
+    let res: ScalarInt = match val.layout.ty.kind() {
+        ty::Float(FloatTy::F16) =>
+            apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(),
+        ty::Float(FloatTy::F32) =>
+            apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(),
+        ty::Float(FloatTy::F64) =>
+            apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(),
+        ty::Float(FloatTy::F128) =>
+            apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(),
+        _ => bug!("intrinsic called with non-float input type"),
+    };
+
+    interp_ok(ImmTy::from_scalar_int(res, val.layout))
+}
+
+/// For the intrinsics:
+/// - sinf32, sinf64
+/// - cosf32, cosf64
+/// - expf32, expf64, exp2f32, exp2f64
+/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
+/// - powf32, powf64
+///
+/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
+/// (specifically, C23 annex F.10)  when given `args` as arguments. Outputs that are unaffected by a relative error
+/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
+/// implementation. Returns `None` if no specific value is guaranteed.
+fn fixed_float_value<S: Semantics>(
+    intrinsic_name: &str,
+    args: &[IeeeFloat<S>],
+) -> Option<IeeeFloat<S>> {
+    let one = IeeeFloat::<S>::one();
+    match (intrinsic_name, args) {
+        // cos(+- 0) = 1
+        ("cosf32" | "cosf64", [input]) if input.is_zero() => Some(one),
+
+        // e^0 = 1
+        ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => Some(one),
+
+        // 1^y = 1 for any y, even a NaN.
+        ("powf32" | "powf64", [base, _]) if *base == one => Some(one),
+
+        // (-1)^(±INF) = 1
+        ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => Some(one),
+
+        // FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate
+        // the NaN. We should return either 1 or the NaN non-deterministically here.
+        // But for now, just handle them all the same.
+        // x^(±0) = 1 for any x, even a NaN
+        ("powf32" | "powf64", [_, exp]) if exp.is_zero() => Some(one),
+
+        // There are a lot of cases for fixed outputs according to the C Standard, but these are mainly INF or zero
+        // which are not affected by the applied error.
+        _ => None,
+    }
+}
+
+/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the C standard
+/// (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
+fn fixed_powi_float_value<S: Semantics>(base: IeeeFloat<S>, exp: i32) -> Option<IeeeFloat<S>> {
+    match (base.category(), exp) {
+        // x^0 = 1, if x is not a Signaling NaN
+        // FIXME(#4286): The C ecosystem is inconsistent with handling sNaN's, some return 1 others propogate
+        // the NaN. We should return either 1 or the NaN non-deterministically here.
+        // But for now, just handle them all the same.
+        (_, 0) => Some(IeeeFloat::<S>::one()),
+
+        _ => None,
+    }
+}
+
+/// Given an floating-point operation and a floating-point value, clamps the result to the output
+/// range of the given operation.
+fn clamp_float_value<S: Semantics>(intrinsic_name: &str, val: IeeeFloat<S>) -> IeeeFloat<S> {
+    match intrinsic_name {
+        // sin and cos: [-1, 1]
+        "sinf32" | "cosf32" | "sinf64" | "cosf64" =>
+            val.clamp(IeeeFloat::<S>::one().neg(), IeeeFloat::<S>::one()),
+        // exp: [0, +INF]
+        "expf32" | "exp2f32" | "expf64" | "exp2f64" =>
+            IeeeFloat::<S>::maximum(val, IeeeFloat::<S>::ZERO),
+        _ => val,
+    }
+}
diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs
index 2ff29c7ac1a..d1355a21684 100644
--- a/src/tools/miri/src/math.rs
+++ b/src/tools/miri/src/math.rs
@@ -151,6 +151,20 @@ pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFl
     }
 }
 
+/// Extend functionality of rustc_apfloat softfloats
+pub trait IeeeExt: rustc_apfloat::Float {
+    #[inline]
+    fn one() -> Self {
+        Self::from_u128(1).value
+    }
+
+    #[inline]
+    fn clamp(self, min: Self, max: Self) -> Self {
+        self.maximum(min).minimum(max)
+    }
+}
+impl<S: rustc_apfloat::ieee::Semantics> IeeeExt for IeeeFloat<S> {}
+
 #[cfg(test)]
 mod tests {
     use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS};
diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs
index 323b95d5f5f..d7bb16f0858 100644
--- a/src/tools/miri/src/shims/alloc.rs
+++ b/src/tools/miri/src/shims/alloc.rs
@@ -13,10 +13,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // alignment requirement and size less than or equal to the size requested."
         // So first we need to figure out what the limits are for "fundamental alignment".
         // This is given by `alignof(max_align_t)`. The following list is taken from
-        // `library/std/src/sys/pal/common/alloc.rs` (where this is called `MIN_ALIGN`) and should
+        // `library/std/src/sys/alloc/mod.rs` (where this is called `MIN_ALIGN`) and should
         // be kept in sync.
         let max_fundamental_align = match this.tcx.sess.target.arch.as_ref() {
-            "x86" | "arm" | "mips" | "mips32r6" | "powerpc" | "powerpc64" | "wasm32" => 8,
+            "x86" | "arm" | "loongarch32" | "mips" | "mips32r6" | "powerpc" | "powerpc64"
+            | "wasm32" => 8,
             "x86_64" | "aarch64" | "mips64" | "mips64r6" | "s390x" | "sparc64" | "loongarch64" =>
                 16,
             arch => bug!("unsupported target architecture for malloc: `{}`", arch),
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 98a88cfd62d..7ce0bc88517 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -45,6 +45,31 @@ macro_rules! assert_approx_eq {
     };
 }
 
+
+/// From IEEE 754 a Signaling NaN for single precision has the following representation:
+/// ```
+/// s | 1111 1111 | 0x..x
+/// ````
+/// Were at least one `x` is a 1.
+///
+/// This sNaN has the following representation and is used for testing purposes.:
+/// ```
+/// 0 | 1111111 | 01..0
+/// ```
+const SNAN_F32: f32 = f32::from_bits(0x7fa00000);
+
+/// From IEEE 754 a Signaling NaN for double precision has the following representation:
+/// ```
+/// s | 1111 1111 111 | 0x..x
+/// ````
+/// Were at least one `x` is a 1.
+///
+/// This sNaN has the following representation and is used for testing purposes.:
+/// ```
+/// 0 | 1111 1111 111 | 01..0
+/// ```
+const SNAN_F64: f64 = f64::from_bits(0x7ff4000000000000);
+
 fn main() {
     basic();
     casts();
@@ -1008,17 +1033,84 @@ pub fn libm() {
     assert_approx_eq!(25f32.powf(-2f32), 0.0016f32);
     assert_approx_eq!(400f64.powf(0.5f64), 20f64);
 
+    // Some inputs to powf and powi result in fixed outputs
+    // and thus must be exactly equal to that value.
+    // C standard says:
+    // 1^y = 1 for any y, even a NaN.
+    assert_eq!(1f32.powf(10.0), 1.0);
+    assert_eq!(1f64.powf(100.0), 1.0);
+    assert_eq!(1f32.powf(f32::INFINITY), 1.0);
+    assert_eq!(1f64.powf(f64::INFINITY), 1.0);
+    assert_eq!(1f32.powf(f32::NAN), 1.0);
+    assert_eq!(1f64.powf(f64::NAN), 1.0);
+
+    // f*::NAN is a quiet NAN and should return 1 as well.
+    assert_eq!(f32::NAN.powf(0.0), 1.0);
+    assert_eq!(f64::NAN.powf(0.0), 1.0);
+
+    assert_eq!(42f32.powf(0.0), 1.0);
+    assert_eq!(42f64.powf(0.0), 1.0);
+    assert_eq!(f32::INFINITY.powf(0.0), 1.0);
+    assert_eq!(f64::INFINITY.powf(0.0), 1.0);
+
+    // f*::NAN is a quiet NAN and should return 1 as well.
+    assert_eq!(f32::NAN.powi(0), 1.0);
+    assert_eq!(f64::NAN.powi(0), 1.0);
+
+    assert_eq!(10.0f32.powi(0), 1.0);
+    assert_eq!(10.0f64.powi(0), 1.0);
+    assert_eq!(f32::INFINITY.powi(0), 1.0);
+    assert_eq!(f64::INFINITY.powi(0), 1.0);
+
+    assert_eq!((-1f32).powf(f32::INFINITY), 1.0);
+    assert_eq!((-1f64).powf(f64::INFINITY), 1.0);
+    assert_eq!((-1f32).powf(f32::NEG_INFINITY), 1.0);
+    assert_eq!((-1f64).powf(f64::NEG_INFINITY), 1.0);
+
+    // For pow (powf in rust) the C standard says:
+    // x^0 = 1 for all x even a sNaN
+    // FIXME(#4286): this does not match the behavior of all implementations.
+    assert_eq!(SNAN_F32.powf(0.0), 1.0);
+    assert_eq!(SNAN_F64.powf(0.0), 1.0);
+
+    // For pown (powi in rust) the C standard says:
+    // x^0 = 1 for all x even a sNaN
+    // FIXME(#4286): this does not match the behavior of all implementations.
+    assert_eq!(SNAN_F32.powi(0), 1.0);
+    assert_eq!(SNAN_F64.powi(0), 1.0);
+
+    assert_eq!(0f32.powi(10), 0.0);
+    assert_eq!(0f64.powi(100), 0.0);
+    assert_eq!(0f32.powi(9), 0.0);
+    assert_eq!(0f64.powi(99), 0.0);
+
+    assert_biteq((-0f32).powf(10.0), 0.0, "-0^x = +0 where x is positive");
+    assert_biteq((-0f64).powf(100.0), 0.0, "-0^x = +0 where x is positive");
+    assert_biteq((-0f32).powf(9.0), -0.0, "-0^x = -0 where x is negative");
+    assert_biteq((-0f64).powf(99.0), -0.0, "-0^x = -0 where x is negative");
+
+    assert_biteq((-0f32).powi(10), 0.0, "-0^x = +0 where x is positive");
+    assert_biteq((-0f64).powi(100), 0.0, "-0^x = +0 where x is positive");
+    assert_biteq((-0f32).powi(9), -0.0, "-0^x = -0 where x is negative");
+    assert_biteq((-0f64).powi(99), -0.0, "-0^x = -0 where x is negative");
+
     assert_approx_eq!(1f32.exp(), f32::consts::E);
     assert_approx_eq!(1f64.exp(), f64::consts::E);
+    assert_eq!(0f32.exp(), 1.0);
+    assert_eq!(0f64.exp(), 1.0);
 
     assert_approx_eq!(1f32.exp_m1(), f32::consts::E - 1.0);
     assert_approx_eq!(1f64.exp_m1(), f64::consts::E - 1.0);
 
     assert_approx_eq!(10f32.exp2(), 1024f32);
     assert_approx_eq!(50f64.exp2(), 1125899906842624f64);
+    assert_eq!(0f32.exp2(), 1.0);
+    assert_eq!(0f64.exp2(), 1.0);
 
     assert_approx_eq!(f32::consts::E.ln(), 1f32);
-    assert_approx_eq!(1f64.ln(), 0f64);
+    assert_approx_eq!(f64::consts::E.ln(), 1f64);
+    assert_eq!(1f32.ln(), 0.0);
+    assert_eq!(1f64.ln(), 0.0);
 
     assert_approx_eq!(0f32.ln_1p(), 0f32);
     assert_approx_eq!(0f64.ln_1p(), 0f64);
@@ -1047,7 +1139,8 @@ pub fn libm() {
 
     // Trigonometric functions.
 
-    assert_approx_eq!(0f32.sin(), 0f32);
+    assert_eq!(0f32.sin(), 0f32);
+    assert_eq!(0f64.sin(), 0f64);
     assert_approx_eq!((f64::consts::PI / 2f64).sin(), 1f64);
     assert_approx_eq!(f32::consts::FRAC_PI_6.sin(), 0.5);
     assert_approx_eq!(f64::consts::FRAC_PI_6.sin(), 0.5);
@@ -1059,7 +1152,23 @@ pub fn libm() {
     assert_approx_eq!(2.0f32.asinh(), 1.443635475178810342493276740273105f32);
     assert_approx_eq!((-2.0f64).asinh(), -1.443635475178810342493276740273105f64);
 
-    assert_approx_eq!(0f32.cos(), 1f32);
+    // Ensure `sin` always returns something that is a valid input for `asin`, and same for
+    // `cos` and `acos`.
+    let halve_pi_f32 = std::f32::consts::FRAC_PI_2;
+    let halve_pi_f64 = std::f64::consts::FRAC_PI_2;
+    let pi_f32 = std::f32::consts::PI;
+    let pi_f64 = std::f64::consts::PI;
+    for _ in 0..64 {
+        // sin() should be clamped to [-1, 1] so asin() can never return NaN
+        assert!(!halve_pi_f32.sin().asin().is_nan());
+        assert!(!halve_pi_f64.sin().asin().is_nan());
+        // cos() should be clamped to [-1, 1] so acos() can never return NaN
+        assert!(!pi_f32.cos().acos().is_nan());
+        assert!(!pi_f64.cos().acos().is_nan());
+    }
+
+    assert_eq!(0f32.cos(), 1f32);
+    assert_eq!(0f64.cos(), 1f64);
     assert_approx_eq!((f64::consts::PI * 2f64).cos(), 1f64);
     assert_approx_eq!(f32::consts::FRAC_PI_3.cos(), 0.5);
     assert_approx_eq!(f64::consts::FRAC_PI_3.cos(), 0.5);