From 48e7c2859b1cc3fb1205aaa86e73ff7da21bf7e6 Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Fri, 12 Jun 2026 20:56:01 +0200 Subject: [PATCH] rebuild LLVM when `bootstrap.toml` config changes --- src/bootstrap/src/core/build_steps/llvm.rs | 10 +-- src/bootstrap/src/core/builder/mod.rs | 10 +++ src/bootstrap/src/core/config/config.rs | 8 +- src/bootstrap/src/core/config/toml/llvm.rs | 87 ++++++++++++++++++++++ 4 files changed, 109 insertions(+), 6 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 087a395a067f0..9891f54907c7c 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -169,7 +169,7 @@ pub fn prebuilt_llvm_config( generate_smart_stamp_hash( builder, &builder.config.src.join("src/llvm-project"), - builder.in_tree_llvm_info.sha().unwrap_or_default(), + &builder.llvm_cache_key(), ) }); @@ -999,7 +999,7 @@ impl Step for OmpOffload { generate_smart_stamp_hash( builder, &builder.config.src.join("src/llvm-project/offload"), - builder.in_tree_llvm_info.sha().unwrap_or_default(), + &builder.llvm_cache_key(), ) }); let stamp = BuildStamp::new(&out_dir).with_prefix("offload").add_stamp(smart_stamp_hash); @@ -1166,8 +1166,8 @@ impl Step for Enzyme { // Enzyme links against LLVM. If we update the LLVM submodule libLLVM might get a new // version number, in which case Enzyme will now fail to find LLVM. By including the LLVM // hash into the Enzyme hash we force a rebuild of Enzyme when updating LLVM. - let enzyme_hash_input = builder.in_tree_llvm_info.sha().unwrap_or_default().to_owned() - + builder.enzyme_info.sha().unwrap_or_default(); + let enzyme_hash_input = + builder.llvm_cache_key() + builder.enzyme_info.sha().unwrap_or_default(); static STAMP_HASH_MEMO: OnceLock = OnceLock::new(); let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| { @@ -1430,7 +1430,7 @@ impl Step for Sanitizers { generate_smart_stamp_hash( builder, &builder.config.src.join("src/llvm-project/compiler-rt"), - builder.in_tree_llvm_info.sha().unwrap_or_default(), + &builder.llvm_cache_key(), ) }); diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index 2e640220efd8e..166ab171631a6 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -1686,6 +1686,16 @@ Alternatively, you can set `build.local-rebuild=true` and use a stage0 compiler pub fn exec_ctx(&self) -> &ExecutionContext { &self.config.exec_ctx } + + /// When to rebuild LLVM. Currently includes the LLVM commit hash and the configuration from + /// bootstrap.toml. + pub(crate) fn llvm_cache_key(&self) -> String { + format!( + "sha={sha}\nkey={key}", + sha = self.in_tree_llvm_info.sha().unwrap_or_default(), + key = self.config.llvm_cache_key, + ) + } } /// Return qualified step name, e.g. `compile::Rustc`. diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 5a9c7264c006f..0297907057ef9 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -156,6 +156,9 @@ pub struct Config { pub backtrace_on_ice: bool, // llvm codegen options + /// Key used to decide when to rebuild LLVM. + pub llvm_cache_key: String, + pub llvm_assertions: bool, pub llvm_tests: bool, pub llvm_enzyme: bool, @@ -595,6 +598,8 @@ impl Config { rustflags: rust_rustflags, } = toml.rust.unwrap_or_default(); + let llvm = toml.llvm.unwrap_or_default(); + let llvm_cache_key = llvm.cache_key(); let Llvm { optimize: llvm_optimize, thin_lto: llvm_thin_lto, @@ -625,7 +630,7 @@ impl Config { enable_warnings: llvm_enable_warnings, download_ci_llvm: llvm_download_ci_llvm, build_config: llvm_build_config, - } = toml.llvm.unwrap_or_default(); + } = llvm; let Dist { sign_folder: dist_sign_folder, @@ -1401,6 +1406,7 @@ impl Config { llvm_assertions, llvm_bitcode_linker_enabled: rust_llvm_bitcode_linker.unwrap_or(false), llvm_build_config: llvm_build_config.clone().unwrap_or(Default::default()), + llvm_cache_key, llvm_cflags, llvm_clang: llvm_clang.unwrap_or(false), llvm_clang_cl, diff --git a/src/bootstrap/src/core/config/toml/llvm.rs b/src/bootstrap/src/core/config/toml/llvm.rs index 5f08884e4ef71..e54c17c63aaae 100644 --- a/src/bootstrap/src/core/config/toml/llvm.rs +++ b/src/bootstrap/src/core/config/toml/llvm.rs @@ -43,6 +43,93 @@ define_config! { } } +impl Llvm { + /// A key that is used to determine whether LLVM should be rebuilt. + pub(crate) fn cache_key(&self) -> String { + let helper = || { + let mut key = String::with_capacity(512); + + let Self { + optimize, + thin_lto, + release_debuginfo, + assertions, + tests, + enzyme, + plugins, + static_libstdcpp, + libzstd, + ninja, + targets, + experimental_targets, + link_jobs: _, + link_shared, + version_suffix, + clang_cl, + cflags, + cxxflags, + ldflags, + use_libcxx, + use_linker, + allow_old_toolchain, + offload, + polly, + offload_clang_dir, + clang, + enable_warnings: _, + build_config, + download_ci_llvm: _, + } = self; + + use std::fmt::Write; + write!(key, "{:?}", optimize)?; + write!(key, "{:?}", thin_lto)?; + write!(key, "{:?}", release_debuginfo)?; + write!(key, "{:?}", assertions)?; + write!(key, "{:?}", tests)?; + write!(key, "{:?}", enzyme)?; + write!(key, "{:?}", plugins)?; + write!(key, "{:?}", static_libstdcpp)?; + write!(key, "{:?}", libzstd)?; + write!(key, "{:?}", ninja)?; + write!(key, "{:?}", targets)?; + write!(key, "{:?}", experimental_targets)?; + write!(key, "{:?}", link_shared)?; + write!(key, "{:?}", version_suffix)?; + write!(key, "{:?}", clang_cl)?; + write!(key, "{:?}", cflags)?; + write!(key, "{:?}", cxxflags)?; + write!(key, "{:?}", ldflags)?; + write!(key, "{:?}", use_libcxx)?; + write!(key, "{:?}", use_linker)?; + write!(key, "{:?}", allow_old_toolchain)?; + write!(key, "{:?}", offload)?; + write!(key, "{:?}", polly)?; + write!(key, "{:?}", offload_clang_dir)?; + write!(key, "{:?}", clang)?; + + match build_config { + None => { + write!(key, "None")?; + } + Some(c) => { + let mut build_config = c.iter().collect::>(); + build_config.sort(); + + for (k, v) in build_config { + write!(key, "{}: {}", k, v)?; + } + } + } + + Ok::<_, std::fmt::Error>(key) + }; + + // write! to a String always succeeds unless OOM. + helper().unwrap() + } +} + /// Compares the current `Llvm` options against those in the CI LLVM builder and detects any incompatible options. /// It does this by destructuring the `Llvm` instance to make sure every `Llvm` field is covered and not missing. #[cfg(not(test))]