about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Murphy <paumurph@redhat.com>2025-06-24 11:07:54 -0500
committerPaul Murphy <murp@redhat.com>2025-08-25 16:08:35 -0500
commit64cbe52849fdfb9ff097dd311be826b00d27ba45 (patch)
treee56cca6a5f7c39b6ed2872acab07180b08b0329e
parent54c581243c977c7662c949b5795c31147f8a60ed (diff)
downloadrust-64cbe52849fdfb9ff097dd311be826b00d27ba45.tar.gz
rust-64cbe52849fdfb9ff097dd311be826b00d27ba45.zip
Allow linking a prebuilt optimized compiler-rt builtins library
Extend the <target>.optimized-compiler-builtins bootstrap option to accept a
path to a prebuilt compiler-rt builtins library, and update compiler-builtins
to enable optimized builtins without building compiler-rt builtins.
-rw-r--r--bootstrap.example.toml10
-rw-r--r--library/compiler-builtins/compiler-builtins/README.md10
-rw-r--r--library/compiler-builtins/compiler-builtins/build.rs55
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs44
-rw-r--r--src/bootstrap/src/core/config/config.rs10
-rw-r--r--src/bootstrap/src/core/config/tests.rs4
-rw-r--r--src/bootstrap/src/core/config/toml/target.rs4
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
8 files changed, 105 insertions, 37 deletions
diff --git a/bootstrap.example.toml b/bootstrap.example.toml
index 4c18d5f8675..ebab2e14828 100644
--- a/bootstrap.example.toml
+++ b/bootstrap.example.toml
@@ -1041,13 +1041,15 @@
 #runner = <none> (string)
 
 # Use the optimized LLVM C intrinsics for `compiler_builtins`, rather than Rust intrinsics
-# on this target.
-# Requires the LLVM submodule to be managed by bootstrap (i.e. not external) so that `compiler-rt`
-# sources are available.
+# on this target. Choosing true requires the LLVM submodule to be managed by bootstrap
+# (i.e. not external) so that `compiler-rt` sources are available.
+#
+# Setting this to a path removes the requirement for a C toolchain, but requires setting the
+# path to an existing library containing the builtins library from LLVM's compiler-rt.
 #
 # Setting this to `false` generates slower code, but removes the requirement for a C toolchain in
 # order to run `x check`.
-#optimized-compiler-builtins = build.optimized-compiler-builtins (bool)
+#optimized-compiler-builtins = build.optimized-compiler-builtins (bool or path)
 
 # Link the compiler and LLVM against `jemalloc` instead of the default libc allocator.
 # This overrides the global `rust.jemalloc` option. See that option for more info.
diff --git a/library/compiler-builtins/compiler-builtins/README.md b/library/compiler-builtins/compiler-builtins/README.md
index 387b70c0499..2d92b7651f9 100644
--- a/library/compiler-builtins/compiler-builtins/README.md
+++ b/library/compiler-builtins/compiler-builtins/README.md
@@ -10,6 +10,16 @@ to be added as an explicit dependency in `Cargo.toml`.
 
 [`compiler-rt`]: https://github.com/llvm/llvm-project/tree/1b1dc505057322f4fa1110ef4f53c44347f52986/compiler-rt
 
+## Configuration
+
+`compiler-builtins` can be configured with the following environment variables when the `c` feature
+is enabled:
+
+- `LLVM_COMPILER_RT_LIB`
+- `RUST_COMPILER_RT_ROOT`
+
+See `build.rs` for details.
+
 ## Contributing
 
 See [CONTRIBUTING.md](CONTRIBUTING.md).
diff --git a/library/compiler-builtins/compiler-builtins/build.rs b/library/compiler-builtins/compiler-builtins/build.rs
index 43b978606e5..6e1d230e3cd 100644
--- a/library/compiler-builtins/compiler-builtins/build.rs
+++ b/library/compiler-builtins/compiler-builtins/build.rs
@@ -540,12 +540,20 @@ mod c {
             sources.extend(&[("__emutls_get_address", "emutls.c")]);
         }
 
+        // Optionally, link against a prebuilt llvm compiler-rt containing the builtins
+        // library. Only the builtins library is required. On many platforms, this is
+        // available as a library named libclang_rt.builtins.a.
+        let link_against_prebuilt_rt = env::var_os("LLVM_COMPILER_RT_LIB").is_some();
+
         // When compiling the C code we require the user to tell us where the
         // source code is, and this is largely done so when we're compiling as
         // part of rust-lang/rust we can use the same llvm-project repository as
         // rust-lang/rust.
         let root = match env::var_os("RUST_COMPILER_RT_ROOT") {
             Some(s) => PathBuf::from(s),
+            // If a prebuild libcompiler-rt is provided, set a valid
+            // path to simplify later logic. Nothing should be compiled.
+            None if link_against_prebuilt_rt => PathBuf::new(),
             None => {
                 panic!(
                     "RUST_COMPILER_RT_ROOT is not set. You may need to run \
@@ -553,7 +561,7 @@ mod c {
                 );
             }
         };
-        if !root.exists() {
+        if !link_against_prebuilt_rt && !root.exists() {
             panic!("RUST_COMPILER_RT_ROOT={} does not exist", root.display());
         }
 
@@ -569,7 +577,7 @@ mod c {
         let src_dir = root.join("lib/builtins");
         if target.arch == "aarch64" && target.env != "msvc" && target.os != "uefi" {
             // See below for why we're building these as separate libraries.
-            build_aarch64_out_of_line_atomics_libraries(&src_dir, cfg);
+            build_aarch64_out_of_line_atomics_libraries(&src_dir, cfg, link_against_prebuilt_rt);
 
             // Some run-time CPU feature detection is necessary, as well.
             let cpu_model_src = if src_dir.join("cpu_model.c").exists() {
@@ -583,20 +591,45 @@ mod c {
         let mut added_sources = HashSet::new();
         for (sym, src) in sources.map.iter() {
             let src = src_dir.join(src);
-            if added_sources.insert(src.clone()) {
+            if !link_against_prebuilt_rt && added_sources.insert(src.clone()) {
                 cfg.file(&src);
                 println!("cargo:rerun-if-changed={}", src.display());
             }
             println!("cargo:rustc-cfg={}=\"optimized-c\"", sym);
         }
 
-        cfg.compile("libcompiler-rt.a");
+        if link_against_prebuilt_rt {
+            let rt_builtins_ext = PathBuf::from(env::var_os("LLVM_COMPILER_RT_LIB").unwrap());
+            if !rt_builtins_ext.exists() {
+                panic!(
+                    "LLVM_COMPILER_RT_LIB={} does not exist",
+                    rt_builtins_ext.display()
+                );
+            }
+            if let Some(dir) = rt_builtins_ext.parent() {
+                println!("cargo::rustc-link-search=native={}", dir.display());
+            }
+            if let Some(lib) = rt_builtins_ext.file_name() {
+                println!(
+                    "cargo::rustc-link-lib=static:+verbatim={}",
+                    lib.to_str().unwrap()
+                );
+            }
+        } else {
+            cfg.compile("libcompiler-rt.a");
+        }
     }
 
-    fn build_aarch64_out_of_line_atomics_libraries(builtins_dir: &Path, cfg: &mut cc::Build) {
+    fn build_aarch64_out_of_line_atomics_libraries(
+        builtins_dir: &Path,
+        cfg: &mut cc::Build,
+        link_against_prebuilt_rt: bool,
+    ) {
         let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
         let outlined_atomics_file = builtins_dir.join("aarch64").join("lse.S");
-        println!("cargo:rerun-if-changed={}", outlined_atomics_file.display());
+        if !link_against_prebuilt_rt {
+            println!("cargo:rerun-if-changed={}", outlined_atomics_file.display());
+        }
 
         cfg.include(&builtins_dir);
 
@@ -609,6 +642,13 @@ mod c {
                 for (model_number, model_name) in
                     &[(1, "relax"), (2, "acq"), (3, "rel"), (4, "acq_rel")]
                 {
+                    let sym = format!("__aarch64_{}{}_{}", instruction_type, size, model_name);
+                    println!("cargo:rustc-cfg={}=\"optimized-c\"", sym);
+
+                    if link_against_prebuilt_rt {
+                        continue;
+                    }
+
                     // The original compiler-rt build system compiles the same
                     // source file multiple times with different compiler
                     // options. Here we do something slightly different: we
@@ -632,9 +672,6 @@ mod c {
                     .unwrap();
                     drop(file);
                     cfg.file(path);
-
-                    let sym = format!("__aarch64_{}{}_{}", instruction_type, size, model_name);
-                    println!("cargo:rustc-cfg={}=\"optimized-c\"", sym);
                 }
             }
         }
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 8cad5b920b9..005c849c47a 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -575,25 +575,31 @@ pub fn std_cargo(builder: &Builder<'_>, target: TargetSelection, cargo: &mut Car
     // `compiler-builtins` crate is enabled and it's configured to learn where
     // `compiler-rt` is located.
     let compiler_builtins_c_feature = if builder.config.optimized_compiler_builtins(target) {
-        // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce `submodules = false`, so this is a no-op.
-        // But, the user could still decide to manually use an in-tree submodule.
-        //
-        // NOTE: if we're using system llvm, we'll end up building a version of `compiler-rt` that doesn't match the LLVM we're linking to.
-        // That's probably ok? At least, the difference wasn't enforced before. There's a comment in
-        // the compiler_builtins build script that makes me nervous, though:
-        // https://github.com/rust-lang/compiler-builtins/blob/31ee4544dbe47903ce771270d6e3bea8654e9e50/build.rs#L575-L579
-        builder.require_submodule(
-            "src/llvm-project",
-            Some(
-                "The `build.optimized-compiler-builtins` config option \
-                 requires `compiler-rt` sources from LLVM.",
-            ),
-        );
-        let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt");
-        assert!(compiler_builtins_root.exists());
-        // The path to `compiler-rt` is also used by `profiler_builtins` (above),
-        // so if you're changing something here please also change that as appropriate.
-        cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root);
+        if let Some(path) = builder.config.optimized_compiler_builtins_path(target) {
+            cargo.env("LLVM_COMPILER_RT_LIB", path);
+        } else {
+            // NOTE: this interacts strangely with `llvm-has-rust-patches`. In that case, we enforce
+            // `submodules = false`, so this is a no-op. But, the user could still decide to
+            //  manually use an in-tree submodule.
+            //
+            // NOTE: if we're using system llvm, we'll end up building a version of `compiler-rt`
+            // that doesn't match the LLVM we're linking to. That's probably ok? At least, the
+            // difference wasn't enforced before. There's a comment in the compiler_builtins build
+            // script that makes me nervous, though:
+            // https://github.com/rust-lang/compiler-builtins/blob/31ee4544dbe47903ce771270d6e3bea8654e9e50/build.rs#L575-L579
+            builder.require_submodule(
+                "src/llvm-project",
+                Some(
+                    "The `build.optimized-compiler-builtins` config option \
+                     requires `compiler-rt` sources from LLVM.",
+                ),
+            );
+            let compiler_builtins_root = builder.src.join("src/llvm-project/compiler-rt");
+            assert!(compiler_builtins_root.exists());
+            // The path to `compiler-rt` is also used by `profiler_builtins` (above),
+            // so if you're changing something here please also change that as appropriate.
+            cargo.env("RUST_COMPILER_RT_ROOT", &compiler_builtins_root);
+        }
         " compiler-builtins-c"
     } else {
         ""
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 05c2579ac08..09a1eb5b847 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -1675,10 +1675,18 @@ impl Config {
     pub fn optimized_compiler_builtins(&self, target: TargetSelection) -> bool {
         self.target_config
             .get(&target)
-            .and_then(|t| t.optimized_compiler_builtins)
+            .and_then(|t| t.optimized_compiler_builtins.as_ref())
+            .map(StringOrBool::is_string_or_true)
             .unwrap_or(self.optimized_compiler_builtins)
     }
 
+    pub fn optimized_compiler_builtins_path(&self, target: TargetSelection) -> Option<&str> {
+        match self.target_config.get(&target)?.optimized_compiler_builtins.as_ref()? {
+            StringOrBool::String(s) => Some(s),
+            StringOrBool::Bool(_) => None,
+        }
+    }
+
     pub fn llvm_enabled(&self, target: TargetSelection) -> bool {
         self.enabled_codegen_backends(target).contains(&CodegenBackendKind::Llvm)
     }
diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs
index 50eba12aba7..c32e4384cf6 100644
--- a/src/bootstrap/src/core/config/tests.rs
+++ b/src/bootstrap/src/core/config/tests.rs
@@ -17,7 +17,7 @@ use crate::core::build_steps::clippy::{LintConfig, get_clippy_rules_in_order};
 use crate::core::build_steps::llvm;
 use crate::core::build_steps::llvm::LLVM_INVALIDATION_PATHS;
 use crate::core::config::toml::TomlConfig;
-use crate::core::config::{LldMode, Target, TargetSelection};
+use crate::core::config::{LldMode, StringOrBool, Target, TargetSelection};
 use crate::utils::tests::git::git_test;
 
 pub(crate) fn parse(config: &str) -> Config {
@@ -212,7 +212,7 @@ runner = "x86_64-runner"
     let darwin = TargetSelection::from_user("aarch64-apple-darwin");
     let darwin_values = Target {
         runner: Some("apple".into()),
-        optimized_compiler_builtins: Some(false),
+        optimized_compiler_builtins: Some(StringOrBool::Bool(false)),
         ..Default::default()
     };
     assert_eq!(
diff --git a/src/bootstrap/src/core/config/toml/target.rs b/src/bootstrap/src/core/config/toml/target.rs
index 2c06fd083a8..3236b5cdbaa 100644
--- a/src/bootstrap/src/core/config/toml/target.rs
+++ b/src/bootstrap/src/core/config/toml/target.rs
@@ -39,7 +39,7 @@ define_config! {
         no_std: Option<bool> = "no-std",
         codegen_backends: Option<Vec<String>> = "codegen-backends",
         runner: Option<String> = "runner",
-        optimized_compiler_builtins: Option<bool> = "optimized-compiler-builtins",
+        optimized_compiler_builtins: Option<StringOrBool> = "optimized-compiler-builtins",
         jemalloc: Option<bool> = "jemalloc",
     }
 }
@@ -71,7 +71,7 @@ pub struct Target {
     pub runner: Option<String>,
     pub no_std: bool,
     pub codegen_backends: Option<Vec<CodegenBackendKind>>,
-    pub optimized_compiler_builtins: Option<bool>,
+    pub optimized_compiler_builtins: Option<StringOrBool>,
     pub jemalloc: Option<bool>,
 }
 
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index 54f563fe68c..073954e9337 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -521,4 +521,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Warning,
         summary: "It is no longer possible to `x dist` or `x install` with stage 0. All dist and install commands have to be on stage 1+.",
     },
+    ChangeInfo {
+        change_id: 143689,
+        severity: ChangeSeverity::Info,
+        summary: "The `optimized-compiler-builtins` option now accepts a path to an existing compiler-rt builtins library.",
+    },
 ];