about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-07-02 08:21:57 +0200
committerRalf Jung <post@ralfj.de>2024-07-02 08:21:57 +0200
commitcbea3d7add356898fdfc84e9433baaa514df5552 (patch)
treed3a4cadce7ec832453d49cb1d9d1c6fce3dd6ce5 /src
parent3e44883606ad4df77018c157971ba7a47e540088 (diff)
parent9f32459c988dec799458f304bbc49ed5dc6ef772 (diff)
downloadrust-cbea3d7add356898fdfc84e9433baaa514df5552.tar.gz
rust-cbea3d7add356898fdfc84e9433baaa514df5552.zip
Merge from rustc
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/src/bin/rustc.rs27
-rw-r--r--src/bootstrap/src/bin/rustdoc.rs20
-rw-r--r--src/bootstrap/src/core/build_steps/clean.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs64
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs37
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs19
-rw-r--r--src/bootstrap/src/core/build_steps/install.rs6
-rw-r--r--src/bootstrap/src/core/build_steps/perf.rs32
-rw-r--r--src/bootstrap/src/core/build_steps/run.rs15
-rw-r--r--src/bootstrap/src/core/build_steps/suggest.rs1
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs128
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs20
-rw-r--r--src/bootstrap/src/core/build_steps/vendor.rs6
-rw-r--r--src/bootstrap/src/core/builder.rs36
-rw-r--r--src/bootstrap/src/core/config/flags.rs4
-rw-r--r--src/bootstrap/src/core/download.rs5
-rw-r--r--src/bootstrap/src/lib.rs86
-rw-r--r--src/bootstrap/src/utils/bin_helpers.rs50
-rw-r--r--src/bootstrap/src/utils/dylib.rs40
-rw-r--r--src/bootstrap/src/utils/exec.rs102
-rw-r--r--src/bootstrap/src/utils/helpers.rs16
-rw-r--r--src/bootstrap/src/utils/mod.rs2
-rw-r--r--src/bootstrap/src/utils/render_tests.rs17
-rw-r--r--src/bootstrap/src/utils/shared_helpers.rs112
-rw-r--r--src/bootstrap/src/utils/shared_helpers/tests.rs28
-rw-r--r--src/bootstrap/src/utils/tarball.rs12
-rw-r--r--src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile27
-rw-r--r--src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile3
-rw-r--r--src/ci/docker/host-x86_64/dist-various-2/Dockerfile2
-rwxr-xr-xsrc/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh54
-rw-r--r--src/ci/docker/host-x86_64/x86_64-fuchsia/Dockerfile (renamed from src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile)10
-rwxr-xr-xsrc/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh (renamed from src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh)10
-rw-r--r--src/ci/docker/host-x86_64/x86_64-rust-for-linux/Dockerfile (renamed from src/ci/docker/host-x86_64/rfl/Dockerfile)0
-rw-r--r--src/ci/github-actions/jobs.yml24
-rw-r--r--src/doc/rustc/src/platform-support.md8
-rw-r--r--src/doc/rustc/src/platform-support/loongarch-linux.md139
-rw-r--r--src/doc/rustc/src/platform-support/loongarch-none.md56
-rw-r--r--src/doc/style-guide/src/nightly.md12
-rw-r--r--src/etc/completions/x.py.fish2
-rw-r--r--src/etc/completions/x.py.ps12
-rw-r--r--src/etc/completions/x.py.zsh2
-rw-r--r--src/librustdoc/clean/mod.rs14
-rw-r--r--src/librustdoc/clean/simplify.rs2
-rw-r--r--src/librustdoc/clean/types.rs4
-rw-r--r--src/librustdoc/json/conversions.rs16
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs2
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed5
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs3
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr25
-rw-r--r--src/tools/compiletest/src/command-list.rs2
-rw-r--r--src/tools/run-make-support/Cargo.toml1
-rw-r--r--src/tools/run-make-support/src/command.rs8
-rw-r--r--src/tools/run-make-support/src/lib.rs18
-rw-r--r--src/tools/run-make-support/src/llvm.rs7
-rw-r--r--src/tools/run-make-support/src/rustc.rs14
-rw-r--r--src/tools/run-make-support/src/rustdoc.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs1
-rw-r--r--src/tools/rustc-perf-wrapper/Cargo.toml7
-rw-r--r--src/tools/rustc-perf-wrapper/README.md3
-rw-r--r--src/tools/rustc-perf-wrapper/src/config.rs45
-rw-r--r--src/tools/rustc-perf-wrapper/src/main.rs130
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt20
-rw-r--r--src/tools/tidy/src/ext_tool_checks.rs7
66 files changed, 1035 insertions, 576 deletions
diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index d2274199177..009e62469b4 100644
--- a/src/bootstrap/src/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -20,26 +20,24 @@ use std::path::{Path, PathBuf};
 use std::process::{Child, Command};
 use std::time::Instant;
 
-use dylib_util::{dylib_path, dylib_path_var, exe};
+use shared_helpers::{
+    dylib_path, dylib_path_var, exe, maybe_dump, parse_rustc_stage, parse_rustc_verbose,
+    parse_value_from_args,
+};
 
-#[path = "../utils/bin_helpers.rs"]
-mod bin_helpers;
-
-#[path = "../utils/dylib.rs"]
-mod dylib_util;
+#[path = "../utils/shared_helpers.rs"]
+mod shared_helpers;
 
 fn main() {
     let orig_args = env::args_os().skip(1).collect::<Vec<_>>();
     let mut args = orig_args.clone();
-    let arg =
-        |name| orig_args.windows(2).find(|args| args[0] == name).and_then(|args| args[1].to_str());
 
-    let stage = bin_helpers::parse_rustc_stage();
-    let verbose = bin_helpers::parse_rustc_verbose();
+    let stage = parse_rustc_stage();
+    let verbose = parse_rustc_verbose();
 
     // Detect whether or not we're a build script depending on whether --target
     // is passed (a bit janky...)
-    let target = arg("--target");
+    let target = parse_value_from_args(&orig_args, "--target");
     let version = args.iter().find(|w| &**w == "-vV");
 
     // Use a different compiler for build scripts, since there may not yet be a
@@ -102,7 +100,7 @@ fn main() {
     cmd.args(&args).env(dylib_path_var(), env::join_paths(&dylib_path).unwrap());
 
     // Get the name of the crate we're compiling, if any.
-    let crate_name = arg("--crate-name");
+    let crate_name = parse_value_from_args(&orig_args, "--crate-name");
 
     if let Some(crate_name) = crate_name {
         if let Some(target) = env::var_os("RUSTC_TIME") {
@@ -143,10 +141,11 @@ fn main() {
             cmd.arg("-C").arg("panic=abort");
         }
 
+        let crate_type = parse_value_from_args(&orig_args, "--crate-type");
         // `-Ztls-model=initial-exec` must not be applied to proc-macros, see
         // issue https://github.com/rust-lang/rust/issues/100530
         if env::var("RUSTC_TLS_MODEL_INITIAL_EXEC").is_ok()
-            && arg("--crate-type") != Some("proc-macro")
+            && crate_type != Some("proc-macro")
             && !matches!(crate_name, Some("proc_macro2" | "quote" | "syn" | "synstructure"))
         {
             cmd.arg("-Ztls-model=initial-exec");
@@ -251,7 +250,7 @@ fn main() {
         eprintln!("{prefix} libdir: {libdir:?}");
     }
 
-    bin_helpers::maybe_dump(format!("stage{stage}-rustc"), &cmd);
+    maybe_dump(format!("stage{stage}-rustc"), &cmd);
 
     let start = Instant::now();
     let (child, status) = {
diff --git a/src/bootstrap/src/bin/rustdoc.rs b/src/bootstrap/src/bin/rustdoc.rs
index b4d1415189c..ba6b0c2dbda 100644
--- a/src/bootstrap/src/bin/rustdoc.rs
+++ b/src/bootstrap/src/bin/rustdoc.rs
@@ -6,19 +6,19 @@ use std::env;
 use std::path::PathBuf;
 use std::process::Command;
 
-use dylib_util::{dylib_path, dylib_path_var};
+use shared_helpers::{
+    dylib_path, dylib_path_var, maybe_dump, parse_rustc_stage, parse_rustc_verbose,
+    parse_value_from_args,
+};
 
-#[path = "../utils/bin_helpers.rs"]
-mod bin_helpers;
-
-#[path = "../utils/dylib.rs"]
-mod dylib_util;
+#[path = "../utils/shared_helpers.rs"]
+mod shared_helpers;
 
 fn main() {
     let args = env::args_os().skip(1).collect::<Vec<_>>();
 
-    let stage = bin_helpers::parse_rustc_stage();
-    let verbose = bin_helpers::parse_rustc_verbose();
+    let stage = parse_rustc_stage();
+    let verbose = parse_rustc_verbose();
 
     let rustdoc = env::var_os("RUSTDOC_REAL").expect("RUSTDOC_REAL was not set");
     let libdir = env::var_os("RUSTDOC_LIBDIR").expect("RUSTDOC_LIBDIR was not set");
@@ -26,7 +26,7 @@ fn main() {
 
     // Detect whether or not we're a build script depending on whether --target
     // is passed (a bit janky...)
-    let target = args.windows(2).find(|w| &*w[0] == "--target").and_then(|w| w[1].to_str());
+    let target = parse_value_from_args(&args, "--target");
 
     let mut dylib_path = dylib_path();
     dylib_path.insert(0, PathBuf::from(libdir.clone()));
@@ -62,7 +62,7 @@ fn main() {
     cmd.arg("-Zunstable-options");
     cmd.arg("--check-cfg=cfg(bootstrap)");
 
-    bin_helpers::maybe_dump(format!("stage{stage}-rustdoc"), &cmd);
+    maybe_dump(format!("stage{stage}-rustdoc"), &cmd);
 
     if verbose > 1 {
         eprintln!(
diff --git a/src/bootstrap/src/core/build_steps/clean.rs b/src/bootstrap/src/core/build_steps/clean.rs
index a81d6403013..479af4af666 100644
--- a/src/bootstrap/src/core/build_steps/clean.rs
+++ b/src/bootstrap/src/core/build_steps/clean.rs
@@ -85,7 +85,7 @@ macro_rules! clean_crate_tree {
 
                 // NOTE: doesn't use `run_cargo` because we don't want to save a stamp file,
                 // and doesn't use `stream_cargo` to avoid passing `--message-format` which `clean` doesn't accept.
-                builder.run(&mut cargo);
+                builder.run(cargo);
             }
         }
     )+ }
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 5b18cb3f7e1..de3b938e427 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -27,6 +27,7 @@ use crate::core::builder::crate_description;
 use crate::core::builder::Cargo;
 use crate::core::builder::{Builder, Kind, PathSet, RunConfig, ShouldRun, Step, TaskPath};
 use crate::core::config::{DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection};
+use crate::utils::exec::BootstrapCommand;
 use crate::utils::helpers::{
     exe, get_clang_cl_resource_dir, is_debug_info, is_dylib, output, symlink_dir, t, up_to_date,
 };
@@ -160,9 +161,10 @@ impl Step for Std {
             // This check is specific to testing std itself; see `test::Std` for more details.
             && !self.force_recompile
         {
+            let sysroot = builder.ensure(Sysroot { compiler, force_recompile: false });
             cp_rustc_component_to_ci_sysroot(
                 builder,
-                compiler,
+                &sysroot,
                 builder.config.ci_rust_std_contents(),
             );
             return;
@@ -771,7 +773,7 @@ impl Step for StartupObjects {
             let src_file = &src_dir.join(file.to_string() + ".rs");
             let dst_file = &dst_dir.join(file.to_string() + ".o");
             if !up_to_date(src_file, dst_file) {
-                let mut cmd = Command::new(&builder.initial_rustc);
+                let mut cmd = BootstrapCommand::new(&builder.initial_rustc);
                 cmd.env("RUSTC_BOOTSTRAP", "1");
                 if !builder.local_rebuild {
                     // a local_rebuild compiler already has stage1 features
@@ -796,12 +798,7 @@ impl Step for StartupObjects {
     }
 }
 
-fn cp_rustc_component_to_ci_sysroot(
-    builder: &Builder<'_>,
-    compiler: Compiler,
-    contents: Vec<String>,
-) {
-    let sysroot = builder.ensure(Sysroot { compiler, force_recompile: false });
+fn cp_rustc_component_to_ci_sysroot(builder: &Builder<'_>, sysroot: &Path, contents: Vec<String>) {
     let ci_rustc_dir = builder.config.ci_rustc_dir();
 
     for file in contents {
@@ -880,13 +877,7 @@ impl Step for Rustc {
         // NOTE: the ABI of the beta compiler is different from the ABI of the downloaded compiler,
         // so its artifacts can't be reused.
         if builder.download_rustc() && compiler.stage != 0 {
-            // Copy the existing artifacts instead of rebuilding them.
-            // NOTE: this path is only taken for tools linking to rustc-dev (including ui-fulldeps tests).
-            cp_rustc_component_to_ci_sysroot(
-                builder,
-                compiler,
-                builder.config.ci_rustc_dev_contents(),
-            );
+            builder.ensure(Sysroot { compiler, force_recompile: false });
             return compiler.stage;
         }
 
@@ -1633,31 +1624,44 @@ impl Step for Sysroot {
         let sysroot_lib_rustlib_src_rust = sysroot_lib_rustlib_src.join("rust");
         if let Err(e) = symlink_dir(&builder.config, &builder.src, &sysroot_lib_rustlib_src_rust) {
             eprintln!(
-                "WARNING: creating symbolic link `{}` to `{}` failed with {}",
+                "ERROR: creating symbolic link `{}` to `{}` failed with {}",
                 sysroot_lib_rustlib_src_rust.display(),
                 builder.src.display(),
                 e,
             );
             if builder.config.rust_remap_debuginfo {
                 eprintln!(
-                    "WARNING: some `tests/ui` tests will fail when lacking `{}`",
+                    "ERROR: some `tests/ui` tests will fail when lacking `{}`",
                     sysroot_lib_rustlib_src_rust.display(),
                 );
             }
+            build_helper::exit!(1);
         }
-        // Same for the rustc-src component.
-        let sysroot_lib_rustlib_rustcsrc = sysroot.join("lib/rustlib/rustc-src");
-        t!(fs::create_dir_all(&sysroot_lib_rustlib_rustcsrc));
-        let sysroot_lib_rustlib_rustcsrc_rust = sysroot_lib_rustlib_rustcsrc.join("rust");
-        if let Err(e) =
-            symlink_dir(&builder.config, &builder.src, &sysroot_lib_rustlib_rustcsrc_rust)
-        {
-            eprintln!(
-                "WARNING: creating symbolic link `{}` to `{}` failed with {}",
-                sysroot_lib_rustlib_rustcsrc_rust.display(),
-                builder.src.display(),
-                e,
+
+        // Unlike rust-src component, we have to handle rustc-src a bit differently.
+        // When using CI rustc, we copy rustc-src component from its sysroot,
+        // otherwise we handle it in a similar way what we do for rust-src above.
+        if builder.download_rustc() {
+            cp_rustc_component_to_ci_sysroot(
+                builder,
+                &sysroot,
+                builder.config.ci_rustc_dev_contents(),
             );
+        } else {
+            let sysroot_lib_rustlib_rustcsrc = sysroot.join("lib/rustlib/rustc-src");
+            t!(fs::create_dir_all(&sysroot_lib_rustlib_rustcsrc));
+            let sysroot_lib_rustlib_rustcsrc_rust = sysroot_lib_rustlib_rustcsrc.join("rust");
+            if let Err(e) =
+                symlink_dir(&builder.config, &builder.src, &sysroot_lib_rustlib_rustcsrc_rust)
+            {
+                eprintln!(
+                    "ERROR: creating symbolic link `{}` to `{}` failed with {}",
+                    sysroot_lib_rustlib_rustcsrc_rust.display(),
+                    builder.src.display(),
+                    e,
+                );
+                build_helper::exit!(1);
+            }
         }
 
         sysroot
@@ -2076,7 +2080,7 @@ pub fn stream_cargo(
     tail_args: Vec<String>,
     cb: &mut dyn FnMut(CargoMessage<'_>),
 ) -> bool {
-    let mut cargo = Command::from(cargo);
+    let mut cargo = BootstrapCommand::from(cargo).command;
     // Instruct Cargo to give us json messages on stdout, critically leaving
     // stderr as piped so we can get those pretty colors.
     let mut message_format = if builder.config.json_output {
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 0f24e91d81d..08795efe5bb 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -26,6 +26,7 @@ use crate::core::build_steps::tool::{self, Tool};
 use crate::core::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
 use crate::utils::channel::{self, Info};
+use crate::utils::exec::BootstrapCommand;
 use crate::utils::helpers::{
     exe, is_dylib, move_file, output, t, target_supports_cranelift_backend, timeit,
 };
@@ -1599,14 +1600,14 @@ impl Step for Extended {
             let _ = fs::remove_dir_all(&pkg);
 
             let pkgbuild = |component: &str| {
-                let mut cmd = Command::new("pkgbuild");
+                let mut cmd = BootstrapCommand::new("pkgbuild");
                 cmd.arg("--identifier")
                     .arg(format!("org.rust-lang.{}", component))
                     .arg("--scripts")
                     .arg(pkg.join(component))
                     .arg("--nopayload")
                     .arg(pkg.join(component).with_extension("pkg"));
-                builder.run(&mut cmd);
+                builder.run(cmd);
             };
 
             let prepare = |name: &str| {
@@ -1636,7 +1637,7 @@ impl Step for Extended {
             builder.create_dir(&pkg.join("res"));
             builder.create(&pkg.join("res/LICENSE.txt"), &license);
             builder.install(&etc.join("gfx/rust-logo.png"), &pkg.join("res"), 0o644);
-            let mut cmd = Command::new("productbuild");
+            let mut cmd = BootstrapCommand::new("productbuild");
             cmd.arg("--distribution")
                 .arg(xform(&etc.join("pkg/Distribution.xml")))
                 .arg("--resources")
@@ -1649,7 +1650,7 @@ impl Step for Extended {
                 .arg("--package-path")
                 .arg(&pkg);
             let _time = timeit(builder);
-            builder.run(&mut cmd);
+            builder.run(cmd);
         }
 
         if target.is_windows() {
@@ -1704,7 +1705,7 @@ impl Step for Extended {
 
             let heat_flags = ["-nologo", "-gg", "-sfrag", "-srd", "-sreg"];
             builder.run(
-                Command::new(&heat)
+                BootstrapCommand::new(&heat)
                     .current_dir(&exe)
                     .arg("dir")
                     .arg("rustc")
@@ -1720,7 +1721,7 @@ impl Step for Extended {
             );
             if built_tools.contains("rust-docs") {
                 builder.run(
-                    Command::new(&heat)
+                    BootstrapCommand::new(&heat)
                         .current_dir(&exe)
                         .arg("dir")
                         .arg("rust-docs")
@@ -1738,7 +1739,7 @@ impl Step for Extended {
                 );
             }
             builder.run(
-                Command::new(&heat)
+                BootstrapCommand::new(&heat)
                     .current_dir(&exe)
                     .arg("dir")
                     .arg("cargo")
@@ -1755,7 +1756,7 @@ impl Step for Extended {
                     .arg(etc.join("msi/remove-duplicates.xsl")),
             );
             builder.run(
-                Command::new(&heat)
+                BootstrapCommand::new(&heat)
                     .current_dir(&exe)
                     .arg("dir")
                     .arg("rust-std")
@@ -1771,7 +1772,7 @@ impl Step for Extended {
             );
             if built_tools.contains("rust-analyzer") {
                 builder.run(
-                    Command::new(&heat)
+                    BootstrapCommand::new(&heat)
                         .current_dir(&exe)
                         .arg("dir")
                         .arg("rust-analyzer")
@@ -1790,7 +1791,7 @@ impl Step for Extended {
             }
             if built_tools.contains("clippy") {
                 builder.run(
-                    Command::new(&heat)
+                    BootstrapCommand::new(&heat)
                         .current_dir(&exe)
                         .arg("dir")
                         .arg("clippy")
@@ -1809,7 +1810,7 @@ impl Step for Extended {
             }
             if built_tools.contains("miri") {
                 builder.run(
-                    Command::new(&heat)
+                    BootstrapCommand::new(&heat)
                         .current_dir(&exe)
                         .arg("dir")
                         .arg("miri")
@@ -1827,7 +1828,7 @@ impl Step for Extended {
                 );
             }
             builder.run(
-                Command::new(&heat)
+                BootstrapCommand::new(&heat)
                     .current_dir(&exe)
                     .arg("dir")
                     .arg("rust-analysis")
@@ -1845,7 +1846,7 @@ impl Step for Extended {
             );
             if target.ends_with("windows-gnu") {
                 builder.run(
-                    Command::new(&heat)
+                    BootstrapCommand::new(&heat)
                         .current_dir(&exe)
                         .arg("dir")
                         .arg("rust-mingw")
@@ -1864,7 +1865,7 @@ impl Step for Extended {
             let candle = |input: &Path| {
                 let output = exe.join(input.file_stem().unwrap()).with_extension("wixobj");
                 let arch = if target.contains("x86_64") { "x64" } else { "x86" };
-                let mut cmd = Command::new(&candle);
+                let mut cmd = BootstrapCommand::new(&candle);
                 cmd.current_dir(&exe)
                     .arg("-nologo")
                     .arg("-dRustcDir=rustc")
@@ -1893,7 +1894,7 @@ impl Step for Extended {
                 if target.ends_with("windows-gnu") {
                     cmd.arg("-dGccDir=rust-mingw");
                 }
-                builder.run(&mut cmd);
+                builder.run(cmd);
             };
             candle(&xform(&etc.join("msi/rust.wxs")));
             candle(&etc.join("msi/ui.wxs"));
@@ -1925,7 +1926,7 @@ impl Step for Extended {
 
             builder.info(&format!("building `msi` installer with {light:?}"));
             let filename = format!("{}-{}.msi", pkgname(builder, "rust"), target.triple);
-            let mut cmd = Command::new(&light);
+            let mut cmd = BootstrapCommand::new(&light);
             cmd.arg("-nologo")
                 .arg("-ext")
                 .arg("WixUIExtension")
@@ -1962,7 +1963,7 @@ impl Step for Extended {
             cmd.arg("-sice:ICE57");
 
             let _time = timeit(builder);
-            builder.run(&mut cmd);
+            builder.run(cmd);
 
             if !builder.config.dry_run() {
                 t!(move_file(exe.join(&filename), distdir(builder).join(&filename)));
@@ -1971,7 +1972,7 @@ impl Step for Extended {
     }
 }
 
-fn add_env(builder: &Builder<'_>, cmd: &mut Command, target: TargetSelection) {
+fn add_env(builder: &Builder<'_>, cmd: &mut BootstrapCommand, target: TargetSelection) {
     let mut parts = builder.version.split('.');
     cmd.env("CFG_RELEASE_INFO", builder.rust_version())
         .env("CFG_RELEASE_NUM", &builder.version)
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 87c700dad06..4a5af25b3b2 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -249,6 +249,7 @@ impl Step for TheBook {
         let shared_assets = builder.ensure(SharedAssets { target });
 
         // build the command first so we don't nest GHA groups
+        // FIXME: this doesn't do anything!
         builder.rustdoc_cmd(compiler);
 
         // build the redirect pages
@@ -300,7 +301,7 @@ fn invoke_rustdoc(
         cmd.arg("-Z").arg("unstable-options").arg("--disable-minification");
     }
 
-    builder.run(&mut cmd);
+    builder.run(cmd);
 }
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@@ -394,7 +395,7 @@ impl Step for Standalone {
             } else {
                 cmd.arg("--markdown-css").arg("rust.css");
             }
-            builder.run(&mut cmd);
+            builder.run(cmd);
         }
 
         // We open doc/index.html as the default if invoked as `x.py doc --open`
@@ -493,7 +494,7 @@ impl Step for Releases {
                 cmd.arg("--disable-minification");
             }
 
-            builder.run(&mut cmd);
+            builder.run(cmd);
         }
 
         // We open doc/RELEASES.html as the default if invoked as `x.py doc --open RELEASES.md`
@@ -737,7 +738,7 @@ fn doc_std(
         format!("library{} in {} format", crate_description(requested_crates), format.as_str());
     let _guard = builder.msg_doc(compiler, description, target);
 
-    builder.run(&mut cargo.into());
+    builder.run(cargo);
     builder.cp_link_r(&out_dir, out);
 }
 
@@ -862,7 +863,7 @@ impl Step for Rustc {
         let proc_macro_out_dir = builder.stage_out(compiler, Mode::Rustc).join("doc");
         symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
 
-        builder.run(&mut cargo.into());
+        builder.run(cargo);
 
         if !builder.config.dry_run() {
             // Sanity check on linked compiler crates
@@ -995,7 +996,7 @@ macro_rules! tool_doc {
                 symlink_dir_force(&builder.config, &out, &proc_macro_out_dir);
 
                 let _guard = builder.msg_doc(compiler, stringify!($tool).to_lowercase(), target);
-                builder.run(&mut cargo.into());
+                builder.run(cargo);
 
                 if !builder.config.dry_run() {
                     // Sanity check on linked doc directories
@@ -1079,7 +1080,7 @@ impl Step for ErrorIndex {
         index.arg(out);
         index.arg(&builder.version);
 
-        builder.run(&mut index);
+        builder.run(index);
     }
 }
 
@@ -1115,7 +1116,7 @@ impl Step for UnstableBookGen {
         cmd.arg(builder.src.join("src"));
         cmd.arg(out);
 
-        builder.run(&mut cmd);
+        builder.run(cmd);
     }
 }
 
@@ -1210,7 +1211,7 @@ impl Step for RustcBook {
             self.compiler.host,
             self.target,
         );
-        builder.run(&mut cmd);
+        builder.run(cmd);
         drop(doc_generator_guard);
 
         // Run rustbook/mdbook to generate the HTML pages.
diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs
index c47233ca42a..7ee1aca2abc 100644
--- a/src/bootstrap/src/core/build_steps/install.rs
+++ b/src/bootstrap/src/core/build_steps/install.rs
@@ -6,11 +6,11 @@
 use std::env;
 use std::fs;
 use std::path::{Component, Path, PathBuf};
-use std::process::Command;
 
 use crate::core::build_steps::dist;
 use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::core::config::{Config, TargetSelection};
+use crate::utils::exec::BootstrapCommand;
 use crate::utils::helpers::t;
 use crate::utils::tarball::GeneratedTarball;
 use crate::{Compiler, Kind};
@@ -102,7 +102,7 @@ fn install_sh(
     let empty_dir = builder.out.join("tmp/empty_dir");
     t!(fs::create_dir_all(&empty_dir));
 
-    let mut cmd = Command::new(SHELL);
+    let mut cmd = BootstrapCommand::new(SHELL);
     cmd.current_dir(&empty_dir)
         .arg(sanitize_sh(&tarball.decompressed_output().join("install.sh")))
         .arg(format!("--prefix={}", prepare_dir(&destdir_env, prefix)))
@@ -113,7 +113,7 @@ fn install_sh(
         .arg(format!("--libdir={}", prepare_dir(&destdir_env, libdir)))
         .arg(format!("--mandir={}", prepare_dir(&destdir_env, mandir)))
         .arg("--disable-ldconfig");
-    builder.run(&mut cmd);
+    builder.run(cmd);
     t!(fs::remove_dir_all(&empty_dir));
 }
 
diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs
index 9d70ca6bd71..f41b5fe10f1 100644
--- a/src/bootstrap/src/core/build_steps/perf.rs
+++ b/src/bootstrap/src/core/build_steps/perf.rs
@@ -1,7 +1,5 @@
-use std::process::Command;
-
 use crate::core::build_steps::compile::{Std, Sysroot};
-use crate::core::build_steps::tool::RustcPerf;
+use crate::core::build_steps::tool::{RustcPerf, Tool};
 use crate::core::builder::Builder;
 use crate::core::config::DebuginfoLevel;
 
@@ -22,24 +20,16 @@ Consider setting `rust.debuginfo-level = 1` in `config.toml`."#);
     let sysroot = builder.ensure(Sysroot::new(compiler));
     let rustc = sysroot.join("bin/rustc");
 
-    let results_dir = builder.build.tempdir().join("rustc-perf");
-
-    let mut cmd = Command::new(collector);
-    let cmd = cmd
-        .arg("profile_local")
-        .arg("eprintln")
-        .arg("--out-dir")
-        .arg(&results_dir)
-        .arg("--include")
-        .arg("helloworld")
-        .arg(&rustc);
-
-    builder.info(&format!("Running `rustc-perf` using `{}`", rustc.display()));
+    let rustc_perf_dir = builder.build.tempdir().join("rustc-perf");
+    let profile_results_dir = rustc_perf_dir.join("results");
 
-    // We need to set the working directory to `src/tools/perf`, so that it can find the directory
-    // with compile-time benchmarks.
-    let cmd = cmd.current_dir(builder.src.join("src/tools/rustc-perf"));
-    builder.build.run(cmd);
+    // We need to take args passed after `--` and pass them to `rustc-perf-wrapper`
+    let args = std::env::args().skip_while(|a| a != "--").skip(1);
 
-    builder.info(&format!("You can find the results at `{}`", results_dir.display()));
+    let mut cmd = builder.tool_cmd(Tool::RustcPerfWrapper);
+    cmd.env("RUSTC_REAL", rustc)
+        .env("PERF_COLLECTOR", collector)
+        .env("PERF_RESULT_DIR", profile_results_dir)
+        .args(args);
+    builder.run(&mut cmd);
 }
diff --git a/src/bootstrap/src/core/build_steps/run.rs b/src/bootstrap/src/core/build_steps/run.rs
index 9268b335db7..22d5efa5d95 100644
--- a/src/bootstrap/src/core/build_steps/run.rs
+++ b/src/bootstrap/src/core/build_steps/run.rs
@@ -50,7 +50,7 @@ impl Step for BuildManifest {
         cmd.arg(&builder.config.channel);
 
         builder.create_dir(&distdir(builder));
-        builder.run(&mut cmd);
+        builder.run(cmd);
     }
 }
 
@@ -72,7 +72,7 @@ impl Step for BumpStage0 {
     fn run(self, builder: &Builder<'_>) -> Self::Output {
         let mut cmd = builder.tool_cmd(Tool::BumpStage0);
         cmd.args(builder.config.args());
-        builder.run(&mut cmd);
+        builder.run(cmd);
     }
 }
 
@@ -94,7 +94,7 @@ impl Step for ReplaceVersionPlaceholder {
     fn run(self, builder: &Builder<'_>) -> Self::Output {
         let mut cmd = builder.tool_cmd(Tool::ReplaceVersionPlaceholder);
         cmd.arg(&builder.src);
-        builder.run(&mut cmd);
+        builder.run(cmd);
     }
 }
 
@@ -158,8 +158,7 @@ impl Step for Miri {
         // after another --, so this must be at the end.
         miri.args(builder.config.args());
 
-        let mut miri = Command::from(miri);
-        builder.run(&mut miri);
+        builder.run(miri);
     }
 }
 
@@ -189,7 +188,7 @@ impl Step for CollectLicenseMetadata {
         let mut cmd = builder.tool_cmd(Tool::CollectLicenseMetadata);
         cmd.env("REUSE_EXE", reuse);
         cmd.env("DEST", &dest);
-        builder.run(&mut cmd);
+        builder.run(cmd);
 
         dest
     }
@@ -219,7 +218,7 @@ impl Step for GenerateCopyright {
         let mut cmd = builder.tool_cmd(Tool::GenerateCopyright);
         cmd.env("LICENSE_METADATA", &license_metadata);
         cmd.env("DEST", &dest);
-        builder.run(&mut cmd);
+        builder.run(cmd);
 
         dest
     }
@@ -243,7 +242,7 @@ impl Step for GenerateWindowsSys {
     fn run(self, builder: &Builder<'_>) {
         let mut cmd = builder.tool_cmd(Tool::GenerateWindowsSys);
         cmd.arg(&builder.src);
-        builder.run(&mut cmd);
+        builder.run(cmd);
     }
 }
 
diff --git a/src/bootstrap/src/core/build_steps/suggest.rs b/src/bootstrap/src/core/build_steps/suggest.rs
index 754d1e61da8..7c5e0d4e13e 100644
--- a/src/bootstrap/src/core/build_steps/suggest.rs
+++ b/src/bootstrap/src/core/build_steps/suggest.rs
@@ -16,6 +16,7 @@ pub fn suggest(builder: &Builder<'_>, run: bool) {
         .tool_cmd(Tool::SuggestTests)
         .env("SUGGEST_TESTS_GIT_REPOSITORY", git_config.git_repository)
         .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch)
+        .command
         .output()
         .expect("failed to run `suggest-tests` tool");
 
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 1ef5af7cc2d..1460a229019 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -156,7 +156,7 @@ You can skip linkcheck with --skip src/tools/linkchecker"
         let _guard =
             builder.msg(Kind::Test, compiler.stage, "Linkcheck", bootstrap_host, bootstrap_host);
         let _time = helpers::timeit(builder);
-        builder.run_tracked(
+        builder.run(
             BootstrapCommand::from(linkchecker.arg(builder.out.join(host.triple).join("doc")))
                 .delay_failure(),
         );
@@ -216,7 +216,7 @@ impl Step for HtmlCheck {
             builder,
         ));
 
-        builder.run_tracked(
+        builder.run(
             BootstrapCommand::from(
                 builder.tool_cmd(Tool::HtmlChecker).arg(builder.doc_out(self.target)),
             )
@@ -267,7 +267,7 @@ impl Step for Cargotest {
             .env("RUSTC", builder.rustc(compiler))
             .env("RUSTDOC", builder.rustdoc(compiler));
         add_rustdoc_cargo_linker_args(cmd, builder, compiler.host, LldThreads::No);
-        builder.run_tracked(BootstrapCommand::from(cmd).delay_failure());
+        builder.run(BootstrapCommand::from(cmd).delay_failure());
     }
 }
 
@@ -465,7 +465,7 @@ impl Miri {
         // Tell it where to put the sysroot.
         cargo.env("MIRI_SYSROOT", &miri_sysroot);
 
-        let mut cargo = Command::from(cargo);
+        let mut cargo = BootstrapCommand::from(cargo);
         let _guard =
             builder.msg(Kind::Build, compiler.stage, "miri sysroot", compiler.host, target);
         builder.run(&mut cargo);
@@ -482,8 +482,10 @@ impl Miri {
             String::new()
         } else {
             builder.verbose(|| println!("running: {cargo:?}"));
-            let out =
-                cargo.output().expect("We already ran `cargo miri setup` before and that worked");
+            let out = cargo
+                .command
+                .output()
+                .expect("We already ran `cargo miri setup` before and that worked");
             assert!(out.status.success(), "`cargo miri setup` returned with non-0 exit code");
             // Output is "<sysroot>\n".
             let stdout = String::from_utf8(out.stdout)
@@ -596,7 +598,7 @@ impl Step for Miri {
                     target,
                 );
                 let _time = helpers::timeit(builder);
-                builder.run(&mut cargo);
+                builder.run(cargo);
             }
         }
     }
@@ -661,11 +663,11 @@ impl Step for CargoMiri {
 
         // Finally, pass test-args and run everything.
         cargo.arg("--").args(builder.config.test_args());
-        let mut cargo = Command::from(cargo);
+        let cargo = BootstrapCommand::from(cargo);
         {
             let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target);
             let _time = helpers::timeit(builder);
-            builder.run(&mut cargo);
+            builder.run(cargo);
         }
     }
 }
@@ -766,7 +768,7 @@ impl Step for Clippy {
         let _guard = builder.msg_sysroot_tool(Kind::Test, compiler.stage, "clippy", host, host);
 
         // Clippy reports errors if it blessed the outputs
-        if builder.run_cmd(BootstrapCommand::from(&mut cargo).allow_failure()) {
+        if builder.run(BootstrapCommand::from(&mut cargo).allow_failure()).is_success() {
             // The tests succeeded; nothing to do.
             return;
         }
@@ -819,7 +821,7 @@ impl Step for RustdocTheme {
             .env("RUSTC_BOOTSTRAP", "1");
         cmd.args(linker_args(builder, self.compiler.host, LldThreads::No));
 
-        builder.run_tracked(BootstrapCommand::from(&mut cmd).delay_failure());
+        builder.run(BootstrapCommand::from(&mut cmd).delay_failure());
     }
 }
 
@@ -845,7 +847,7 @@ impl Step for RustdocJSStd {
     fn run(self, builder: &Builder<'_>) {
         let nodejs =
             builder.config.nodejs.as_ref().expect("need nodejs to run rustdoc-js-std tests");
-        let mut command = Command::new(nodejs);
+        let mut command = BootstrapCommand::new(nodejs);
         command
             .arg(builder.src.join("src/tools/rustdoc-js/tester.js"))
             .arg("--crate-name")
@@ -879,7 +881,7 @@ impl Step for RustdocJSStd {
             builder.config.build,
             self.target,
         );
-        builder.run(&mut command);
+        builder.run(command);
     }
 }
 
@@ -1097,7 +1099,7 @@ HELP: to skip test's attempt to check tidiness, pass `--skip src/tools/tidy` to
         }
 
         builder.info("tidy check");
-        builder.run_tracked(BootstrapCommand::from(&mut cmd).delay_failure());
+        builder.run(BootstrapCommand::from(&mut cmd).delay_failure());
 
         builder.info("x.py completions check");
         let [bash, zsh, fish, powershell] = ["x.py.sh", "x.py.zsh", "x.py.fish", "x.py.ps1"]
@@ -1304,8 +1306,7 @@ impl Step for RunMakeSupport {
             &[],
         );
 
-        let mut cargo = Command::from(cargo);
-        builder.run(&mut cargo);
+        builder.run(cargo);
 
         let lib_name = "librun_make_support.rlib";
         let lib = builder.tools_dir(self.compiler).join(lib_name);
@@ -1816,23 +1817,25 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
             cmd.arg("--gdb").arg(gdb);
         }
 
-        let run = |cmd: &mut Command| {
-            cmd.output().map(|output| {
-                String::from_utf8_lossy(&output.stdout)
-                    .lines()
-                    .next()
-                    .unwrap_or_else(|| panic!("{:?} failed {:?}", cmd, output))
-                    .to_string()
-            })
-        };
-
         let lldb_exe = builder.config.lldb.clone().unwrap_or_else(|| PathBuf::from("lldb"));
         let lldb_version = Command::new(&lldb_exe)
             .arg("--version")
             .output()
-            .map(|output| String::from_utf8_lossy(&output.stdout).to_string())
-            .ok();
+            .map(|output| {
+                (String::from_utf8_lossy(&output.stdout).to_string(), output.status.success())
+            })
+            .ok()
+            .and_then(|(output, success)| if success { Some(output) } else { None });
         if let Some(ref vers) = lldb_version {
+            let run = |cmd: &mut Command| {
+                cmd.output().map(|output| {
+                    String::from_utf8_lossy(&output.stdout)
+                        .lines()
+                        .next()
+                        .unwrap_or_else(|| panic!("{:?} failed {:?}", cmd, output))
+                        .to_string()
+                })
+            };
             cmd.arg("--lldb-version").arg(vers);
             let lldb_python_dir = run(Command::new(&lldb_exe).arg("-P")).ok();
             if let Some(ref dir) = lldb_python_dir {
@@ -2066,7 +2069,8 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         cmd.arg("--git-repository").arg(git_config.git_repository);
         cmd.arg("--nightly-branch").arg(git_config.nightly_branch);
 
-        builder.ci_env.force_coloring_in_ci(&mut cmd);
+        // FIXME: Move CiEnv back to bootstrap, it is only used here anyway
+        builder.ci_env.force_coloring_in_ci(&mut cmd.command);
 
         #[cfg(feature = "build-metrics")]
         builder.metrics.begin_test_suite(
@@ -2184,11 +2188,8 @@ impl BookTest {
         );
         let _time = helpers::timeit(builder);
         let cmd = BootstrapCommand::from(&mut rustbook_cmd).delay_failure();
-        let toolstate = if builder.run_tracked(cmd).is_success() {
-            ToolState::TestPass
-        } else {
-            ToolState::TestFail
-        };
+        let toolstate =
+            if builder.run(cmd).is_success() { ToolState::TestPass } else { ToolState::TestFail };
         builder.save_toolstate(self.name, toolstate);
     }
 
@@ -2317,8 +2318,7 @@ impl Step for ErrorIndex {
         let guard =
             builder.msg(Kind::Test, compiler.stage, "error-index", compiler.host, compiler.host);
         let _time = helpers::timeit(builder);
-        builder
-            .run_tracked(BootstrapCommand::from(&mut tool).output_mode(OutputMode::OnlyOnFailure));
+        builder.run(BootstrapCommand::from(&mut tool).output_mode(OutputMode::OnlyOnFailure));
         drop(guard);
         // The tests themselves need to link to std, so make sure it is
         // available.
@@ -2347,11 +2347,11 @@ fn markdown_test(builder: &Builder<'_>, compiler: Compiler, markdown: &Path) ->
     let test_args = builder.config.test_args().join(" ");
     cmd.arg("--test-args").arg(test_args);
 
-    let mut cmd = BootstrapCommand::from(&mut cmd).delay_failure();
+    cmd = cmd.delay_failure();
     if !builder.config.verbose_tests {
         cmd = cmd.quiet();
     }
-    builder.run_tracked(cmd).is_success()
+    builder.run(cmd).is_success()
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -2377,11 +2377,8 @@ impl Step for RustcGuide {
         let src = builder.src.join(relative_path);
         let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
         let cmd = BootstrapCommand::from(rustbook_cmd.arg("linkcheck").arg(&src)).delay_failure();
-        let toolstate = if builder.run_tracked(cmd).is_success() {
-            ToolState::TestPass
-        } else {
-            ToolState::TestFail
-        };
+        let toolstate =
+            if builder.run(cmd).is_success() { ToolState::TestPass } else { ToolState::TestFail };
         builder.save_toolstate("rustc-dev-guide", toolstate);
     }
 }
@@ -2432,7 +2429,7 @@ impl Step for CrateLibrustc {
 /// Returns whether the test succeeded.
 #[allow(clippy::too_many_arguments)] // FIXME: reduce the number of args and remove this.
 fn run_cargo_test<'a>(
-    cargo: impl Into<Command>,
+    cargo: impl Into<BootstrapCommand>,
     libtest_args: &[&str],
     crates: &[String],
     primary_crate: &str,
@@ -2463,14 +2460,14 @@ fn run_cargo_test<'a>(
 
 /// Given a `cargo test` subcommand, pass it the appropriate test flags given a `builder`.
 fn prepare_cargo_test(
-    cargo: impl Into<Command>,
+    cargo: impl Into<BootstrapCommand>,
     libtest_args: &[&str],
     crates: &[String],
     primary_crate: &str,
     compiler: Compiler,
     target: TargetSelection,
     builder: &Builder<'_>,
-) -> Command {
+) -> BootstrapCommand {
     let mut cargo = cargo.into();
 
     // Propegate `--bless` if it has not already been set/unset
@@ -2881,19 +2878,19 @@ impl Step for RemoteCopyLibs {
 
         // Spawn the emulator and wait for it to come online
         let tool = builder.tool_exe(Tool::RemoteTestClient);
-        let mut cmd = Command::new(&tool);
+        let mut cmd = BootstrapCommand::new(&tool);
         cmd.arg("spawn-emulator").arg(target.triple).arg(&server).arg(builder.tempdir());
         if let Some(rootfs) = builder.qemu_rootfs(target) {
             cmd.arg(rootfs);
         }
-        builder.run(&mut cmd);
+        builder.run(cmd);
 
         // Push all our dylibs to the emulator
         for f in t!(builder.sysroot_libdir(compiler, target).read_dir()) {
             let f = t!(f);
             let name = f.file_name().into_string().unwrap();
             if helpers::is_dylib(&name) {
-                builder.run(Command::new(&tool).arg("push").arg(f.path()));
+                builder.run(BootstrapCommand::new(&tool).arg("push").arg(f.path()));
             }
         }
     }
@@ -2924,20 +2921,20 @@ impl Step for Distcheck {
         builder.ensure(dist::PlainSourceTarball);
         builder.ensure(dist::Src);
 
-        let mut cmd = Command::new("tar");
+        let mut cmd = BootstrapCommand::new("tar");
         cmd.arg("-xf")
             .arg(builder.ensure(dist::PlainSourceTarball).tarball())
             .arg("--strip-components=1")
             .current_dir(&dir);
-        builder.run(&mut cmd);
+        builder.run(cmd);
         builder.run(
-            Command::new("./configure")
+            BootstrapCommand::new("./configure")
                 .args(&builder.config.configure_args)
                 .arg("--enable-vendor")
                 .current_dir(&dir),
         );
         builder.run(
-            Command::new(helpers::make(&builder.config.build.triple))
+            BootstrapCommand::new(helpers::make(&builder.config.build.triple))
                 .arg("check")
                 .current_dir(&dir),
         );
@@ -2948,16 +2945,16 @@ impl Step for Distcheck {
         let _ = fs::remove_dir_all(&dir);
         t!(fs::create_dir_all(&dir));
 
-        let mut cmd = Command::new("tar");
+        let mut cmd = BootstrapCommand::new("tar");
         cmd.arg("-xf")
             .arg(builder.ensure(dist::Src).tarball())
             .arg("--strip-components=1")
             .current_dir(&dir);
-        builder.run(&mut cmd);
+        builder.run(cmd);
 
         let toml = dir.join("rust-src/lib/rustlib/src/rust/library/std/Cargo.toml");
         builder.run(
-            Command::new(&builder.initial_cargo)
+            BootstrapCommand::new(&builder.initial_cargo)
                 // Will read the libstd Cargo.toml
                 // which uses the unstable `public-dependency` feature.
                 .env("RUSTC_BOOTSTRAP", "1")
@@ -2986,7 +2983,7 @@ impl Step for Bootstrap {
         // Some tests require cargo submodule to be present.
         builder.build.update_submodule(Path::new("src/tools/cargo"));
 
-        let mut check_bootstrap = Command::new(builder.python());
+        let mut check_bootstrap = BootstrapCommand::new(builder.python());
         check_bootstrap
             .args(["-m", "unittest", "bootstrap_test.py"])
             .env("BUILD_DIR", &builder.out)
@@ -2994,9 +2991,9 @@ impl Step for Bootstrap {
             .current_dir(builder.src.join("src/bootstrap/"));
         // NOTE: we intentionally don't pass test_args here because the args for unittest and cargo test are mutually incompatible.
         // Use `python -m unittest` manually if you want to pass arguments.
-        builder.run_tracked(BootstrapCommand::from(&mut check_bootstrap).delay_failure());
+        builder.run(check_bootstrap.delay_failure());
 
-        let mut cmd = Command::new(&builder.initial_cargo);
+        let mut cmd = BootstrapCommand::new(&builder.initial_cargo);
         cmd.arg("test")
             .args(["--features", "bootstrap-self-test"])
             .current_dir(builder.src.join("src/bootstrap"))
@@ -3071,7 +3068,7 @@ impl Step for TierCheck {
             self.compiler.host,
             self.compiler.host,
         );
-        builder.run_tracked(BootstrapCommand::from(&mut cargo.into()).delay_failure());
+        builder.run(BootstrapCommand::from(cargo).delay_failure());
     }
 }
 
@@ -3147,8 +3144,7 @@ impl Step for RustInstaller {
             return;
         }
 
-        let mut cmd =
-            std::process::Command::new(builder.src.join("src/tools/rust-installer/test.sh"));
+        let mut cmd = BootstrapCommand::new(builder.src.join("src/tools/rust-installer/test.sh"));
         let tmpdir = testdir(builder, compiler.host).join("rust-installer");
         let _ = std::fs::remove_dir_all(&tmpdir);
         let _ = std::fs::create_dir_all(&tmpdir);
@@ -3157,7 +3153,7 @@ impl Step for RustInstaller {
         cmd.env("CARGO", &builder.initial_cargo);
         cmd.env("RUSTC", &builder.initial_rustc);
         cmd.env("TMP_DIR", &tmpdir);
-        builder.run_tracked(BootstrapCommand::from(&mut cmd).delay_failure());
+        builder.run(cmd.delay_failure());
     }
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
@@ -3351,8 +3347,7 @@ impl Step for CodegenCranelift {
             .arg("testsuite.extended_sysroot");
         cargo.args(builder.config.test_args());
 
-        let mut cmd: Command = cargo.into();
-        builder.run_cmd(BootstrapCommand::from(&mut cmd).fail_fast());
+        builder.run(cargo);
     }
 }
 
@@ -3477,7 +3472,6 @@ impl Step for CodegenGCC {
             .arg("--std-tests");
         cargo.args(builder.config.test_args());
 
-        let mut cmd: Command = cargo.into();
-        builder.run_cmd(BootstrapCommand::from(&mut cmd).fail_fast());
+        builder.run(cargo);
     }
 }
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index a95a7f5491f..b3464043912 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -9,6 +9,7 @@ use crate::core::builder;
 use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step};
 use crate::core::config::TargetSelection;
 use crate::utils::channel::GitInfo;
+use crate::utils::exec::BootstrapCommand;
 use crate::utils::helpers::output;
 use crate::utils::helpers::{add_dylib_path, exe, t};
 use crate::Compiler;
@@ -336,6 +337,7 @@ bootstrap_tool!(
     GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
     RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = "test";
     CoverageDump, "src/tools/coverage-dump", "coverage-dump";
+    RustcPerfWrapper, "src/tools/rustc-perf-wrapper", "rustc-perf-wrapper";
 );
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@@ -432,12 +434,12 @@ pub struct ErrorIndex {
 }
 
 impl ErrorIndex {
-    pub fn command(builder: &Builder<'_>) -> Command {
+    pub fn command(builder: &Builder<'_>) -> BootstrapCommand {
         // Error-index-generator links with the rustdoc library, so we need to add `rustc_lib_paths`
         // for rustc_private and libLLVM.so, and `sysroot_lib` for libstd, etc.
         let host = builder.config.build;
         let compiler = builder.compiler_for(builder.top_stage, host, host);
-        let mut cmd = Command::new(builder.ensure(ErrorIndex { compiler }));
+        let mut cmd = BootstrapCommand::new(builder.ensure(ErrorIndex { compiler }));
         let mut dylib_paths = builder.rustc_lib_paths(compiler);
         dylib_paths.push(PathBuf::from(&builder.sysroot_libdir(compiler, compiler.host)));
         add_dylib_path(dylib_paths, &mut cmd);
@@ -601,7 +603,7 @@ impl Step for Rustdoc {
             &self.compiler.host,
             &target,
         );
-        builder.run(&mut cargo.into());
+        builder.run(cargo);
 
         // Cargo adds a number of paths to the dylib search path on windows, which results in
         // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
@@ -856,7 +858,7 @@ impl Step for LlvmBitcodeLinker {
             &self.extra_features,
         );
 
-        builder.run(&mut cargo.into());
+        builder.run(cargo);
 
         let tool_out = builder
             .cargo_out(self.compiler, Mode::ToolRustc, self.target)
@@ -911,13 +913,13 @@ impl Step for LibcxxVersionTool {
             }
 
             let compiler = builder.cxx(self.target).unwrap();
-            let mut cmd = Command::new(compiler);
+            let mut cmd = BootstrapCommand::new(compiler);
 
             cmd.arg("-o")
                 .arg(&executable)
                 .arg(builder.src.join("src/tools/libcxx-version/main.cpp"));
 
-            builder.run_cmd(&mut cmd);
+            builder.run(cmd);
 
             if !executable.exists() {
                 panic!("Something went wrong. {} is not present", executable.display());
@@ -1045,10 +1047,10 @@ tool_extended!((self, builder),
 );
 
 impl<'a> Builder<'a> {
-    /// Gets a `Command` which is ready to run `tool` in `stage` built for
+    /// Gets a `BootstrapCommand` which is ready to run `tool` in `stage` built for
     /// `host`.
-    pub fn tool_cmd(&self, tool: Tool) -> Command {
-        let mut cmd = Command::new(self.tool_exe(tool));
+    pub fn tool_cmd(&self, tool: Tool) -> BootstrapCommand {
+        let mut cmd = BootstrapCommand::new(self.tool_exe(tool));
         let compiler = self.compiler(0, self.config.build);
         let host = &compiler.host;
         // Prepares the `cmd` provided to be able to run the `compiler` provided.
diff --git a/src/bootstrap/src/core/build_steps/vendor.rs b/src/bootstrap/src/core/build_steps/vendor.rs
index e92ab57619b..0b999a24a1f 100644
--- a/src/bootstrap/src/core/build_steps/vendor.rs
+++ b/src/bootstrap/src/core/build_steps/vendor.rs
@@ -1,6 +1,6 @@
 use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
+use crate::utils::exec::BootstrapCommand;
 use std::path::{Path, PathBuf};
-use std::process::Command;
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub(crate) struct Vendor {
@@ -27,7 +27,7 @@ impl Step for Vendor {
     }
 
     fn run(self, builder: &Builder<'_>) -> Self::Output {
-        let mut cmd = Command::new(&builder.initial_cargo);
+        let mut cmd = BootstrapCommand::new(&builder.initial_cargo);
         cmd.arg("vendor");
 
         if self.versioned_dirs {
@@ -59,6 +59,6 @@ impl Step for Vendor {
 
         cmd.current_dir(self.root_dir);
 
-        builder.run(&mut cmd);
+        builder.run(cmd);
     }
 }
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 14ccbfe0267..58d6e7a58e3 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -24,6 +24,7 @@ use crate::utils::helpers::{check_cfg_arg, libdir, linker_flags, output, t, LldT
 use crate::EXTRA_CHECK_CFGS;
 use crate::{Build, CLang, Crate, DocTests, GitRepo, Mode};
 
+use crate::utils::exec::BootstrapCommand;
 pub use crate::Compiler;
 
 use clap::ValueEnum;
@@ -1217,7 +1218,7 @@ impl<'a> Builder<'a> {
 
     /// Adds the compiler's directory of dynamic libraries to `cmd`'s dynamic
     /// library lookup path.
-    pub fn add_rustc_lib_path(&self, compiler: Compiler, cmd: &mut Command) {
+    pub fn add_rustc_lib_path(&self, compiler: Compiler, cmd: &mut BootstrapCommand) {
         // Windows doesn't need dylib path munging because the dlls for the
         // compiler live next to the compiler and the system will find them
         // automatically.
@@ -1250,11 +1251,11 @@ impl<'a> Builder<'a> {
         self.ensure(tool::Rustdoc { compiler })
     }
 
-    pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> Command {
+    pub fn cargo_clippy_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
         if run_compiler.stage == 0 {
             // `ensure(Clippy { stage: 0 })` *builds* clippy with stage0, it doesn't use the beta clippy.
             let cargo_clippy = self.build.config.download_clippy();
-            let mut cmd = Command::new(cargo_clippy);
+            let mut cmd = BootstrapCommand::new(cargo_clippy);
             cmd.env("CARGO", &self.initial_cargo);
             return cmd;
         }
@@ -1273,13 +1274,13 @@ impl<'a> Builder<'a> {
         let mut dylib_path = helpers::dylib_path();
         dylib_path.insert(0, self.sysroot(run_compiler).join("lib"));
 
-        let mut cmd = Command::new(cargo_clippy);
+        let mut cmd = BootstrapCommand::new(cargo_clippy);
         cmd.env(helpers::dylib_path_var(), env::join_paths(&dylib_path).unwrap());
         cmd.env("CARGO", &self.initial_cargo);
         cmd
     }
 
-    pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> Command {
+    pub fn cargo_miri_cmd(&self, run_compiler: Compiler) -> BootstrapCommand {
         assert!(run_compiler.stage > 0, "miri can not be invoked at stage 0");
         let build_compiler = self.compiler(run_compiler.stage - 1, self.build.build);
 
@@ -1295,7 +1296,7 @@ impl<'a> Builder<'a> {
             extra_features: Vec::new(),
         });
         // Invoke cargo-miri, make sure it can find miri and cargo.
-        let mut cmd = Command::new(cargo_miri);
+        let mut cmd = BootstrapCommand::new(cargo_miri);
         cmd.env("MIRI", &miri);
         cmd.env("CARGO", &self.initial_cargo);
         // Need to add the `run_compiler` libs. Those are the libs produces *by* `build_compiler`,
@@ -1310,8 +1311,8 @@ impl<'a> Builder<'a> {
         cmd
     }
 
-    pub fn rustdoc_cmd(&self, compiler: Compiler) -> Command {
-        let mut cmd = Command::new(self.bootstrap_out.join("rustdoc"));
+    pub fn rustdoc_cmd(&self, compiler: Compiler) -> BootstrapCommand {
+        let mut cmd = BootstrapCommand::new(self.bootstrap_out.join("rustdoc"));
         cmd.env("RUSTC_STAGE", compiler.stage.to_string())
             .env("RUSTC_SYSROOT", self.sysroot(compiler))
             // Note that this is *not* the sysroot_libdir because rustdoc must be linked
@@ -1352,7 +1353,7 @@ impl<'a> Builder<'a> {
         mode: Mode,
         target: TargetSelection,
         cmd: &str, // FIXME make this properly typed
-    ) -> Command {
+    ) -> BootstrapCommand {
         let mut cargo;
         if cmd == "clippy" {
             cargo = self.cargo_clippy_cmd(compiler);
@@ -1365,7 +1366,7 @@ impl<'a> Builder<'a> {
             cargo = self.cargo_miri_cmd(compiler);
             cargo.arg("miri").arg(subcmd);
         } else {
-            cargo = Command::new(&self.initial_cargo);
+            cargo = BootstrapCommand::new(&self.initial_cargo);
             cargo.arg(cmd);
         }
 
@@ -2104,7 +2105,7 @@ impl<'a> Builder<'a> {
         // Try to use a sysroot-relative bindir, in case it was configured absolutely.
         cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative());
 
-        self.ci_env.force_coloring_in_ci(&mut cargo);
+        self.ci_env.force_coloring_in_ci(&mut cargo.command);
 
         // When we build Rust dylibs they're all intended for intermediate
         // usage, so make sure we pass the -Cprefer-dynamic flag instead of
@@ -2373,7 +2374,7 @@ impl HostFlags {
 
 #[derive(Debug)]
 pub struct Cargo {
-    command: Command,
+    command: BootstrapCommand,
     compiler: Compiler,
     target: TargetSelection,
     rustflags: Rustflags,
@@ -2598,8 +2599,8 @@ impl Cargo {
     }
 }
 
-impl From<Cargo> for Command {
-    fn from(mut cargo: Cargo) -> Command {
+impl From<Cargo> for BootstrapCommand {
+    fn from(mut cargo: Cargo) -> BootstrapCommand {
         let rustflags = &cargo.rustflags.0;
         if !rustflags.is_empty() {
             cargo.command.env("RUSTFLAGS", rustflags);
@@ -2618,7 +2619,12 @@ impl From<Cargo> for Command {
         if !cargo.allow_features.is_empty() {
             cargo.command.env("RUSTC_ALLOW_FEATURES", cargo.allow_features);
         }
-
         cargo.command
     }
 }
+
+impl From<Cargo> for Command {
+    fn from(cargo: Cargo) -> Command {
+        BootstrapCommand::from(cargo).command
+    }
+}
diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs
index eb5152a3831..aeb608a9ea2 100644
--- a/src/bootstrap/src/core/config/flags.rs
+++ b/src/bootstrap/src/core/config/flags.rs
@@ -470,7 +470,9 @@ Arguments:
         versioned_dirs: bool,
     },
     /// Perform profiling and benchmarking of the compiler using the
-    /// `rustc-perf` benchmark suite.
+    /// `rustc-perf-wrapper` tool.
+    ///
+    /// You need to pass arguments after `--`, e.g.`x perf -- cachegrind`.
     Perf {},
 }
 
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index fd077ab2d7c..c35398e2eb7 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -11,6 +11,7 @@ use std::{
 use build_helper::ci::CiEnv;
 use xz2::bufread::XzDecoder;
 
+use crate::utils::exec::BootstrapCommand;
 use crate::utils::helpers::hex_encode;
 use crate::utils::helpers::{check_run, exe, move_file, program_out_of_date};
 use crate::{t, Config};
@@ -56,7 +57,7 @@ impl Config {
     /// Runs a command, printing out nice contextual information if it fails.
     /// Returns false if do not execute at all, otherwise returns its
     /// `status.success()`.
-    pub(crate) fn check_run(&self, cmd: &mut Command) -> bool {
+    pub(crate) fn check_run(&self, cmd: &mut BootstrapCommand) -> bool {
         if self.dry_run() {
             return true;
         }
@@ -211,7 +212,7 @@ impl Config {
     fn download_http_with_retries(&self, tempfile: &Path, url: &str, help_on_error: &str) {
         println!("downloading {url}");
         // Try curl. If that fails and we are on windows, fallback to PowerShell.
-        let mut curl = Command::new("curl");
+        let mut curl = BootstrapCommand::new("curl");
         curl.args([
             "-y",
             "30",
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index afba907ee92..c12449fdc4a 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -26,6 +26,7 @@ use std::path::{Path, PathBuf};
 use std::process::{Command, Stdio};
 use std::str;
 use std::sync::OnceLock;
+use std::time::SystemTime;
 
 use build_helper::ci::{gha, CiEnv};
 use build_helper::exit;
@@ -74,7 +75,7 @@ const LLVM_TOOLS: &[&str] = &[
 /// LLD file names for all flavors.
 const LLD_FILE_NAMES: &[&str] = &["ld.lld", "ld64.lld", "lld-link", "wasm-ld"];
 
-/// Extra --check-cfg to add when building
+/// Extra `--check-cfg` to add when building the compiler or tools
 /// (Mode restriction, config name, config values (if any))
 #[allow(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]>)] = &[
@@ -84,38 +85,9 @@ const EXTRA_CHECK_CFGS: &[(Option<Mode>, &str, Option<&[&'static str]>)] = &[
     (Some(Mode::ToolRustc), "rust_analyzer", None),
     (Some(Mode::ToolStd), "rust_analyzer", None),
     (Some(Mode::Codegen), "parallel_compiler", None),
-    // NOTE: consider updating `check-cfg` entries in `std/Cargo.toml` too.
-    // cfg(bootstrap) remove these once the bootstrap compiler supports
-    // `lints.rust.unexpected_cfgs.check-cfg`
-    (Some(Mode::Std), "stdarch_intel_sde", None),
-    (Some(Mode::Std), "no_fp_fmt_parse", None),
-    (Some(Mode::Std), "no_global_oom_handling", None),
-    (Some(Mode::Std), "no_rc", None),
-    (Some(Mode::Std), "no_sync", None),
-    /* Extra values not defined in the built-in targets yet, but used in std */
-    (Some(Mode::Std), "target_env", Some(&["libnx", "p2"])),
-    (Some(Mode::Std), "target_os", Some(&["visionos"])),
-    (Some(Mode::Std), "target_arch", Some(&["arm64ec", "spirv", "nvptx", "xtensa"])),
-    (Some(Mode::ToolStd), "target_os", Some(&["visionos"])),
-    /* Extra names used by dependencies */
-    // FIXME: Used by serde_json, but we should not be triggering on external dependencies.
-    (Some(Mode::Rustc), "no_btreemap_remove_entry", None),
-    (Some(Mode::ToolRustc), "no_btreemap_remove_entry", None),
-    // FIXME: Used by crossbeam-utils, but we should not be triggering on external dependencies.
-    (Some(Mode::Rustc), "crossbeam_loom", None),
-    (Some(Mode::ToolRustc), "crossbeam_loom", None),
-    // FIXME: Used by proc-macro2, but we should not be triggering on external dependencies.
-    (Some(Mode::Rustc), "span_locations", None),
-    (Some(Mode::ToolRustc), "span_locations", None),
-    // FIXME: Used by rustix, but we should not be triggering on external dependencies.
-    (Some(Mode::Rustc), "rustix_use_libc", None),
-    (Some(Mode::ToolRustc), "rustix_use_libc", None),
-    // FIXME: Used by filetime, but we should not be triggering on external dependencies.
-    (Some(Mode::Rustc), "emulate_second_only_system", None),
-    (Some(Mode::ToolRustc), "emulate_second_only_system", None),
-    // Needed to avoid the need to copy windows.lib into the sysroot.
-    (Some(Mode::Rustc), "windows_raw_dylib", None),
-    (Some(Mode::ToolRustc), "windows_raw_dylib", None),
+    // Any library specific cfgs like `target_os`, `target_arch` should be put in
+    // priority the `[lints.rust.unexpected_cfgs.check-cfg]` table
+    // in the appropriate `library/{std,alloc,core}/Cargo.toml`
 ];
 
 /// A structure representing a Rust compiler.
@@ -575,19 +547,17 @@ impl Build {
         };
         // NOTE: doesn't use `try_run` because this shouldn't print an error if it fails.
         if !update(true).status().map_or(false, |status| status.success()) {
-            self.run(&mut update(false));
+            self.run(update(false));
         }
 
         // Save any local changes, but avoid running `git stash pop` if there are none (since it will exit with an error).
         // diff-index reports the modifications through the exit status
-        let has_local_modifications = !self.run_cmd(
-            BootstrapCommand::from(submodule_git().args(["diff-index", "--quiet", "HEAD"]))
-                .allow_failure()
-                .output_mode(match self.is_verbose() {
-                    true => OutputMode::All,
-                    false => OutputMode::OnlyOutput,
-                }),
-        );
+        let has_local_modifications = self
+            .run(
+                BootstrapCommand::from(submodule_git().args(["diff-index", "--quiet", "HEAD"]))
+                    .allow_failure(),
+            )
+            .is_failure();
         if has_local_modifications {
             self.run(submodule_git().args(["stash", "push"]));
         }
@@ -939,7 +909,7 @@ impl Build {
     }
 
     /// Adds the `RUST_TEST_THREADS` env var if necessary
-    fn add_rust_test_threads(&self, cmd: &mut Command) {
+    fn add_rust_test_threads(&self, cmd: &mut BootstrapCommand) {
         if env::var_os("RUST_TEST_THREADS").is_none() {
             cmd.env("RUST_TEST_THREADS", self.jobs().to_string());
         }
@@ -961,11 +931,14 @@ impl Build {
     }
 
     /// Execute a command and return its output.
-    fn run_tracked(&self, command: BootstrapCommand<'_>) -> CommandOutput {
+    /// This method should be used for all command executions in bootstrap.
+    fn run<C: Into<BootstrapCommand>>(&self, command: C) -> CommandOutput {
         if self.config.dry_run() {
             return CommandOutput::default();
         }
 
+        let mut command = command.into();
+
         self.verbose(|| println!("running: {command:?}"));
 
         let output_mode = command.output_mode.unwrap_or_else(|| match self.is_verbose() {
@@ -1024,22 +997,6 @@ impl Build {
         output
     }
 
-    /// Runs a command, printing out nice contextual information if it fails.
-    fn run(&self, cmd: &mut Command) {
-        self.run_cmd(BootstrapCommand::from(cmd).fail_fast().output_mode(
-            match self.is_verbose() {
-                true => OutputMode::All,
-                false => OutputMode::OnlyOutput,
-            },
-        ));
-    }
-
-    /// A centralized function for running commands that do not return output.
-    pub(crate) fn run_cmd<'a, C: Into<BootstrapCommand<'a>>>(&self, cmd: C) -> bool {
-        let command = cmd.into();
-        self.run_tracked(command).is_success()
-    }
-
     /// Check if verbosity is greater than the `level`
     pub fn is_verbose_than(&self, level: usize) -> bool {
         self.verbosity > level
@@ -1691,7 +1648,14 @@ impl Build {
         if src == dst {
             return;
         }
-        let _ = fs::remove_file(dst);
+        if let Err(e) = fs::remove_file(dst) {
+            if cfg!(windows) && e.kind() != io::ErrorKind::NotFound {
+                // workaround for https://github.com/rust-lang/rust/issues/127126
+                // if removing the file fails, attempt to rename it instead.
+                let now = t!(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH));
+                let _ = fs::rename(dst, format!("{}-{}", dst.display(), now.as_nanos()));
+            }
+        }
         let metadata = t!(src.symlink_metadata(), format!("src = {}", src.display()));
         let mut src = src.to_path_buf();
         if metadata.file_type().is_symlink() {
diff --git a/src/bootstrap/src/utils/bin_helpers.rs b/src/bootstrap/src/utils/bin_helpers.rs
deleted file mode 100644
index 5fbbe0bde0e..00000000000
--- a/src/bootstrap/src/utils/bin_helpers.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-//! This file is meant to be included directly from bootstrap shims to avoid a
-//! dependency on the bootstrap library. This reduces the binary size and
-//! improves compilation time by reducing the linking time.
-
-use std::env;
-use std::fs::OpenOptions;
-use std::io::Write;
-use std::process::Command;
-use std::str::FromStr;
-
-/// Parses the value of the "RUSTC_VERBOSE" environment variable and returns it as a `usize`.
-/// If it was not defined, returns 0 by default.
-///
-/// Panics if "RUSTC_VERBOSE" is defined with the value that is not an unsigned integer.
-pub(crate) fn parse_rustc_verbose() -> usize {
-    match env::var("RUSTC_VERBOSE") {
-        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
-        Err(_) => 0,
-    }
-}
-
-/// Parses the value of the "RUSTC_STAGE" environment variable and returns it as a `String`.
-///
-/// If "RUSTC_STAGE" was not set, the program will be terminated with 101.
-pub(crate) fn parse_rustc_stage() -> String {
-    env::var("RUSTC_STAGE").unwrap_or_else(|_| {
-        // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
-        eprintln!("rustc shim: FATAL: RUSTC_STAGE was not set");
-        eprintln!("rustc shim: NOTE: use `x.py build -vvv` to see all environment variables set by bootstrap");
-        std::process::exit(101);
-    })
-}
-
-/// Writes the command invocation to a file if `DUMP_BOOTSTRAP_SHIMS` is set during bootstrap.
-///
-/// Before writing it, replaces user-specific values to create generic dumps for cross-environment
-/// comparisons.
-pub(crate) fn maybe_dump(dump_name: String, cmd: &Command) {
-    if let Ok(dump_dir) = env::var("DUMP_BOOTSTRAP_SHIMS") {
-        let dump_file = format!("{dump_dir}/{dump_name}");
-
-        let mut file = OpenOptions::new().create(true).append(true).open(dump_file).unwrap();
-
-        let cmd_dump = format!("{:?}\n", cmd);
-        let cmd_dump = cmd_dump.replace(&env::var("BUILD_OUT").unwrap(), "${BUILD_OUT}");
-        let cmd_dump = cmd_dump.replace(&env::var("CARGO_HOME").unwrap(), "${CARGO_HOME}");
-
-        file.write_all(cmd_dump.as_bytes()).expect("Unable to write file");
-    }
-}
diff --git a/src/bootstrap/src/utils/dylib.rs b/src/bootstrap/src/utils/dylib.rs
deleted file mode 100644
index 90bcff59a64..00000000000
--- a/src/bootstrap/src/utils/dylib.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-//! Various utilities for working with dylib paths.
-
-/// Returns the environment variable which the dynamic library lookup path
-/// resides in for this platform.
-pub fn dylib_path_var() -> &'static str {
-    if cfg!(target_os = "windows") {
-        "PATH"
-    } else if cfg!(target_vendor = "apple") {
-        "DYLD_LIBRARY_PATH"
-    } else if cfg!(target_os = "haiku") {
-        "LIBRARY_PATH"
-    } else if cfg!(target_os = "aix") {
-        "LIBPATH"
-    } else {
-        "LD_LIBRARY_PATH"
-    }
-}
-
-/// Parses the `dylib_path_var()` environment variable, returning a list of
-/// paths that are members of this lookup path.
-pub fn dylib_path() -> Vec<std::path::PathBuf> {
-    let var = match std::env::var_os(dylib_path_var()) {
-        Some(v) => v,
-        None => return vec![],
-    };
-    std::env::split_paths(&var).collect()
-}
-
-/// Given an executable called `name`, return the filename for the
-/// executable for a particular target.
-#[allow(dead_code)]
-pub fn exe(name: &str, target: &str) -> String {
-    if target.contains("windows") {
-        format!("{name}.exe")
-    } else if target.contains("uefi") {
-        format!("{name}.efi")
-    } else {
-        name.to_string()
-    }
-}
diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs
index e8c588b75b3..8bcb2301f1a 100644
--- a/src/bootstrap/src/utils/exec.rs
+++ b/src/bootstrap/src/utils/exec.rs
@@ -1,4 +1,6 @@
-use std::process::{Command, ExitStatus, Output};
+use std::ffi::OsStr;
+use std::path::Path;
+use std::process::{Command, CommandArgs, CommandEnvs, ExitStatus, Output};
 
 /// What should be done when the command fails.
 #[derive(Debug, Copy, Clone)]
@@ -24,14 +26,71 @@ pub enum OutputMode {
 }
 
 /// Wrapper around `std::process::Command`.
+///
+/// By default, the command will exit bootstrap if it fails.
+/// If you want to allow failures, use [allow_failure].
+/// If you want to delay failures until the end of bootstrap, use [delay_failure].
+///
+/// By default, the command will print its stdout/stderr to stdout/stderr of bootstrap
+/// ([OutputMode::OnlyOutput]). If bootstrap uses verbose mode, then it will also print the
+/// command itself in case of failure ([OutputMode::All]).
+/// If you want to handle the output programmatically, use `output_mode(OutputMode::OnlyOnFailure)`.
+///
+/// [allow_failure]: BootstrapCommand::allow_failure
+/// [delay_failure]: BootstrapCommand::delay_failure
 #[derive(Debug)]
-pub struct BootstrapCommand<'a> {
-    pub command: &'a mut Command,
+pub struct BootstrapCommand {
+    pub command: Command,
     pub failure_behavior: BehaviorOnFailure,
     pub output_mode: Option<OutputMode>,
 }
 
-impl<'a> BootstrapCommand<'a> {
+impl BootstrapCommand {
+    pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
+        Command::new(program).into()
+    }
+
+    pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
+        self.command.arg(arg.as_ref());
+        self
+    }
+
+    pub fn args<I, S>(&mut self, args: I) -> &mut Self
+    where
+        I: IntoIterator<Item = S>,
+        S: AsRef<OsStr>,
+    {
+        self.command.args(args);
+        self
+    }
+
+    pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
+    where
+        K: AsRef<OsStr>,
+        V: AsRef<OsStr>,
+    {
+        self.command.env(key, val);
+        self
+    }
+
+    pub fn get_envs(&self) -> CommandEnvs<'_> {
+        self.command.get_envs()
+    }
+
+    pub fn get_args(&self) -> CommandArgs<'_> {
+        self.command.get_args()
+    }
+
+    pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Self {
+        self.command.env_remove(key);
+        self
+    }
+
+    pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
+        self.command.current_dir(dir);
+        self
+    }
+
     pub fn delay_failure(self) -> Self {
         Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self }
     }
@@ -54,8 +113,41 @@ impl<'a> BootstrapCommand<'a> {
     }
 }
 
-impl<'a> From<&'a mut Command> for BootstrapCommand<'a> {
+/// This implementation is temporary, until all `Command` invocations are migrated to
+/// `BootstrapCommand`.
+impl<'a> From<&'a mut Command> for BootstrapCommand {
     fn from(command: &'a mut Command) -> Self {
+        // This is essentially a manual `Command::clone`
+        let mut cmd = Command::new(command.get_program());
+        if let Some(dir) = command.get_current_dir() {
+            cmd.current_dir(dir);
+        }
+        cmd.args(command.get_args());
+        for (key, value) in command.get_envs() {
+            match value {
+                Some(value) => {
+                    cmd.env(key, value);
+                }
+                None => {
+                    cmd.env_remove(key);
+                }
+            }
+        }
+
+        cmd.into()
+    }
+}
+
+/// This implementation is temporary, until all `Command` invocations are migrated to
+/// `BootstrapCommand`.
+impl<'a> From<&'a mut BootstrapCommand> for BootstrapCommand {
+    fn from(command: &'a mut BootstrapCommand) -> Self {
+        BootstrapCommand::from(&mut command.command)
+    }
+}
+
+impl From<Command> for BootstrapCommand {
+    fn from(command: Command) -> Self {
         Self { command, failure_behavior: BehaviorOnFailure::Exit, output_mode: None }
     }
 }
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index 59b29eedb79..adf18c0ace1 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -18,7 +18,7 @@ use crate::core::builder::Builder;
 use crate::core::config::{Config, TargetSelection};
 use crate::LldMode;
 
-pub use crate::utils::dylib::{dylib_path, dylib_path_var};
+pub use crate::utils::shared_helpers::{dylib_path, dylib_path_var};
 
 #[cfg(test)]
 mod tests;
@@ -47,10 +47,11 @@ macro_rules! t {
         }
     };
 }
+use crate::utils::exec::BootstrapCommand;
 pub use t;
 
 pub fn exe(name: &str, target: TargetSelection) -> String {
-    crate::utils::dylib::exe(name, &target.triple)
+    crate::utils::shared_helpers::exe(name, &target.triple)
 }
 
 /// Returns `true` if the file name given looks like a dynamic library.
@@ -72,7 +73,7 @@ pub fn libdir(target: TargetSelection) -> &'static str {
 
 /// Adds a list of lookup paths to `cmd`'s dynamic library lookup path.
 /// If the dylib_path_var is already set for this cmd, the old value will be overwritten!
-pub fn add_dylib_path(path: Vec<PathBuf>, cmd: &mut Command) {
+pub fn add_dylib_path(path: Vec<PathBuf>, cmd: &mut BootstrapCommand) {
     let mut list = dylib_path();
     for path in path {
         list.insert(0, path);
@@ -81,7 +82,7 @@ pub fn add_dylib_path(path: Vec<PathBuf>, cmd: &mut Command) {
 }
 
 /// Adds a list of lookup paths to `cmd`'s link library lookup path.
-pub fn add_link_lib_path(path: Vec<PathBuf>, cmd: &mut Command) {
+pub fn add_link_lib_path(path: Vec<PathBuf>, cmd: &mut BootstrapCommand) {
     let mut list = link_lib_path();
     for path in path {
         list.insert(0, path);
@@ -241,8 +242,9 @@ pub fn is_valid_test_suite_arg<'a, P: AsRef<Path>>(
     }
 }
 
-pub fn check_run(cmd: &mut Command, print_cmd_on_fail: bool) -> bool {
-    let status = match cmd.status() {
+// FIXME: get rid of this function
+pub fn check_run(cmd: &mut BootstrapCommand, print_cmd_on_fail: bool) -> bool {
+    let status = match cmd.command.status() {
         Ok(status) => status,
         Err(e) => {
             println!("failed to execute command: {cmd:?}\nERROR: {e}");
@@ -437,7 +439,7 @@ pub fn linker_flags(
 }
 
 pub fn add_rustdoc_cargo_linker_args(
-    cmd: &mut Command,
+    cmd: &mut BootstrapCommand,
     builder: &Builder<'_>,
     target: TargetSelection,
     lld_threads: LldThreads,
diff --git a/src/bootstrap/src/utils/mod.rs b/src/bootstrap/src/utils/mod.rs
index cb535f0e163..53b41f15780 100644
--- a/src/bootstrap/src/utils/mod.rs
+++ b/src/bootstrap/src/utils/mod.rs
@@ -6,11 +6,11 @@ pub(crate) mod cache;
 pub(crate) mod cc_detect;
 pub(crate) mod change_tracker;
 pub(crate) mod channel;
-pub(crate) mod dylib;
 pub(crate) mod exec;
 pub(crate) mod helpers;
 pub(crate) mod job;
 #[cfg(feature = "build-metrics")]
 pub(crate) mod metrics;
 pub(crate) mod render_tests;
+pub(crate) mod shared_helpers;
 pub(crate) mod tarball;
diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs
index 5c9918bce32..2e99bc68a8b 100644
--- a/src/bootstrap/src/utils/render_tests.rs
+++ b/src/bootstrap/src/utils/render_tests.rs
@@ -7,14 +7,18 @@
 //! to reimplement all the rendering logic in this module because of that.
 
 use crate::core::builder::Builder;
+use crate::utils::exec::BootstrapCommand;
 use std::io::{BufRead, BufReader, Read, Write};
-use std::process::{ChildStdout, Command, Stdio};
+use std::process::{ChildStdout, Stdio};
 use std::time::Duration;
 use termcolor::{Color, ColorSpec, WriteColor};
 
 const TERSE_TESTS_PER_LINE: usize = 88;
 
-pub(crate) fn add_flags_and_try_run_tests(builder: &Builder<'_>, cmd: &mut Command) -> bool {
+pub(crate) fn add_flags_and_try_run_tests(
+    builder: &Builder<'_>,
+    cmd: &mut BootstrapCommand,
+) -> bool {
     if !cmd.get_args().any(|arg| arg == "--") {
         cmd.arg("--");
     }
@@ -23,7 +27,11 @@ pub(crate) fn add_flags_and_try_run_tests(builder: &Builder<'_>, cmd: &mut Comma
     try_run_tests(builder, cmd, false)
 }
 
-pub(crate) fn try_run_tests(builder: &Builder<'_>, cmd: &mut Command, stream: bool) -> bool {
+pub(crate) fn try_run_tests(
+    builder: &Builder<'_>,
+    cmd: &mut BootstrapCommand,
+    stream: bool,
+) -> bool {
     if builder.config.dry_run() {
         return true;
     }
@@ -41,7 +49,8 @@ pub(crate) fn try_run_tests(builder: &Builder<'_>, cmd: &mut Command, stream: bo
     }
 }
 
-fn run_tests(builder: &Builder<'_>, cmd: &mut Command, stream: bool) -> bool {
+fn run_tests(builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool) -> bool {
+    let cmd = &mut cmd.command;
     cmd.stdout(Stdio::piped());
 
     builder.verbose(|| println!("running: {cmd:?}"));
diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs
new file mode 100644
index 00000000000..7150c84313c
--- /dev/null
+++ b/src/bootstrap/src/utils/shared_helpers.rs
@@ -0,0 +1,112 @@
+//! This module serves two purposes:
+//!     1. It is part of the `utils` module and used in other parts of bootstrap.
+//!     2. It is embedded inside bootstrap shims to avoid a dependency on the bootstrap library.
+//!        Therefore, this module should never use any other bootstrap module. This reduces binary
+//!        size and improves compilation time by minimizing linking time.
+
+#![allow(dead_code)]
+
+use std::env;
+use std::ffi::OsString;
+use std::fs::OpenOptions;
+use std::io::Write;
+use std::process::Command;
+use std::str::FromStr;
+
+#[cfg(test)]
+mod tests;
+
+/// Returns the environment variable which the dynamic library lookup path
+/// resides in for this platform.
+pub fn dylib_path_var() -> &'static str {
+    if cfg!(target_os = "windows") {
+        "PATH"
+    } else if cfg!(target_vendor = "apple") {
+        "DYLD_LIBRARY_PATH"
+    } else if cfg!(target_os = "haiku") {
+        "LIBRARY_PATH"
+    } else if cfg!(target_os = "aix") {
+        "LIBPATH"
+    } else {
+        "LD_LIBRARY_PATH"
+    }
+}
+
+/// Parses the `dylib_path_var()` environment variable, returning a list of
+/// paths that are members of this lookup path.
+pub fn dylib_path() -> Vec<std::path::PathBuf> {
+    let var = match std::env::var_os(dylib_path_var()) {
+        Some(v) => v,
+        None => return vec![],
+    };
+    std::env::split_paths(&var).collect()
+}
+
+/// Given an executable called `name`, return the filename for the
+/// executable for a particular target.
+pub fn exe(name: &str, target: &str) -> String {
+    if target.contains("windows") {
+        format!("{name}.exe")
+    } else if target.contains("uefi") {
+        format!("{name}.efi")
+    } else {
+        name.to_string()
+    }
+}
+
+/// Parses the value of the "RUSTC_VERBOSE" environment variable and returns it as a `usize`.
+/// If it was not defined, returns 0 by default.
+///
+/// Panics if "RUSTC_VERBOSE" is defined with the value that is not an unsigned integer.
+pub fn parse_rustc_verbose() -> usize {
+    match env::var("RUSTC_VERBOSE") {
+        Ok(s) => usize::from_str(&s).expect("RUSTC_VERBOSE should be an integer"),
+        Err(_) => 0,
+    }
+}
+
+/// Parses the value of the "RUSTC_STAGE" environment variable and returns it as a `String`.
+///
+/// If "RUSTC_STAGE" was not set, the program will be terminated with 101.
+pub fn parse_rustc_stage() -> String {
+    env::var("RUSTC_STAGE").unwrap_or_else(|_| {
+        // Don't panic here; it's reasonable to try and run these shims directly. Give a helpful error instead.
+        eprintln!("rustc shim: FATAL: RUSTC_STAGE was not set");
+        eprintln!("rustc shim: NOTE: use `x.py build -vvv` to see all environment variables set by bootstrap");
+        std::process::exit(101);
+    })
+}
+
+/// Writes the command invocation to a file if `DUMP_BOOTSTRAP_SHIMS` is set during bootstrap.
+///
+/// Before writing it, replaces user-specific values to create generic dumps for cross-environment
+/// comparisons.
+pub fn maybe_dump(dump_name: String, cmd: &Command) {
+    if let Ok(dump_dir) = env::var("DUMP_BOOTSTRAP_SHIMS") {
+        let dump_file = format!("{dump_dir}/{dump_name}");
+
+        let mut file = OpenOptions::new().create(true).append(true).open(dump_file).unwrap();
+
+        let cmd_dump = format!("{:?}\n", cmd);
+        let cmd_dump = cmd_dump.replace(&env::var("BUILD_OUT").unwrap(), "${BUILD_OUT}");
+        let cmd_dump = cmd_dump.replace(&env::var("CARGO_HOME").unwrap(), "${CARGO_HOME}");
+
+        file.write_all(cmd_dump.as_bytes()).expect("Unable to write file");
+    }
+}
+
+/// Finds `key` and returns its value from the given list of arguments `args`.
+pub fn parse_value_from_args<'a>(args: &'a [OsString], key: &str) -> Option<&'a str> {
+    let mut args = args.iter();
+    while let Some(arg) = args.next() {
+        let arg = arg.to_str().unwrap();
+
+        if let Some(value) = arg.strip_prefix(&format!("{key}=")) {
+            return Some(value);
+        } else if arg == key {
+            return args.next().map(|v| v.to_str().unwrap());
+        }
+    }
+
+    None
+}
diff --git a/src/bootstrap/src/utils/shared_helpers/tests.rs b/src/bootstrap/src/utils/shared_helpers/tests.rs
new file mode 100644
index 00000000000..da7924276f7
--- /dev/null
+++ b/src/bootstrap/src/utils/shared_helpers/tests.rs
@@ -0,0 +1,28 @@
+use super::parse_value_from_args;
+
+#[test]
+fn test_parse_value_from_args() {
+    let args = vec![
+        "--stage".into(),
+        "1".into(),
+        "--version".into(),
+        "2".into(),
+        "--target".into(),
+        "x86_64-unknown-linux".into(),
+    ];
+
+    assert_eq!(parse_value_from_args(args.as_slice(), "--stage").unwrap(), "1");
+    assert_eq!(parse_value_from_args(args.as_slice(), "--version").unwrap(), "2");
+    assert_eq!(parse_value_from_args(args.as_slice(), "--target").unwrap(), "x86_64-unknown-linux");
+    assert!(parse_value_from_args(args.as_slice(), "random-key").is_none());
+
+    let args = vec![
+        "app-name".into(),
+        "--key".into(),
+        "value".into(),
+        "random-value".into(),
+        "--sysroot=/x/y/z".into(),
+    ];
+    assert_eq!(parse_value_from_args(args.as_slice(), "--key").unwrap(), "value");
+    assert_eq!(parse_value_from_args(args.as_slice(), "--sysroot").unwrap(), "/x/y/z");
+}
diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs
index fd934f18de2..5cc319826db 100644
--- a/src/bootstrap/src/utils/tarball.rs
+++ b/src/bootstrap/src/utils/tarball.rs
@@ -5,14 +5,12 @@
 //! In uplifting, a tarball from Stage N captures essential components
 //! to assemble Stage N + 1 compiler.
 
-use std::{
-    path::{Path, PathBuf},
-    process::Command,
-};
+use std::path::{Path, PathBuf};
 
 use crate::core::builder::Builder;
 use crate::core::{build_steps::dist::distdir, builder::Kind};
 use crate::utils::channel;
+use crate::utils::exec::BootstrapCommand;
 use crate::utils::helpers::{move_file, t};
 
 #[derive(Copy, Clone)]
@@ -300,7 +298,7 @@ impl<'a> Tarball<'a> {
         }
     }
 
-    fn non_bare_args(&self, cmd: &mut Command) {
+    fn non_bare_args(&self, cmd: &mut BootstrapCommand) {
         cmd.arg("--rel-manifest-dir=rustlib")
             .arg("--legacy-manifest-dirs=rustlib,cargo")
             .arg(format!("--product-name={}", self.product_name))
@@ -312,7 +310,7 @@ impl<'a> Tarball<'a> {
             .arg(distdir(self.builder));
     }
 
-    fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut Command)) -> GeneratedTarball {
+    fn run(self, build_cli: impl FnOnce(&Tarball<'a>, &mut BootstrapCommand)) -> GeneratedTarball {
         t!(std::fs::create_dir_all(&self.overlay_dir));
         self.builder.create(&self.overlay_dir.join("version"), &self.overlay.version(self.builder));
         if let Some(info) = self.builder.rust_info().info() {
@@ -353,7 +351,7 @@ impl<'a> Tarball<'a> {
         };
 
         cmd.args(["--compression-profile", compression_profile]);
-        self.builder.run(&mut cmd);
+        self.builder.run(cmd);
 
         // Ensure there are no symbolic links in the tarball. In particular,
         // rustup-toolchain-install-master and most versions of Windows can't handle symbolic links.
diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile
index 55c737bd0aa..d3956651663 100644
--- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile
@@ -23,7 +23,30 @@ ENV CC_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-gcc \
     AR_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-ar \
     CXX_loongarch64_unknown_linux_gnu=loongarch64-unknown-linux-gnu-g++
 
+# We re-use the Linux toolchain for bare-metal, because upstream bare-metal
+# target support for LoongArch is only available from GCC 14+.
+#
+# See: https://github.com/gcc-mirror/gcc/commit/976f4f9e4770
+ENV CC_loongarch64_unknown_none=loongarch64-unknown-linux-gnu-gcc \
+    AR_loongarch64_unknown_none=loongarch64-unknown-linux-gnu-ar \
+    CXX_loongarch64_unknown_none=loongarch64-unknown-linux-gnu-g++ \
+    CFLAGS_loongarch64_unknown_none="-ffreestanding -mabi=lp64d" \
+    CXXFLAGS_loongarch64_unknown_none="-ffreestanding -mabi=lp64d" \
+    CC_loongarch64_unknown_none_softfloat=loongarch64-unknown-linux-gnu-gcc \
+    AR_loongarch64_unknown_none_softfloat=loongarch64-unknown-linux-gnu-ar \
+    CXX_loongarch64_unknown_none_softfloat=loongarch64-unknown-linux-gnu-g++ \
+    CFLAGS_loongarch64_unknown_none_softfloat="-ffreestanding -mabi=lp64s -mfpu=none" \
+    CXXFLAGS_loongarch64_unknown_none_softfloat="-ffreestanding -mabi=lp64s -mfpu=none"
+
 ENV HOSTS=loongarch64-unknown-linux-gnu
+ENV TARGETS=$HOSTS
+ENV TARGETS=$TARGETS,loongarch64-unknown-none
+ENV TARGETS=$TARGETS,loongarch64-unknown-none-softfloat
+
+ENV RUST_CONFIGURE_ARGS \
+      --enable-extended \
+      --enable-full-tools \
+      --enable-profiler \
+      --disable-docs
 
-ENV RUST_CONFIGURE_ARGS --enable-extended --disable-docs
-ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $HOSTS
+ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $TARGETS
diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile
index 560adf971ba..62dbfaaa673 100644
--- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile
@@ -27,7 +27,8 @@ ENV HOSTS=loongarch64-unknown-linux-musl
 
 ENV RUST_CONFIGURE_ARGS \
       --enable-extended \
-      --enable-lld \
+      --enable-full-tools \
+      --enable-profiler \
       --disable-docs \
       --set target.loongarch64-unknown-linux-musl.crt-static=false \
       --musl-root-loongarch64=/x-tools/loongarch64-unknown-linux-musl/loongarch64-unknown-linux-musl/sysroot/usr
diff --git a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
index bb6254942cb..e3cb396b782 100644
--- a/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-various-2/Dockerfile
@@ -121,8 +121,6 @@ ENV TARGETS=$TARGETS,armv7-unknown-linux-gnueabi
 ENV TARGETS=$TARGETS,armv7-unknown-linux-musleabi
 ENV TARGETS=$TARGETS,i686-unknown-freebsd
 ENV TARGETS=$TARGETS,x86_64-unknown-none
-ENV TARGETS=$TARGETS,loongarch64-unknown-none
-ENV TARGETS=$TARGETS,loongarch64-unknown-none-softfloat
 ENV TARGETS=$TARGETS,aarch64-unknown-uefi
 ENV TARGETS=$TARGETS,i686-unknown-uefi
 ENV TARGETS=$TARGETS,x86_64-unknown-uefi
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh
index e0c008b76fa..4a42f5da29f 100755
--- a/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh
+++ b/src/ci/docker/host-x86_64/dist-x86_64-netbsd/build-netbsd-toolchain.sh
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 # ignore-tidy-linelength
 
-set -ex
+set -eux
 
 hide_output() {
   set +x
@@ -20,6 +20,22 @@ exit 1
   set -x
 }
 
+# Download, verify SHA512, and remove the downloaded file
+# Usage: <file name> <url> <file sha> <full tar command using fname>
+download() {
+  fname="$1"
+  shift
+  url="$1"
+  shift
+  sha="$1"
+  shift
+
+  curl "$url" -o "$fname"
+  echo "$sha  $fname" | shasum -a 512 --check || exit 1
+  "$@"
+  rm "$fname"
+}
+
 mkdir netbsd
 cd netbsd
 
@@ -27,17 +43,31 @@ mkdir -p /x-tools/x86_64-unknown-netbsd/sysroot
 
 # URL=https://ci-mirrors.rust-lang.org/rustc
 
-SOURCE_URL=https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/source/sets
-curl $SOURCE_URL/src.tgz | tar xzf -
-curl $SOURCE_URL/gnusrc.tgz | tar xzf -
-curl $SOURCE_URL/sharesrc.tgz | tar xzf -
-curl $SOURCE_URL/syssrc.tgz | tar xzf -
-
-BINARY_URL=https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/amd64/binary/sets
-curl $BINARY_URL/base.tar.xz | \
-  tar xJf - -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib ./lib
-curl $BINARY_URL/comp.tar.xz | \
-  tar xJf - -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib
+# Hashes come from https://cdn.netbsd.org/pub/NetBSD/security/hashes/NetBSD-9.0_hashes.asc
+SRC_SHA=2c791ae009a6929c6fc893ec5df7e62910ee8207e0b2159d6937309c03efe175b6ae1e445829a13d041b6851334ad35c521f2fa03c97675d4a05f1fafe58ede0
+GNUSRC_SHA=3710085a73feecf6a843415271ec794c90146b03f6bbd30f07c9e0c79febf8995d557e40194f1e05db655e4f5ef2fae97563f8456fceaae65d4ea98857a83b1c
+SHARESRC_SHA=f080776ed82c3ac5d6272dee39746f87897d8e6984996caf5bf6d87bf11d9c9e0c1ad5c437c21258bd278bb6fd76974946e878f548517885f71c556096231369
+SYSSRC_SHA=60b9ddf4cc6402256473e2e1eefeabd9001aa4e205208715ecc6d6fc3f5b400e469944580077271b8e80562a4c2f601249e69e07a504f46744e0c50335f1cbf1
+BASE_SHA=b5926b107cebf40c3c19b4f6cd039b610987dd7f819e7cdde3bd1e5230a856906e7930b15ab242d52ced9f0bda01d574be59488b8dbb95fa5df2987d0a70995f
+COMP_SHA=38ea54f30d5fc2afea87e5096f06873e00182789e8ad9cec0cb3e9f7c538c1aa4779e63fd401a36ba02676158e83fa5c95e8e87898db59c1914fb206aecd82d2
+
+# FIXME: the archive URL is being used temporarily while the CDN is down.
+# We should serve this from our own CDN
+# SOURCE_URL=https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/source/sets
+SOURCE_URL=http://archive.netbsd.org/pub/NetBSD-archive/NetBSD-9.0/source/sets
+download src.tgz "$SOURCE_URL/src.tgz" "$SRC_SHA" tar xzf src.tgz
+download gnusrc.tgz "$SOURCE_URL/gnusrc.tgz" "$GNUSRC_SHA" tar xzf gnusrc.tgz
+download sharesrc.tgz "$SOURCE_URL/sharesrc.tgz" "$SHARESRC_SHA" tar xzf sharesrc.tgz
+download syssrc.tgz "$SOURCE_URL/syssrc.tgz" "$SYSSRC_SHA" tar xzf syssrc.tgz
+
+# FIXME: the archive URL is being used temporarily while the CDN is down.
+# We should serve this from our own CDN
+# BINARY_URL=https://cdn.netbsd.org/pub/NetBSD/NetBSD-9.0/amd64/binary/sets
+BINARY_URL=http://archive.netbsd.org/pub/NetBSD-archive/NetBSD-9.0/amd64/binary/sets
+download base.tar.xz "$BINARY_URL/base.tar.xz" "$BASE_SHA" \
+  tar xJf base.tar.xz -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib ./lib
+download comp.tar.xz "$BINARY_URL/comp.tar.xz" "$COMP_SHA" \
+  tar xJf comp.tar.xz -C /x-tools/x86_64-unknown-netbsd/sysroot ./usr/include ./usr/lib
 
 cd usr/src
 
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile b/src/ci/docker/host-x86_64/x86_64-fuchsia/Dockerfile
index a944f370c6b..ba3e8bdb687 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-integration/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-fuchsia/Dockerfile
@@ -1,5 +1,6 @@
 # This job builds a toolchain capable of building Fuchsia, and then builds
-# Fuchsia. See the build-fuchsia.sh script in this directory for more details.
+# Fuchsia as an integration test of the toolchain. See the build-fuchsia.sh
+# script in this directory for more details.
 
 FROM ubuntu:22.04
 
@@ -24,7 +25,6 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
   && rm -rf /var/lib/apt/lists/*
 
 # Duplicated in dist-various-2 Dockerfile.
-# FIXME: Move to canonical triple
 ENV \
     AR_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-ar \
     CC_x86_64_unknown_fuchsia=x86_64-unknown-fuchsia-clang \
@@ -48,10 +48,6 @@ ENV CARGO_TARGET_X86_64_UNKNOWN_FUCHSIA_RUSTFLAGS \
 
 ENV TARGETS=x86_64-unknown-fuchsia
 ENV TARGETS=$TARGETS,x86_64-unknown-linux-gnu
-ENV TARGETS=$TARGETS,wasm32-unknown-unknown
-
-# Fuchsia clang does not have wasm target enabled, use system clang.
-ENV CC_wasm32_unknown_unknown=clang-15
 
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
@@ -76,4 +72,4 @@ ENV RUST_CONFIGURE_ARGS \
   --set target.x86_64-unknown-fuchsia.linker=/usr/local/bin/ld.lld
 ENV SCRIPT \
     python3 ../x.py install --target $TARGETS compiler/rustc library/std clippy && \
-    bash ../src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh
+    bash ../src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh b/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh
index 2bb1d0a6338..c806b886dae 100755
--- a/src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh
+++ b/src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh
@@ -5,14 +5,14 @@
 #
 # You may run this script locally using Docker with the following command:
 #
-# $ src/ci/docker/run.sh x86_64-gnu-integration
+# $ src/ci/docker/run.sh x86_64-fuchsia
 #
 # Alternatively, from within the container with --dev, assuming you have made it
 # as far as building the toolchain with the above command:
 #
-# $ src/ci/docker/run.sh --dev x86_64-gnu-integration
+# $ src/ci/docker/run.sh --dev x86_64-fuchsia
 # docker# git config --global --add safe.directory /checkout/obj/fuchsia
-# docker# ../src/ci/docker/host-x86_64/x86_64-gnu-integration/build-fuchsia.sh
+# docker# ../src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh
 #
 # Also see the docs in the rustc-dev-guide for more info:
 # https://github.com/rust-lang/rustc-dev-guide/pull/1989
@@ -21,7 +21,7 @@ set -euf -o pipefail
 
 # Set this variable to 1 to disable updating the Fuchsia checkout. This is
 # useful for making local changes. You can find the Fuchsia checkout in
-# `obj/x86_64-gnu-integration/fuchsia` in your local checkout after running this
+# `obj/x86_64-fuchsia/fuchsia` in your local checkout after running this
 # job for the first time.
 KEEP_CHECKOUT=
 
@@ -35,7 +35,7 @@ PICK_REFS=()
 # commit hash of fuchsia.git and some other repos in the "monorepo" checkout, in
 # addition to versions of prebuilts. It should be bumped regularly by the
 # Fuchsia team – we aim for every 1-2 months.
-INTEGRATION_SHA=737ebdd83afa47b742ca8325fad0176952fcefbd
+INTEGRATION_SHA=d1d2f20efe46e22be179953dd6726c96eced54ab
 
 checkout=fuchsia
 jiri=.jiri_root/bin/jiri
diff --git a/src/ci/docker/host-x86_64/rfl/Dockerfile b/src/ci/docker/host-x86_64/x86_64-rust-for-linux/Dockerfile
index 97298519cf2..97298519cf2 100644
--- a/src/ci/docker/host-x86_64/rfl/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-rust-for-linux/Dockerfile
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index e3903c3dd5a..a6e12c6ff95 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -208,6 +208,18 @@ auto:
   - image: test-various
     <<: *job-linux-4c
 
+  - image: x86_64-fuchsia
+    # Only run this job on the nightly channel. Fuchsia requires
+    # nightly features to compile, and this job would fail if
+    # executed on beta and stable.
+    only_on_channel: nightly
+    <<: *job-linux-8c
+
+  # Tests integration with Rust for Linux.
+  # Builds stage 1 compiler and tries to compile a few RfL examples with it.
+  - image: x86_64-rust-for-linux
+    <<: *job-linux-8c
+
   - image: x86_64-gnu
     <<: *job-linux-4c
 
@@ -229,13 +241,6 @@ auto:
   - image: x86_64-gnu-aux
     <<: *job-linux-4c
 
-  - image: x86_64-gnu-integration
-    # Only run this job on the nightly channel. Fuchsia requires
-    # nightly features to compile, and this job would fail if
-    # executed on beta and stable.
-    only_on_channel: nightly
-    <<: *job-linux-8c
-
   - image: x86_64-gnu-debug
     <<: *job-linux-4c
 
@@ -465,8 +470,3 @@ auto:
       RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-extended --enable-profiler
       SCRIPT: python x.py dist bootstrap --include-default-paths
     <<: *job-windows-8c
-
-  # Tests integration with Rust for Linux.
-  # Builds stage 1 compiler and tries to compile a few RfL examples with it.
-  - image: rfl
-    <<: *job-linux-8c
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 71dc8c4ca0f..f5cd4bd217a 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -168,12 +168,12 @@ target | std | notes
 `i686-unknown-freebsd` | ✓ | 32-bit FreeBSD [^x86_32-floats-return-ABI]
 `i686-unknown-linux-musl` | ✓ | 32-bit Linux with musl 1.2.3 [^x86_32-floats-return-ABI]
 [`i686-unknown-uefi`](platform-support/unknown-uefi.md) | ? | 32-bit UEFI
-[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * |  | LoongArch64 Bare-metal (LP64D ABI)
-[`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * |  | LoongArch64 Bare-metal (LP64S ABI)
+[`loongarch64-unknown-none`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64D ABI)
+[`loongarch64-unknown-none-softfloat`](platform-support/loongarch-none.md) | * | LoongArch64 Bare-metal (LP64S ABI)
 [`nvptx64-nvidia-cuda`](platform-support/nvptx64-nvidia-cuda.md) | * | --emit=asm generates PTX code that [runs on NVIDIA GPUs]
 [`riscv32imac-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAC ISA)
 [`riscv32i-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32I ISA)
-[`riscv32im-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * |  | Bare RISC-V (RV32IM ISA)
+[`riscv32im-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IM ISA)
 [`riscv32imc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMC ISA)
 [`riscv32imafc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * | Bare RISC-V (RV32IMAFC ISA)
 `riscv64gc-unknown-none-elf` | * | Bare RISC-V (RV64IMAFDC ISA)
@@ -193,7 +193,7 @@ target | std | notes
 `wasm32-unknown-unknown` | ✓ | WebAssembly
 `wasm32-wasi` | ✓ | WebAssembly with WASI (undergoing a [rename to `wasm32-wasip1`][wasi-rename])
 [`wasm32-wasip1`](platform-support/wasm32-wasip1.md) | ✓ | WebAssembly with WASI
-[`wasm32-wasip1-threads`](platform-support/wasm32-wasip1-threads.md) | ✓ |  | WebAssembly with WASI Preview 1 and threads
+[`wasm32-wasip1-threads`](platform-support/wasm32-wasip1-threads.md) | ✓ | WebAssembly with WASI Preview 1 and threads
 [`x86_64-apple-ios`](platform-support/apple-ios.md) | ✓ | 64-bit x86 iOS
 [`x86_64-fortanix-unknown-sgx`](platform-support/x86_64-fortanix-unknown-sgx.md) | ✓ | [Fortanix ABI] for 64-bit Intel SGX
 `x86_64-fuchsia` | ✓ | Alias for `x86_64-unknown-fuchsia`
diff --git a/src/doc/rustc/src/platform-support/loongarch-linux.md b/src/doc/rustc/src/platform-support/loongarch-linux.md
index e8f55b8bfce..45eb0a81216 100644
--- a/src/doc/rustc/src/platform-support/loongarch-linux.md
+++ b/src/doc/rustc/src/platform-support/loongarch-linux.md
@@ -1,30 +1,24 @@
-# loongarch\*-unknown-linux-\*
+# `loongarch*-unknown-linux-*`
 
-**Tier: 2**
+**Tier: 2 (with Host Tools)**
 
-[LoongArch] is a new RISC ISA developed by Loongson Technology Corporation Limited.
+[LoongArch][la-docs] Linux targets.
+LoongArch is a RISC ISA developed by Loongson Technology Corporation Limited.
 
-[LoongArch]: https://loongson.github.io/LoongArch-Documentation/README-EN.html
+| Target | Description |
+|--------|-------------|
+| `loongarch64-unknown-linux-gnu` | LoongArch64 Linux, LP64D ABI (kernel 5.19, glibc 2.36) |
+| `loongarch64-unknown-linux-musl` | LoongArch64 Linux, LP64D ABI (kernel 5.19, musl 1.2.5) |
 
-The target name follow this format: `<machine>-<vendor>-<os><fabi_suffix>`, where `<machine>` specifies the CPU family/model, `<vendor>` specifies the vendor and `<os>` the operating system name.
-While the integer base ABI is implied by the machine field, the floating point base ABI type is encoded into the os field of the specifier using the string suffix `<fabi-suffix>`.
+These support both native and cross builds, and have full support for `std`.
 
-|    `<fabi-suffix>`     |                           `Description`                            |
-|------------------------|--------------------------------------------------------------------|
-|          f64           | The base ABI use 64-bits FPRs for parameter passing. (lp64d)|
-|          f32           | The base ABI uses 32-bit FPRs for parameter passing. (lp64f)|
-|          sf            | The base ABI uses no FPR for parameter passing. (lp64s)     |
+Reference material:
 
-<br>
+* [LoongArch ISA manuals][la-docs]
+* [Application Binary Interface for the LoongArch&trade; Architecture][la-abi-specs]
 
-|`ABI type(Base ABI/ABI extension)`| `C library` | `kernel` |          `target tuple`          |
-|----------------------------------|-------------|----------|----------------------------------|
-|           lp64d/base             |   glibc     |  linux   | loongarch64-unknown-linux-gnu |
-|           lp64f/base             |   glibc     |  linux   | loongarch64-unknown-linux-gnuf32 |
-|           lp64s/base             |   glibc     |  linux   | loongarch64-unknown-linux-gnusf  |
-|           lp64d/base             |  musl libc  |  linux   | loongarch64-unknown-linux-musl|
-|           lp64f/base             |  musl libc  |  linux   | loongarch64-unknown-linux-muslf32|
-|           lp64s/base             |  musl libc  |  linux   | loongarch64-unknown-linux-muslsf |
+[la-abi-specs]: https://github.com/loongson/la-abi-specs
+[la-docs]: https://loongson.github.io/LoongArch-Documentation/README-EN.html
 
 ## Target maintainers
 
@@ -35,23 +29,57 @@ While the integer base ABI is implied by the machine field, the floating po
 
 ## Requirements
 
-This target is cross-compiled.
-A GNU toolchain for LoongArch target is required.  It can be downloaded from https://github.com/loongson/build-tools/releases, or built from the source code of GCC (12.1.0 or later) and Binutils (2.40 or later).
+### OS Version
 
-## Building the target
+The minimum supported Linux version is 5.19.
 
-The target can be built by enabling it for a `rustc` build.
+Some Linux distributions, mostly commercial ones, may provide forked Linux
+kernels that has a version number less than 5.19 for their LoongArch ports.
+Such kernels may still get patched to be compatible with the upstream Linux
+5.19 UAPI, therefore supporting the targets described in this document, but
+this is not always the case. The `rustup` installer contains a check for this,
+and will abort if incompatibility is detected.
+
+### Host toolchain
+
+The targets require a reasonably up-to-date LoongArch toolchain on the host.
+Currently the following components are used by the Rust CI to build the target,
+and the versions can be seen as the minimum requirement:
+
+* GNU Binutils 2.40
+* GCC 13.x
+* glibc 2.36
+* linux-headers 5.19
+
+Of these, glibc and linux-headers are at their respective earliest versions with
+mainline LoongArch support, so it is impossible to use older versions of these.
+Older versions of Binutils and GCC will not work either, due to lack of support
+for newer LoongArch ELF relocation types, among other features.
+
+Recent LLVM/Clang toolchains may be able to build the targets, but are not
+currently being actively tested.
+
+## Building
+
+These targets are distributed through `rustup`, and otherwise require no
+special configuration.
+
+If you need to build your own Rust for some reason though, the targets can be
+simply enabled in `config.toml`. For example:
 
 ```toml
 [build]
 target = ["loongarch64-unknown-linux-gnu"]
 ```
 
-Make sure `loongarch64-unknown-linux-gnu-gcc` can be searched from the directories specified in`$PATH`. Alternatively, you can use GNU LoongArch Toolchain by adding the following to `config.toml`:
+Make sure the LoongArch toolchain binaries are reachable from `$PATH`.
+Alternatively, you can explicitly configure the paths in `config.toml`:
 
 ```toml
 [target.loongarch64-unknown-linux-gnu]
-# ADJUST THIS PATH TO POINT AT YOUR TOOLCHAIN
+# Adjust the paths to point at your toolchain
+# Suppose the toolchain is placed at /TOOLCHAIN_PATH, and the cross prefix is
+# "loongarch64-unknown-linux-gnu-":
 cc = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc"
 cxx = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-g++"
 ar = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-ar"
@@ -59,36 +87,51 @@ ranlib = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-ranlib"
 linker = "/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc"
 ```
 
-## Cross-compilation
+### Cross-compilation
 
-This target can be cross-compiled on a `x86_64-unknown-linux-gnu` host. Cross-compilation on other hosts may work but is not tested.
+This target can be cross-compiled on a `x86_64-unknown-linux-gnu` host.
+Other hosts are also likely to work, but not actively tested.
+
+You can test the cross build directly on the host, thanks to QEMU linux-user emulation.
+An example is given below:
+
+```sh
+# Suppose the cross toolchain is placed at $TOOLCHAIN_PATH, with a cross prefix
+# of "loongarch64-unknown-linux-gnu-".
+export CC_loongarch64_unknown_linux_gnu="$TOOLCHAIN_PATH"/bin/loongarch64-unknown-linux-gnu-gcc
+export CXX_loongarch64_unknown_linux_gnu="$TOOLCHAIN_PATH"/bin/loongarch64-unknown-linux-gnu-g++
+export AR_loongarch64_unknown_linux_gnu="$TOOLCHAIN_PATH"/bin/loongarch64-unknown-linux-gnu-gcc-ar
+export CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER="$TOOLCHAIN_PATH"/bin/loongarch64-unknown-linux-gnu-gcc
+
+# Point qemu-loongarch64 to the LoongArch sysroot.
+# Suppose the sysroot is located at "sysroot" below the toolchain root:
+export CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-loongarch64 -L $TOOLCHAIN_PATH/sysroot"
+# Or alternatively, if binfmt_misc is set up for running LoongArch binaries
+# transparently:
+export QEMU_LD_PREFIX="$TOOLCHAIN_PATH"/sysroot
 
-## Testing
-To test a cross-compiled binary on your build system, install the qemu binary that supports the LoongArch architecture and execute the following commands.
-```text
-CC_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc \
-CXX_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-g++ \
-AR_loongarch64_unknown_linux_gnu=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc-ar \
-CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNUN_LINKER=/TOOLCHAIN_PATH/bin/loongarch64-unknown-linux-gnu-gcc \
-# SET TARGET SYSTEM LIBRARY PATH
-CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNUN_RUNNER="qemu-loongarch64 -L /TOOLCHAIN_PATH/TARGET_LIBRARY_PATH" \
 cargo run --target loongarch64-unknown-linux-gnu --release
 ```
-Tested on x86 architecture, other architectures not tested.
 
-## Building Rust programs
+## Testing
+
+There are no special requirements for testing and running the targets.
+For testing cross builds on the host, please refer to the "Cross-compilation"
+section above.
 
-Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you will either need to build Rust with the target enabled (see "Building the target" above), or build your own copy of `std` by using `build-std` or similar.
+## Building Rust programs
 
-If `rustc` has support for that target and the library artifacts are available, then Rust static libraries can be built for that target:
+As the targets are available through `rustup`, it is very easy to build Rust
+programs for these targets: same as with other architectures.
+Note that you will need a 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).
 
-```shell
-$ rustc --target loongarch64-unknown-linux-gnu your-code.rs --crate-type staticlib
-$ ls libyour_code.a
+```sh
+rustup target add loongarch64-unknown-linux-gnu
+cargo build --target loongarch64-unknown-linux-gnu
 ```
 
-On Rust Nightly it's possible to build without the target artifacts available:
+Availability of pre-built artifacts through `rustup` are as follows:
 
-```text
-cargo build -Z build-std --target loongarch64-unknown-linux-gnu
-```
+* `loongarch64-unknown-linux-gnu`: since Rust 1.71;
+* `loongarch64-unknown-linux-musl`: since Rust 1.81.
diff --git a/src/doc/rustc/src/platform-support/loongarch-none.md b/src/doc/rustc/src/platform-support/loongarch-none.md
index 68d7c9d85e4..110a7cc3424 100644
--- a/src/doc/rustc/src/platform-support/loongarch-none.md
+++ b/src/doc/rustc/src/platform-support/loongarch-none.md
@@ -4,10 +4,10 @@
 
 Freestanding/bare-metal LoongArch64 binaries in ELF format: firmware, kernels, etc.
 
-| Target                             | Descriptions                                          |
-|------------------------------------|-------------------------------------------------------|
-| loongarch64-unknown-none           | LoongArch 64-bit, LP64D ABI (freestanding, hardfloat) |
-| loongarch64-unknown-none-softfloat | LoongArch 64-bit, LP64S ABI (freestanding, softfloat) |
+| 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 maintainers
 
@@ -19,6 +19,8 @@ Freestanding/bare-metal LoongArch64 binaries in ELF format: firmware, kernels, e
 This target is cross-compiled. There is no support for `std`. There is no
 default allocator, but it's possible to use `alloc` by supplying an allocator.
 
+The `*-softfloat` target does not assume existence of FPU or any other LoongArch
+ISA extension, and does not make use of any non-GPR register.
 This allows the generated code to run in environments, such as kernels, which
 may need to avoid the use of such registers or which may have special considerations
 about the use of such registers (e.g. saving and restoring them to avoid breaking
@@ -26,54 +28,64 @@ userspace code using the same registers). You can change code generation to use
 additional CPU features via the `-C target-feature=` codegen options to rustc, or
 via the `#[target_feature]` mechanism within Rust code.
 
-By default, code generated with this target should run on any `loongarch`
-hardware; enabling additional target features may raise this baseline.
+By default, code generated with the soft-float target should run on any
+LoongArch64 hardware, with the hard-float target additionally requiring an FPU;
+enabling additional target features may raise this baseline.
 
-Code generated with this target will use the `small` code model by default.
+Code generated with the targets will use the `small` code model by default.
 You can change this using the `-C code-model=` option to rustc.
 
-On `loongarch64-unknown-none*`, `extern "C"` uses the [standard calling
-convention](https://loongson.github.io/LoongArch-Documentation/LoongArch-ELF-ABI-EN.html).
+On `loongarch64-unknown-none*`, `extern "C"` uses the [architecture's standard calling convention][lapcs].
 
-This target generates binaries in the ELF format. Any alternate formats or
+[lapcs]: https://github.com/loongson/la-abi-specs/blob/release/lapcs.adoc
+
+The targets generate binaries in the ELF format. Any alternate formats or
 special considerations for binary layout will require linker options or linker
 scripts.
 
 ## Building the target
 
-You can build Rust with support for the target by adding it to the `target`
+You can build Rust with support for the targets by adding them to the `target`
 list in `config.toml`:
 
 ```toml
 [build]
 build-stage = 1
-target = ["loongarch64-unknown-none"]
+target = [
+  "loongarch64-unknown-none",
+  "loongarch64-unknown-none-softfloat",
+]
 ```
 
+## Testing
+
+As the targets support a variety of different environments and do not support
+`std`, they do not support running the Rust test suite.
+
 ## Building Rust programs
 
-```text
+Starting with Rust 1.74, precompiled artifacts are provided via `rustup`:
+
+```sh
+# install cross-compile toolchain
+rustup target add loongarch64-unknown-none
 # target flag may be used with any cargo or rustc command
 cargo build --target loongarch64-unknown-none
 ```
 
-## Testing
-
-As `loongarch64-unknown-none*` supports a variety of different environments and does
-not support `std`, this target does not support running the Rust test suite.
-
 ## Cross-compilation toolchains and C code
 
-If you want to compile C code along with Rust (such as for Rust crates with C
-dependencies), you will need an appropriate `loongarch` toolchain.
+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
 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.
 
-On some `loongarch` hosts that use ELF binaries, you *may* be able to use the host
+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
 that don't match the expectations of a standalone environment. Otherwise, you
 may need a separate toolchain for standalone/freestanding development, just as
-when cross-compiling from a non-`loongarch` platform.
+when cross-compiling from a non-LoongArch platform.
diff --git a/src/doc/style-guide/src/nightly.md b/src/doc/style-guide/src/nightly.md
index 66e7fa3c9f8..d870edf1888 100644
--- a/src/doc/style-guide/src/nightly.md
+++ b/src/doc/style-guide/src/nightly.md
@@ -5,3 +5,15 @@ This chapter documents style and formatting for nightly-only syntax. The rest of
 Style and formatting for nightly-only syntax should be removed from this chapter and integrated into the appropriate sections of the style guide at the time of stabilization.
 
 There is no guarantee of the stability of this chapter in contrast to the rest of the style guide. Refer to the style team policy for nightly formatting procedure regarding breaking changes to this chapter.
+
+### `feature(precise_capturing)`
+
+A `use<'a, T>` precise capturing bound is formatted as if it were a single path segment with non-turbofished angle-bracketed args, like a trait bound whose identifier is `use`.
+
+```
+fn foo() -> impl Sized + use<'a> {}
+
+// is formatted analogously to:
+
+fn foo() -> impl Sized + Use<'a> {}
+```
diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish
index 2072f76a481..805fc8aa8cc 100644
--- a/src/etc/completions/x.py.fish
+++ b/src/etc/completions/x.py.fish
@@ -48,7 +48,7 @@ complete -c x.py -n "__fish_use_subcommand" -f -a "run" -d 'Run tools contained
 complete -c x.py -n "__fish_use_subcommand" -f -a "setup" -d 'Set up the environment for development'
 complete -c x.py -n "__fish_use_subcommand" -f -a "suggest" -d 'Suggest a subset of tests to run, based on modified files'
 complete -c x.py -n "__fish_use_subcommand" -f -a "vendor" -d 'Vendor dependencies'
-complete -c x.py -n "__fish_use_subcommand" -f -a "perf" -d 'Perform profiling and benchmarking of the compiler using the `rustc-perf` benchmark suite'
+complete -c x.py -n "__fish_use_subcommand" -f -a "perf" -d 'Perform profiling and benchmarking of the compiler using the `rustc-perf-wrapper` tool'
 complete -c x.py -n "__fish_seen_subcommand_from build" -l config -d 'TOML configuration file for build' -r -F
 complete -c x.py -n "__fish_seen_subcommand_from build" -l build-dir -d 'Build directory, overrides `build.build-dir` in `config.toml`' -r -f -a "(__fish_complete_directories)"
 complete -c x.py -n "__fish_seen_subcommand_from build" -l build -d 'build target of the stage0 compiler' -r -f
diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1
index 919382d441f..ce590d2fa48 100644
--- a/src/etc/completions/x.py.ps1
+++ b/src/etc/completions/x.py.ps1
@@ -75,7 +75,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('setup', 'setup', [CompletionResultType]::ParameterValue, 'Set up the environment for development')
             [CompletionResult]::new('suggest', 'suggest', [CompletionResultType]::ParameterValue, 'Suggest a subset of tests to run, based on modified files')
             [CompletionResult]::new('vendor', 'vendor', [CompletionResultType]::ParameterValue, 'Vendor dependencies')
-            [CompletionResult]::new('perf', 'perf', [CompletionResultType]::ParameterValue, 'Perform profiling and benchmarking of the compiler using the `rustc-perf` benchmark suite')
+            [CompletionResult]::new('perf', 'perf', [CompletionResultType]::ParameterValue, 'Perform profiling and benchmarking of the compiler using the `rustc-perf-wrapper` tool')
             break
         }
         'x.py;build' {
diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh
index bbebf8b892d..fc8be4f7881 100644
--- a/src/etc/completions/x.py.zsh
+++ b/src/etc/completions/x.py.zsh
@@ -856,7 +856,7 @@ _x.py_commands() {
 'setup:Set up the environment for development' \
 'suggest:Suggest a subset of tests to run, based on modified files' \
 'vendor:Vendor dependencies' \
-'perf:Perform profiling and benchmarking of the compiler using the \`rustc-perf\` benchmark suite' \
+'perf:Perform profiling and benchmarking of the compiler using the \`rustc-perf-wrapper\` tool' \
     )
     _describe -t commands 'x.py commands' commands "$@"
 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 22565ea4028..b5660cd8492 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -549,7 +549,7 @@ fn clean_generic_param_def<'tcx>(
                 },
             )
         }
-        ty::GenericParamDefKind::Const { has_default, is_host_effect } => (
+        ty::GenericParamDefKind::Const { has_default, synthetic, is_host_effect: _ } => (
             def.name,
             GenericParamDefKind::Const {
                 ty: Box::new(clean_middle_ty(
@@ -572,7 +572,7 @@ fn clean_generic_param_def<'tcx>(
                 } else {
                     None
                 },
-                is_host_effect,
+                synthetic,
             },
         ),
     };
@@ -628,13 +628,13 @@ fn clean_generic_param<'tcx>(
                 },
             )
         }
-        hir::GenericParamKind::Const { ty, default, is_host_effect } => (
+        hir::GenericParamKind::Const { ty, default, synthetic, is_host_effect: _ } => (
             param.name.ident().name,
             GenericParamDefKind::Const {
                 ty: Box::new(clean_ty(ty, cx)),
                 default: default
                     .map(|ct| Box::new(ty::Const::from_anon_const(cx.tcx, ct.def_id).to_string())),
-                is_host_effect,
+                synthetic,
             },
         ),
     };
@@ -1411,7 +1411,11 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
             let mut generics = clean_ty_generics(
                 cx,
                 tcx.generics_of(assoc_item.def_id),
-                ty::GenericPredicates { parent: None, predicates },
+                ty::GenericPredicates {
+                    parent: None,
+                    predicates,
+                    effects_min_tys: ty::List::empty(),
+                },
             );
             simplify::move_bounds_to_generic_parameters(&mut generics);
 
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index af61eb6ae8d..58eef36677b 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -113,7 +113,7 @@ fn trait_is_same_or_supertrait(cx: &DocContext<'_>, child: DefId, trait_: DefId)
     if child == trait_ {
         return true;
     }
-    let predicates = cx.tcx.super_predicates_of(child);
+    let predicates = cx.tcx.explicit_super_predicates_of(child);
     debug_assert!(cx.tcx.generics_of(child).has_self);
     let self_ty = cx.tcx.types.self_param;
     predicates
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index c4020f2a450..9050a1c1207 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1321,7 +1321,7 @@ pub(crate) enum GenericParamDefKind {
     Lifetime { outlives: ThinVec<Lifetime> },
     Type { bounds: ThinVec<GenericBound>, default: Option<Box<Type>>, synthetic: bool },
     // Option<Box<String>> makes this type smaller than `Option<String>` would.
-    Const { ty: Box<Type>, default: Option<Box<String>>, is_host_effect: bool },
+    Const { ty: Box<Type>, default: Option<Box<String>>, synthetic: bool },
 }
 
 impl GenericParamDefKind {
@@ -1345,7 +1345,7 @@ impl GenericParamDef {
     pub(crate) fn is_synthetic_param(&self) -> bool {
         match self.kind {
             GenericParamDefKind::Lifetime { .. } => false,
-            GenericParamDefKind::Const { is_host_effect, .. } => is_host_effect,
+            GenericParamDefKind::Const { synthetic: is_host_effect, .. } => is_host_effect,
             GenericParamDefKind::Type { synthetic, .. } => synthetic,
         }
     }
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index d099a97f1cb..b965ab019cc 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -466,7 +466,7 @@ impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
                 default: default.map(|x| (*x).into_tcx(tcx)),
                 synthetic,
             },
-            Const { ty, default, is_host_effect: _ } => GenericParamDefKind::Const {
+            Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
                 type_: (*ty).into_tcx(tcx),
                 default: default.map(|x| *x),
             },
@@ -501,14 +501,12 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
                                     synthetic,
                                 }
                             }
-                            clean::GenericParamDefKind::Const {
-                                ty,
-                                default,
-                                is_host_effect: _,
-                            } => GenericParamDefKind::Const {
-                                type_: (*ty).into_tcx(tcx),
-                                default: default.map(|d| *d),
-                            },
+                            clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => {
+                                GenericParamDefKind::Const {
+                                    type_: (*ty).into_tcx(tcx),
+                                    default: default.map(|d| *d),
+                                }
+                            }
                         };
                         GenericParamDef { name, kind }
                     })
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 440b02a1fa7..37f7e7ed385 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -1507,6 +1507,15 @@ impl Disambiguator {
     fn from_str(link: &str) -> Result<Option<(Self, &str, &str)>, (String, Range<usize>)> {
         use Disambiguator::{Kind, Namespace as NS, Primitive};
 
+        let suffixes = [
+            // If you update this list, please also update the relevant rustdoc book section!
+            ("!()", DefKind::Macro(MacroKind::Bang)),
+            ("!{}", DefKind::Macro(MacroKind::Bang)),
+            ("![]", DefKind::Macro(MacroKind::Bang)),
+            ("()", DefKind::Fn),
+            ("!", DefKind::Macro(MacroKind::Bang)),
+        ];
+
         if let Some(idx) = link.find('@') {
             let (prefix, rest) = link.split_at(idx);
             let d = match prefix {
@@ -1530,16 +1539,23 @@ impl Disambiguator {
                 "prim" | "primitive" => Primitive,
                 _ => return Err((format!("unknown disambiguator `{prefix}`"), 0..idx)),
             };
+
+            for (suffix, kind) in suffixes {
+                if let Some(path_str) = rest.strip_suffix(suffix) {
+                    if d.ns() != Kind(kind).ns() {
+                        return Err((
+                            format!("unmatched disambiguator `{prefix}` and suffix `{suffix}`"),
+                            0..idx,
+                        ));
+                    } else if path_str.len() > 1 {
+                        // path_str != "@"
+                        return Ok(Some((d, &path_str[1..], &rest[1..])));
+                    }
+                }
+            }
+
             Ok(Some((d, &rest[1..], &rest[1..])))
         } else {
-            let suffixes = [
-                // If you update this list, please also update the relevant rustdoc book section!
-                ("!()", DefKind::Macro(MacroKind::Bang)),
-                ("!{}", DefKind::Macro(MacroKind::Bang)),
-                ("![]", DefKind::Macro(MacroKind::Bang)),
-                ("()", DefKind::Fn),
-                ("!", DefKind::Macro(MacroKind::Bang)),
-            ];
             for (suffix, kind) in suffixes {
                 if let Some(path_str) = link.strip_suffix(suffix) {
                     // Avoid turning `!` or `()` into an empty string
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index 170ecf896b4..67b48878ca5 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -246,7 +246,7 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds
                 && let [.., path] = poly_trait.trait_ref.path.segments
                 && poly_trait.bound_generic_params.is_empty()
                 && let Some(trait_def_id) = path.res.opt_def_id()
-                && let predicates = cx.tcx.super_predicates_of(trait_def_id).predicates
+                && let predicates = cx.tcx.explicit_super_predicates_of(trait_def_id).predicates
                 // If the trait has no supertrait, there is no need to collect anything from that bound
                 && !predicates.is_empty()
             {
diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
index 6f9b38fcf83..b62ecef0069 100644
--- a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs
@@ -24,7 +24,7 @@ fn is_subtrait_of_any(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
                 cx.tcx.is_diagnostic_item(sym::Any, tr.def_id)
                     || cx
                         .tcx
-                        .super_predicates_of(tr.def_id)
+                        .explicit_super_predicates_of(tr.def_id)
                         .predicates
                         .iter()
                         .any(|(clause, _)| {
diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
index 4922c87b206..a1d8ec3b32e 100644
--- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
@@ -91,7 +91,7 @@ fn path_to_sized_bound(cx: &LateContext<'_>, trait_bound: &PolyTraitRef<'_>) ->
             return true;
         }
 
-        for &(predicate, _) in cx.tcx.super_predicates_of(trait_def_id).predicates {
+        for &(predicate, _) in cx.tcx.explicit_super_predicates_of(trait_def_id).predicates {
             if let ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder()
                 && trait_predicate.polarity == PredicatePolarity::Positive
                 && !path.contains(&trait_predicate.def_id())
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
index 921dcf0b162..f8fc935f367 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
@@ -104,15 +104,18 @@ fn main() {}
 
 struct D;
 
+/* FIXME(effects)
 impl const Drop for D {
     fn drop(&mut self) {
         todo!();
     }
 }
+*/
 
 // Lint this, since it can be dropped in const contexts
 // FIXME(effects)
-fn d(this: D) {}
+const fn d(this: D) {}
+//~^ ERROR: this could be a `const fn`
 
 mod msrv {
     struct Foo(*const u8, &'static u8);
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
index 58e639cc7fd..5e4e2c58e5a 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -104,15 +104,18 @@ fn main() {}
 
 struct D;
 
+/* FIXME(effects)
 impl const Drop for D {
     fn drop(&mut self) {
         todo!();
     }
 }
+*/
 
 // Lint this, since it can be dropped in const contexts
 // FIXME(effects)
 fn d(this: D) {}
+//~^ ERROR: this could be a `const fn`
 
 mod msrv {
     struct Foo(*const u8, &'static u8);
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
index 8999af761e3..8302b074127 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -157,7 +157,18 @@ LL | const fn msrv_1_46() -> i32 {
    | +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:122:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:117:1
+   |
+LL | fn d(this: D) {}
+   | ^^^^^^^^^^^^^^^^
+   |
+help: make the function `const`
+   |
+LL | const fn d(this: D) {}
+   | +++++
+
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:125:9
    |
 LL | /         fn deref_ptr_can_be_const(self) -> usize {
 LL | |
@@ -171,7 +182,7 @@ LL |         const fn deref_ptr_can_be_const(self) -> usize {
    |         +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:127:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:130:9
    |
 LL | /         fn deref_copied_val(self) -> usize {
 LL | |
@@ -185,7 +196,7 @@ LL |         const fn deref_copied_val(self) -> usize {
    |         +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:138:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:141:5
    |
 LL | /     fn union_access_can_be_const() {
 LL | |
@@ -200,7 +211,7 @@ LL |     const fn union_access_can_be_const() {
    |     +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:152:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:155:9
    |
 LL | /         pub fn new(strings: Vec<String>) -> Self {
 LL | |             Self { strings }
@@ -213,7 +224,7 @@ LL |         pub const fn new(strings: Vec<String>) -> Self {
    |             +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:157:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:160:9
    |
 LL | /         pub fn empty() -> Self {
 LL | |             Self { strings: Vec::new() }
@@ -226,7 +237,7 @@ LL |         pub const fn empty() -> Self {
    |             +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:168:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:171:9
    |
 LL | /         pub fn new(text: String) -> Self {
 LL | |             let vec = Vec::new();
@@ -239,5 +250,5 @@ help: make the function `const`
 LL |         pub const fn new(text: String) -> Self {
    |             +++++
 
-error: aborting due to 17 previous errors
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs
index 6e1685a8a94..6735e9faa7a 100644
--- a/src/tools/compiletest/src/command-list.rs
+++ b/src/tools/compiletest/src/command-list.rs
@@ -53,6 +53,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-gnu",
     "ignore-haiku",
     "ignore-horizon",
+    "ignore-i686-pc-windows-gnu",
     "ignore-i686-pc-windows-msvc",
     "ignore-illumos",
     "ignore-ios",
@@ -174,6 +175,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-bpf",
     "only-cdb",
     "only-gnu",
+    "only-i686-pc-windows-gnu",
     "only-i686-pc-windows-msvc",
     "only-ios",
     "only-linux",
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index e3837a2f8cc..ec3b8a96ef3 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.2.0"
 edition = "2021"
 
 [dependencies]
+bstr = "1.6.0"
 object = "0.34.0"
 similar = "2.5.0"
 wasmparser = "0.118.2"
diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs
index 0a1bd9b0b34..ee651704c6f 100644
--- a/src/tools/run-make-support/src/command.rs
+++ b/src/tools/run-make-support/src/command.rs
@@ -185,14 +185,14 @@ impl CompletedProcess {
     /// Checks that `stdout` does not contain `unexpected`.
     #[track_caller]
     pub fn assert_stdout_not_contains<S: AsRef<str>>(&self, unexpected: S) -> &Self {
-        assert_not_contains(&self.stdout_utf8(), unexpected.as_ref());
+        assert_not_contains(&self.stdout_utf8(), unexpected);
         self
     }
 
     /// Checks that `stdout` contains `expected`.
     #[track_caller]
     pub fn assert_stdout_contains<S: AsRef<str>>(&self, expected: S) -> &Self {
-        assert_contains(&self.stdout_utf8(), expected.as_ref());
+        assert_contains(&self.stdout_utf8(), expected);
         self
     }
 
@@ -206,14 +206,14 @@ impl CompletedProcess {
     /// Checks that `stderr` contains `expected`.
     #[track_caller]
     pub fn assert_stderr_contains<S: AsRef<str>>(&self, expected: S) -> &Self {
-        assert_contains(&self.stderr_utf8(), expected.as_ref());
+        assert_contains(&self.stderr_utf8(), expected);
         self
     }
 
     /// Checks that `stderr` does not contain `unexpected`.
     #[track_caller]
     pub fn assert_stderr_not_contains<S: AsRef<str>>(&self, unexpected: S) -> &Self {
-        assert_not_contains(&self.stdout_utf8(), unexpected.as_ref());
+        assert_not_contains(&self.stdout_utf8(), unexpected);
         self
     }
 
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 771cda630af..294dae10942 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -21,6 +21,7 @@ use std::io;
 use std::panic;
 use std::path::{Path, PathBuf};
 
+pub use bstr;
 pub use gimli;
 pub use object;
 pub use regex;
@@ -408,7 +409,9 @@ pub fn read_dir<F: FnMut(&Path)>(dir: impl AsRef<Path>, mut callback: F) {
 
 /// Check that `actual` is equal to `expected`. Panic otherwise.
 #[track_caller]
-pub fn assert_equals(actual: &str, expected: &str) {
+pub fn assert_equals<S1: AsRef<str>, S2: AsRef<str>>(actual: S1, expected: S2) {
+    let actual = actual.as_ref();
+    let expected = expected.as_ref();
     if actual != expected {
         eprintln!("=== ACTUAL TEXT ===");
         eprintln!("{}", actual);
@@ -420,7 +423,9 @@ pub fn assert_equals(actual: &str, expected: &str) {
 
 /// Check that `haystack` contains `needle`. Panic otherwise.
 #[track_caller]
-pub fn assert_contains(haystack: &str, needle: &str) {
+pub fn assert_contains<S1: AsRef<str>, S2: AsRef<str>>(haystack: S1, needle: S2) {
+    let haystack = haystack.as_ref();
+    let needle = needle.as_ref();
     if !haystack.contains(needle) {
         eprintln!("=== HAYSTACK ===");
         eprintln!("{}", haystack);
@@ -432,7 +437,9 @@ pub fn assert_contains(haystack: &str, needle: &str) {
 
 /// Check that `haystack` does not contain `needle`. Panic otherwise.
 #[track_caller]
-pub fn assert_not_contains(haystack: &str, needle: &str) {
+pub fn assert_not_contains<S1: AsRef<str>, S2: AsRef<str>>(haystack: S1, needle: S2) {
+    let haystack = haystack.as_ref();
+    let needle = needle.as_ref();
     if haystack.contains(needle) {
         eprintln!("=== HAYSTACK ===");
         eprintln!("{}", haystack);
@@ -524,11 +531,12 @@ macro_rules! impl_common_helpers {
             /// Generic command arguments provider. Prefer specific helper methods if possible.
             /// Note that for some executables, arguments might be platform specific. For C/C++
             /// compilers, arguments might be platform *and* compiler specific.
-            pub fn args<S>(&mut self, args: &[S]) -> &mut Self
+            pub fn args<V, S>(&mut self, args: V) -> &mut Self
             where
+                V: AsRef<[S]>,
                 S: AsRef<::std::ffi::OsStr>,
             {
-                self.cmd.args(args);
+                self.cmd.args(args.as_ref());
                 self
             }
 
diff --git a/src/tools/run-make-support/src/llvm.rs b/src/tools/run-make-support/src/llvm.rs
index 7f42223bf7f..4c9e9a53230 100644
--- a/src/tools/run-make-support/src/llvm.rs
+++ b/src/tools/run-make-support/src/llvm.rs
@@ -180,6 +180,13 @@ impl LlvmFilecheck {
         self.cmd.arg(path.as_ref());
         self
     }
+
+    /// `--input-file` option.
+    pub fn input_file<P: AsRef<Path>>(&mut self, input_file: P) -> &mut Self {
+        self.cmd.arg("--input-file");
+        self.cmd.arg(input_file.as_ref());
+        self
+    }
 }
 
 impl LlvmObjdump {
diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs
index 28ece1dff12..3f23c1b8f9e 100644
--- a/src/tools/run-make-support/src/rustc.rs
+++ b/src/tools/run-make-support/src/rustc.rs
@@ -106,6 +106,17 @@ impl Rustc {
         self
     }
 
+    /// Remap source path prefixes in all output.
+    pub fn remap_path_prefix<P: AsRef<Path>>(&mut self, from: P, to: P) -> &mut Self {
+        let from = from.as_ref().to_string_lossy();
+        let to = to.as_ref().to_string_lossy();
+
+        self.cmd.arg("--remap-path-prefix");
+        self.cmd.arg(format!("{from}={to}"));
+
+        self
+    }
+
     /// Specify path to the input file.
     pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
         self.cmd.arg(path.as_ref());
@@ -190,7 +201,8 @@ impl Rustc {
     }
 
     /// Specify the target triple, or a path to a custom target json spec file.
-    pub fn target(&mut self, target: &str) -> &mut Self {
+    pub fn target<S: AsRef<str>>(&mut self, target: S) -> &mut Self {
+        let target = target.as_ref();
         self.cmd.arg(format!("--target={target}"));
         self
     }
diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs
index fb00427b1c1..2be962ad888 100644
--- a/src/tools/run-make-support/src/rustdoc.rs
+++ b/src/tools/run-make-support/src/rustdoc.rs
@@ -104,7 +104,8 @@ impl Rustdoc {
     }
 
     /// Specify the target triple, or a path to a custom target json spec file.
-    pub fn target(&mut self, target: &str) -> &mut Self {
+    pub fn target<S: AsRef<str>>(&mut self, target: S) -> &mut Self {
+        let target = target.as_ref();
         self.cmd.arg(format!("--target={target}"));
         self
     }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
index c92d4e78ffa..7755a9b9748 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
@@ -49,7 +49,6 @@ pub const DEFAULT_LINTS: &[Lint] = &[
         label: "bindings_with_variant_name",
         description: r##"detects pattern bindings with the same name as one of the matched variants"##,
     },
-    Lint { label: "box_pointers", description: r##"use of owned (Box type) heap memory"## },
     Lint {
         label: "break_with_label_and_loop",
         description: r##"`break` expression with label and unlabeled loop as value expression"##,
diff --git a/src/tools/rustc-perf-wrapper/Cargo.toml b/src/tools/rustc-perf-wrapper/Cargo.toml
new file mode 100644
index 00000000000..416bfef41d7
--- /dev/null
+++ b/src/tools/rustc-perf-wrapper/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "rustc-perf-wrapper"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+clap = { version = "4.5.7", features = ["derive", "env"] }
diff --git a/src/tools/rustc-perf-wrapper/README.md b/src/tools/rustc-perf-wrapper/README.md
new file mode 100644
index 00000000000..d7655459a2f
--- /dev/null
+++ b/src/tools/rustc-perf-wrapper/README.md
@@ -0,0 +1,3 @@
+# rustc-perf wrapper
+Utility tool for invoking [`rustc-perf`](https://github.com/rust-lang/rustc-perf) for benchmarking/profiling
+a stage1/2 compiler built by bootstrap using `x perf -- <command>`.
diff --git a/src/tools/rustc-perf-wrapper/src/config.rs b/src/tools/rustc-perf-wrapper/src/config.rs
new file mode 100644
index 00000000000..a88abfe4723
--- /dev/null
+++ b/src/tools/rustc-perf-wrapper/src/config.rs
@@ -0,0 +1,45 @@
+use std::fmt::{Display, Formatter};
+
+#[derive(Clone, Copy, Debug, clap::ValueEnum)]
+#[value(rename_all = "PascalCase")]
+pub enum Profile {
+    Check,
+    Debug,
+    Doc,
+    Opt,
+    Clippy,
+}
+
+impl Display for Profile {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        let name = match self {
+            Profile::Check => "Check",
+            Profile::Debug => "Debug",
+            Profile::Doc => "Doc",
+            Profile::Opt => "Opt",
+            Profile::Clippy => "Clippy",
+        };
+        f.write_str(name)
+    }
+}
+
+#[derive(Clone, Copy, Debug, clap::ValueEnum)]
+#[value(rename_all = "PascalCase")]
+pub enum Scenario {
+    Full,
+    IncrFull,
+    IncrUnchanged,
+    IncrPatched,
+}
+
+impl Display for Scenario {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        let name = match self {
+            Scenario::Full => "Full",
+            Scenario::IncrFull => "IncrFull",
+            Scenario::IncrUnchanged => "IncrUnchanged",
+            Scenario::IncrPatched => "IncrPatched",
+        };
+        f.write_str(name)
+    }
+}
diff --git a/src/tools/rustc-perf-wrapper/src/main.rs b/src/tools/rustc-perf-wrapper/src/main.rs
new file mode 100644
index 00000000000..1c0d1745f3d
--- /dev/null
+++ b/src/tools/rustc-perf-wrapper/src/main.rs
@@ -0,0 +1,130 @@
+use crate::config::{Profile, Scenario};
+use clap::Parser;
+use std::path::PathBuf;
+use std::process::Command;
+
+mod config;
+
+/// Performs profiling or benchmarking with [`rustc-perf`](https://github.com/rust-lang/rustc-perf)
+/// using a locally built compiler.
+#[derive(Debug, clap::Parser)]
+// Hide arguments from BuildContext in the default usage string.
+// Clap does not seem to have a way of disabling the usage of these arguments.
+#[clap(override_usage = "rustc-perf-wrapper [OPTIONS] <COMMAND>")]
+pub struct Args {
+    #[clap(subcommand)]
+    cmd: PerfCommand,
+
+    #[clap(flatten)]
+    opts: SharedOpts,
+
+    #[clap(flatten)]
+    ctx: BuildContext,
+}
+
+#[derive(Debug, clap::Parser)]
+enum PerfCommand {
+    /// Run `profile_local eprintln`.
+    /// This executes the compiler on the given benchmarks and stores its stderr output.
+    Eprintln,
+    /// Run `profile_local samply`
+    /// This executes the compiler on the given benchmarks and profiles it with `samply`.
+    /// You need to install `samply`, e.g. using `cargo install samply`.
+    Samply,
+    /// Run `profile_local cachegrind`.
+    /// This executes the compiler on the given benchmarks under `Cachegrind`.
+    Cachegrind,
+}
+
+impl PerfCommand {
+    fn is_profiling(&self) -> bool {
+        match self {
+            PerfCommand::Eprintln | PerfCommand::Samply | PerfCommand::Cachegrind => true,
+        }
+    }
+}
+
+#[derive(Debug, clap::Parser)]
+struct SharedOpts {
+    /// Select the benchmarks that you want to run (separated by commas).
+    /// If unspecified, all benchmarks will be executed.
+    #[clap(long, global = true, value_delimiter = ',')]
+    include: Vec<String>,
+    /// Select the scenarios that should be benchmarked.
+    #[clap(
+        long,
+        global = true,
+        value_delimiter = ',',
+        default_value = "Full,IncrFull,IncrUnchanged,IncrPatched"
+    )]
+    scenarios: Vec<Scenario>,
+    /// Select the profiles that should be benchmarked.
+    #[clap(long, global = true, value_delimiter = ',', default_value = "Check,Debug,Opt")]
+    profiles: Vec<Profile>,
+}
+
+/// These arguments are mostly designed to be passed from bootstrap, not by users
+/// directly.
+#[derive(Debug, clap::Parser)]
+struct BuildContext {
+    /// Compiler binary that will be benchmarked/profiled.
+    #[clap(long, hide = true, env = "RUSTC_REAL")]
+    compiler: PathBuf,
+    /// rustc-perf collector binary that will be used for running benchmarks/profilers.
+    #[clap(long, hide = true, env = "PERF_COLLECTOR")]
+    collector: PathBuf,
+    /// Directory where to store results.
+    #[clap(long, hide = true, env = "PERF_RESULT_DIR")]
+    results_dir: PathBuf,
+}
+
+fn main() {
+    let args = Args::parse();
+    run(args);
+}
+
+fn run(args: Args) {
+    let mut cmd = Command::new(args.ctx.collector);
+    match &args.cmd {
+        PerfCommand::Eprintln => {
+            cmd.arg("profile_local").arg("eprintln");
+        }
+        PerfCommand::Samply => {
+            cmd.arg("profile_local").arg("samply");
+        }
+        PerfCommand::Cachegrind => {
+            cmd.arg("profile_local").arg("cachegrind");
+        }
+    }
+    if args.cmd.is_profiling() {
+        cmd.arg("--out-dir").arg(&args.ctx.results_dir);
+    }
+
+    if !args.opts.include.is_empty() {
+        cmd.arg("--include").arg(args.opts.include.join(","));
+    }
+    if !args.opts.profiles.is_empty() {
+        cmd.arg("--profiles")
+            .arg(args.opts.profiles.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(","));
+    }
+    if !args.opts.scenarios.is_empty() {
+        cmd.arg("--scenarios")
+            .arg(args.opts.scenarios.iter().map(|p| p.to_string()).collect::<Vec<_>>().join(","));
+    }
+    cmd.arg(&args.ctx.compiler);
+
+    println!("Running `rustc-perf` using `{}`", args.ctx.compiler.display());
+
+    const MANIFEST_DIR: &str = env!("CARGO_MANIFEST_DIR");
+
+    let rustc_perf_dir = PathBuf::from(MANIFEST_DIR).join("../rustc-perf");
+
+    // We need to set the working directory to `src/tools/perf`, so that it can find the directory
+    // with compile-time benchmarks.
+    let cmd = cmd.current_dir(rustc_perf_dir);
+    cmd.status().expect("error while running rustc-perf collector");
+
+    if args.cmd.is_profiling() {
+        println!("You can find the results at `{}`", args.ctx.results_dir.display());
+    }
+}
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index a29d57d1603..276d2d694cd 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -18,7 +18,6 @@ run-make/cross-lang-lto-clang/Makefile
 run-make/cross-lang-lto-pgo-smoketest/Makefile
 run-make/cross-lang-lto-upstream-rlibs/Makefile
 run-make/cross-lang-lto/Makefile
-run-make/debug-assertions/Makefile
 run-make/dep-info-doesnt-run-much/Makefile
 run-make/dep-info-spaces/Makefile
 run-make/dep-info/Makefile
@@ -27,7 +26,6 @@ run-make/dump-mono-stats/Makefile
 run-make/dylib-chain/Makefile
 run-make/emit-path-unhashed/Makefile
 run-make/emit-shared-files/Makefile
-run-make/emit-stack-sizes/Makefile
 run-make/emit-to-stdout/Makefile
 run-make/env-dep-info/Makefile
 run-make/export-executable-symbols/Makefile
@@ -70,7 +68,6 @@ run-make/issue-37839/Makefile
 run-make/issue-40535/Makefile
 run-make/issue-47384/Makefile
 run-make/issue-47551/Makefile
-run-make/issue-68794-textrel-on-minimal-lib/Makefile
 run-make/issue-69368/Makefile
 run-make/issue-83045/Makefile
 run-make/issue-83112-incr-test-moved-file/Makefile
@@ -83,13 +80,11 @@ run-make/jobserver-error/Makefile
 run-make/libs-through-symlinks/Makefile
 run-make/libtest-json/Makefile
 run-make/libtest-junit/Makefile
-run-make/libtest-padding/Makefile
 run-make/libtest-thread-limit/Makefile
 run-make/link-cfg/Makefile
 run-make/link-framework/Makefile
 run-make/link-path-order/Makefile
 run-make/linkage-attr-on-static/Makefile
-run-make/llvm-ident/Makefile
 run-make/long-linker-command-lines-cmd-exe/Makefile
 run-make/long-linker-command-lines/Makefile
 run-make/longjmp-across-rust/Makefile
@@ -101,8 +96,6 @@ run-make/macos-fat-archive/Makefile
 run-make/manual-link/Makefile
 run-make/metadata-dep-info/Makefile
 run-make/min-global-align/Makefile
-run-make/mingw-export-call-convention/Makefile
-run-make/mismatching-target-triples/Makefile
 run-make/missing-crate-dependency/Makefile
 run-make/mixing-libs/Makefile
 run-make/msvc-opt-minsize/Makefile
@@ -115,13 +108,11 @@ run-make/obey-crate-type-flag/Makefile
 run-make/optimization-remarks-dir-pgo/Makefile
 run-make/optimization-remarks-dir/Makefile
 run-make/output-type-permutations/Makefile
-run-make/override-aliased-flags/Makefile
 run-make/panic-abort-eh_frame/Makefile
 run-make/pass-linker-flags-flavor/Makefile
 run-make/pass-linker-flags-from-dep/Makefile
 run-make/pass-linker-flags/Makefile
 run-make/pass-non-c-like-enum-to-c/Makefile
-run-make/pdb-alt-path/Makefile
 run-make/pdb-buildinfo-cl-cmd/Makefile
 run-make/pgo-gen-lto/Makefile
 run-make/pgo-gen-no-imp-symbols/Makefile
@@ -129,15 +120,12 @@ run-make/pgo-gen/Makefile
 run-make/pgo-indirect-call-promotion/Makefile
 run-make/pgo-use/Makefile
 run-make/pointer-auth-link-with-c/Makefile
-run-make/pretty-print-to-file/Makefile
-run-make/pretty-print-with-dep-file/Makefile
 run-make/print-calling-conventions/Makefile
 run-make/print-target-list/Makefile
 run-make/profile/Makefile
 run-make/prune-link-args/Makefile
 run-make/raw-dylib-alt-calling-convention/Makefile
 run-make/raw-dylib-c/Makefile
-run-make/raw-dylib-cross-compilation/Makefile
 run-make/raw-dylib-custom-dlltool/Makefile
 run-make/raw-dylib-import-name-type/Makefile
 run-make/raw-dylib-inline-cross-dylib/Makefile
@@ -145,11 +133,9 @@ run-make/raw-dylib-link-ordinal/Makefile
 run-make/raw-dylib-stdcall-ordinal/Makefile
 run-make/redundant-libs/Makefile
 run-make/remap-path-prefix-dwarf/Makefile
-run-make/remap-path-prefix/Makefile
 run-make/reproducible-build-2/Makefile
 run-make/reproducible-build/Makefile
 run-make/return-non-c-like-enum-from-c/Makefile
-run-make/return-non-c-like-enum/Makefile
 run-make/rlib-chain/Makefile
 run-make/rlib-format-packed-bundled-libs-2/Makefile
 run-make/rlib-format-packed-bundled-libs-3/Makefile
@@ -187,11 +173,5 @@ run-make/track-path-dep-info/Makefile
 run-make/track-pgo-dep-info/Makefile
 run-make/translation/Makefile
 run-make/type-mismatch-same-crate-name/Makefile
-run-make/unknown-mod-stdin/Makefile
 run-make/unstable-flag-required/Makefile
-run-make/used-cdylib-macos/Makefile
-run-make/volatile-intrinsics/Makefile
-run-make/wasm-exceptions-nostd/Makefile
-run-make/wasm-override-linker/Makefile
-run-make/weird-output-filenames/Makefile
 run-make/x86_64-fortanix-unknown-sgx-lvi/Makefile
diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs
index 1df4921ef86..8ce44115ef3 100644
--- a/src/tools/tidy/src/ext_tool_checks.rs
+++ b/src/tools/tidy/src/ext_tool_checks.rs
@@ -364,11 +364,10 @@ fn create_venv_at_path(path: &Path) -> Result<(), Error> {
 
     let stderr = String::from_utf8_lossy(&out.stderr);
     let err = if stderr.contains("No module named virtualenv") {
-        Error::Generic(
+        Error::Generic(format!(
             "virtualenv not found: you may need to install it \
-                               (`python3 -m pip install venv`)"
-                .to_owned(),
-        )
+                               (`{sys_py} -m pip install virtualenv`)"
+        ))
     } else {
         Error::Generic(format!(
             "failed to create venv at '{}' using {sys_py}: {stderr}",