diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 89abf976776..11e789a996a 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -870,7 +870,7 @@ impl Step for Rustc { // is already on by default in MSVC optimized builds, which is interpreted as --icf=all: // https://github.com/llvm/llvm-project/blob/3329cec2f79185bafd678f310fafadba2a8c76d2/lld/COFF/Driver.cpp#L1746 // https://github.com/rust-lang/rust/blob/f22819bcce4abaff7d1246a56eec493418f9f4ee/compiler/rustc_codegen_ssa/src/back/linker.rs#L827 - if builder.config.use_lld && !compiler.host.is_msvc() { + if builder.config.lld_mode.is_used() && !compiler.host.is_msvc() { cargo.rustflag("-Clink-args=-Wl,--icf=all"); } diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 23a9b6a2f11..ade7d83f8b2 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -105,6 +105,39 @@ impl Display for DebuginfoLevel { } } +/// LLD in bootstrap works like this: +/// - Self-contained lld: use `rust-lld` from the compiler's sysroot +/// - External: use an external `lld` binary +/// +/// It is configured depending on the target: +/// 1) Everything except MSVC +/// - Self-contained: -Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker +/// - External: -Clinker-flavor=gnu-lld-cc +/// 2) MSVC +/// - Self-contained: -Clinker= +/// - External: -Clinker=lld +#[derive(Default, Clone)] +pub enum LldMode { + /// Do not use LLD + #[default] + Unused, + /// Use `rust-lld` from the compiler's sysroot + SelfContained, + /// Use an externally provided `lld` binary. + /// Note that the linker name cannot be overridden, the binary has to be named `lld` and it has + /// to be in $PATH. + External, +} + +impl LldMode { + pub fn is_used(&self) -> bool { + match self { + LldMode::SelfContained | LldMode::External => true, + LldMode::Unused => false, + } + } +} + /// Global configuration for the entire build and/or bootstrap. /// /// This structure is parsed from `config.toml`, and some of the fields are inferred from `git` or build-time parameters. @@ -199,7 +232,7 @@ pub struct Config { pub llvm_from_ci: bool, pub llvm_build_config: HashMap, - pub use_lld: bool, + pub lld_mode: LldMode, pub lld_enabled: bool, pub llvm_tools_enabled: bool, @@ -981,6 +1014,44 @@ enum StringOrInt<'a> { String(&'a str), Int(i64), } + +impl<'de> Deserialize<'de> for LldMode { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + struct LldModeVisitor; + + impl<'de> serde::de::Visitor<'de> for LldModeVisitor { + type Value = LldMode; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("one of true, 'self-contained' or 'external'") + } + + fn visit_bool(self, v: bool) -> Result + where + E: serde::de::Error, + { + Ok(if v { LldMode::External } else { LldMode::Unused }) + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + match v { + "external" => Ok(LldMode::External), + "self-contained" => Ok(LldMode::SelfContained), + _ => Err(E::custom("unknown mode {v}")), + } + } + } + + deserializer.deserialize_any(LldModeVisitor) + } +} + define_config! { /// TOML representation of how the Rust build is configured. struct Rust { @@ -1018,7 +1089,7 @@ define_config! { save_toolstates: Option = "save-toolstates", codegen_backends: Option> = "codegen-backends", lld: Option = "lld", - use_lld: Option = "use-lld", + lld_mode: Option = "use-lld", llvm_tools: Option = "llvm-tools", deny_warnings: Option = "deny-warnings", backtrace_on_ice: Option = "backtrace-on-ice", @@ -1446,7 +1517,7 @@ impl Config { if let Some(true) = rust.incremental { config.incremental = true; } - set(&mut config.use_lld, rust.use_lld); + set(&mut config.lld_mode, rust.lld_mode); set(&mut config.lld_enabled, rust.lld); set(&mut config.llvm_tools_enabled, rust.llvm_tools); config.rustc_parallel = rust diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs index 5442ce97a8d..fd0b61eae8d 100644 --- a/src/bootstrap/src/lib.rs +++ b/src/bootstrap/src/lib.rs @@ -37,7 +37,7 @@ use utils::channel::GitInfo; use crate::core::builder; use crate::core::builder::Kind; -use crate::core::config::flags; +use crate::core::config::{flags, LldMode}; use crate::core::config::{DryRun, Target}; use crate::core::config::{LlvmLibunwind, TargetSelection}; use crate::utils::cache::{Interned, INTERNER}; @@ -1258,17 +1258,24 @@ impl Build { && !target.is_msvc() { Some(self.cc(target)) - } else if self.config.use_lld && !self.is_fuse_ld_lld(target) && self.build == target { - Some(self.initial_lld.clone()) + } else if self.config.lld_mode.is_used() + && self.is_lld_direct_linker(target) + && self.build == target + { + match self.config.lld_mode { + LldMode::SelfContained => Some(self.initial_lld.clone()), + LldMode::External => Some("lld".into()), + LldMode::Unused => None, + } } else { None } } - // LLD is used through `-fuse-ld=lld` rather than directly. + // Is LLD configured directly through `-Clinker`? // Only MSVC targets use LLD directly at the moment. - fn is_fuse_ld_lld(&self, target: TargetSelection) -> bool { - self.config.use_lld && !target.is_msvc() + fn is_lld_direct_linker(&self, target: TargetSelection) -> bool { + target.is_msvc() } /// Returns if this target should statically link the C runtime, if specified diff --git a/src/bootstrap/src/tests/config.rs b/src/bootstrap/src/tests/config.rs index a8106ca8ff9..c24d57fb8f8 100644 --- a/src/bootstrap/src/tests/config.rs +++ b/src/bootstrap/src/tests/config.rs @@ -1,5 +1,5 @@ -use crate::core::config::TomlConfig; use super::{Config, Flags}; +use crate::core::config::{LldMode, TomlConfig}; use clap::CommandFactory; use serde::Deserialize; @@ -217,3 +217,12 @@ fn verify_file_integrity() { remove_file(tempfile).unwrap(); } + +#[test] +fn rust_lld() { + assert!(matches!(parse("").lld_mode, LldMode::Unused)); + assert!(matches!(parse("rust.use-lld = \"self-contained\"").lld_mode, LldMode::SelfContained)); + assert!(matches!(parse("rust.use-lld = \"external\"").lld_mode, LldMode::External)); + assert!(matches!(parse("rust.use-lld = true").lld_mode, LldMode::External)); + assert!(matches!(parse("rust.use-lld = false").lld_mode, LldMode::Unused)); +} diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs index eeed9c87438..2da556f0a2d 100644 --- a/src/bootstrap/src/utils/helpers.rs +++ b/src/bootstrap/src/utils/helpers.rs @@ -499,7 +499,7 @@ pub fn linker_flags( lld_threads: LldThreads, ) -> Vec { let mut args = vec![]; - if builder.is_fuse_ld_lld(target) { + if !builder.is_lld_direct_linker(target) && builder.config.lld_mode.is_used() { args.push(String::from("-Clink-arg=-fuse-ld=lld")); if matches!(lld_threads, LldThreads::No) {