diff --git a/bootstrap.example.toml b/bootstrap.example.toml index f623a3db0029d..6f37e51a47de2 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -758,12 +758,8 @@ # Currently, the only standard options supported here are `"llvm"`, `"cranelift"` and `"gcc"`. #rust.codegen-backends = ["llvm"] -# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, and -# whether to set it as rustc's default linker on `x86_64-unknown-linux-gnu`. This will also only be -# when *not* building an external LLVM (so only when using `download-ci-llvm` or building LLVM from -# the in-tree source): setting `llvm-config` in the `[target.x86_64-unknown-linux-gnu]` section will -# make this default to false. -#rust.lld = false in all cases, except on `x86_64-unknown-linux-gnu` as described above, where it is true +# Indicates whether LLD will be compiled and made available in the sysroot for rustc to execute, +#rust.lld = false, except for targets that opt into LLD (see `target.default-linker-linux-override`) # Indicates if we should override the linker used to link Rust crates during bootstrap to be LLD. # If set to `true` or `"external"`, a global `lld` binary that has to be in $PATH @@ -1067,3 +1063,17 @@ # 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. #jemalloc = rust.jemalloc (bool) + +# The linker configuration that will *override* the default linker used for Linux +# targets in the built compiler. +# +# The following values are supported: +# - `off` => do not apply any override and use the default linker. This can be used to opt out of +# linker overrides set by bootstrap for specific targets (see below). +# - `self-contained-lld-cc` => override the default linker to be self-contained LLD (`rust-lld`) +# that is invoked through `cc`. +# +# Currently, the following targets automatically opt into the self-contained LLD linker, unless you +# pass `off`: +# - x86_64-unknown-linux-gnu +#default-linker-linux-override = "off" (for most targets) diff --git a/compiler/rustc_target/src/spec/base/linux_gnu.rs b/compiler/rustc_target/src/spec/base/linux_gnu.rs index e9135b43ebc1f..2fcd8c61a9c62 100644 --- a/compiler/rustc_target/src/spec/base/linux_gnu.rs +++ b/compiler/rustc_target/src/spec/base/linux_gnu.rs @@ -1,5 +1,14 @@ -use crate::spec::{TargetOptions, base}; +use crate::spec::{Cc, LinkerFlavor, Lld, TargetOptions, base}; pub(crate) fn opts() -> TargetOptions { - TargetOptions { env: "gnu".into(), ..base::linux::opts() } + let mut base = TargetOptions { env: "gnu".into(), ..base::linux::opts() }; + + // When we're asked to use the `rust-lld` linker by default, set the appropriate lld-using + // linker flavor, and self-contained linker component. + if option_env!("CFG_DEFAULT_LINKER_SELF_CONTAINED_LLD_CC").is_some() { + base.linker_flavor = LinkerFlavor::Gnu(Cc::Yes, Lld::Yes); + base.link_self_contained = crate::spec::LinkSelfContainedDefault::with_linker(); + } + + base } diff --git a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs index 0c8353fad182f..59bb7f5962101 100644 --- a/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs +++ b/compiler/rustc_target/src/spec/targets/x86_64_unknown_linux_gnu.rs @@ -20,13 +20,6 @@ pub(crate) fn target() -> Target { | SanitizerSet::THREAD; base.supports_xray = true; - // When we're asked to use the `rust-lld` linker by default, set the appropriate lld-using - // linker flavor, and self-contained linker component. - if option_env!("CFG_USE_SELF_CONTAINED_LINKER").is_some() { - base.linker_flavor = LinkerFlavor::Gnu(Cc::Yes, Lld::Yes); - base.link_self_contained = crate::spec::LinkSelfContainedDefault::with_linker(); - } - Target { llvm_target: "x86_64-unknown-linux-gnu".into(), metadata: TargetMetadata { diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index d29d1041486bd..dd90ac7cdda31 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -26,6 +26,7 @@ use crate::core::builder; use crate::core::builder::{ Builder, Cargo, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description, }; +use crate::core::config::toml::target::DefaultLinuxLinkerOverride; use crate::core::config::{ CompilerBuiltins, DebuginfoLevel, LlvmLibunwind, RustcLto, TargetSelection, }; @@ -1355,9 +1356,14 @@ pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetS cargo.env("CFG_DEFAULT_LINKER", s); } - // Enable rustc's env var for `rust-lld` when requested. - if builder.config.lld_enabled { - cargo.env("CFG_USE_SELF_CONTAINED_LINKER", "1"); + // Enable rustc's env var to use a linker override on Linux when requested. + if let Some(linker) = target_config.map(|c| c.default_linker_linux_override) { + match linker { + DefaultLinuxLinkerOverride::Off => {} + DefaultLinuxLinkerOverride::SelfContainedLldCc => { + cargo.env("CFG_DEFAULT_LINKER_SELF_CONTAINED_LLD_CC", "1"); + } + } } if builder.config.rust_verify_llvm_ir { diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs index d0429387f824e..e0eb38d04aad0 100644 --- a/src/bootstrap/src/core/builder/tests.rs +++ b/src/bootstrap/src/core/builder/tests.rs @@ -514,7 +514,9 @@ mod snapshot { }; use crate::core::builder::{Builder, Kind, StepDescription, StepMetadata}; use crate::core::config::TargetSelection; - use crate::core::config::toml::rust::with_lld_opt_in_targets; + use crate::core::config::toml::target::{ + DefaultLinuxLinkerOverride, with_default_linux_linker_overrides, + }; use crate::utils::cache::Cache; use crate::utils::helpers::get_host_target; use crate::utils::tests::{ConfigBuilder, TestCtx}; @@ -782,9 +784,11 @@ mod snapshot { #[test] fn build_compiler_lld_opt_in() { - with_lld_opt_in_targets(vec![host_target()], || { - let ctx = TestCtx::new(); - insta::assert_snapshot!( + with_default_linux_linker_overrides( + [(host_target(), DefaultLinuxLinkerOverride::SelfContainedLldCc)].into(), + || { + let ctx = TestCtx::new(); + insta::assert_snapshot!( ctx.config("build") .path("compiler") .render_steps(), @r" @@ -792,7 +796,26 @@ mod snapshot { [build] rustc 0 -> rustc 1 [build] rustc 0 -> LldWrapper 1 "); - }); + }, + ); + } + + #[test] + fn build_compiler_lld_opt_in_lld_disabled() { + with_default_linux_linker_overrides( + [(host_target(), DefaultLinuxLinkerOverride::SelfContainedLldCc)].into(), + || { + let ctx = TestCtx::new(); + insta::assert_snapshot!( + ctx.config("build") + .path("compiler") + .args(&["--set", "rust.lld=false"]) + .render_steps(), @r" + [build] llvm + [build] rustc 0 -> rustc 1 + "); + }, + ); } #[test] diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 1fcc1174e856c..02dd3f0c05c50 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -42,9 +42,11 @@ use crate::core::config::toml::install::Install; use crate::core::config::toml::llvm::Llvm; use crate::core::config::toml::rust::{ BootstrapOverrideLld, Rust, RustOptimize, check_incompatible_options_for_ci_rustc, - default_lld_opt_in_targets, parse_codegen_backends, + parse_codegen_backends, +}; +use crate::core::config::toml::target::{ + DefaultLinuxLinkerOverride, Target, TomlTarget, default_linux_linker_overrides, }; -use crate::core::config::toml::target::Target; use crate::core::config::{ CompilerBuiltins, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt, RustcLto, SplitDebuginfo, StringOrBool, threads_from_config, @@ -829,11 +831,71 @@ impl Config { .to_owned(); } + let mut lld_enabled = rust_lld_enabled.unwrap_or(false); + + // Linux targets for which the user explicitly overrode the used linker + let mut targets_with_user_linker_override = HashSet::new(); + if let Some(t) = toml.target { for (triple, cfg) in t { + let TomlTarget { + cc: target_cc, + cxx: target_cxx, + ar: target_ar, + ranlib: target_ranlib, + default_linker: target_default_linker, + default_linker_linux: target_default_linker_linux_override, + linker: target_linker, + split_debuginfo: target_split_debuginfo, + llvm_config: target_llvm_config, + llvm_has_rust_patches: target_llvm_has_rust_patches, + llvm_filecheck: target_llvm_filecheck, + llvm_libunwind: target_llvm_libunwind, + sanitizers: target_sanitizers, + profiler: target_profiler, + rpath: target_rpath, + crt_static: target_crt_static, + musl_root: target_musl_root, + musl_libdir: target_musl_libdir, + wasi_root: target_wasi_root, + qemu_rootfs: target_qemu_rootfs, + no_std: target_no_std, + codegen_backends: target_codegen_backends, + runner: target_runner, + optimized_compiler_builtins: target_optimized_compiler_builtins, + jemalloc: target_jemalloc, + } = cfg; + let mut target = Target::from_triple(&triple); - if let Some(ref s) = cfg.llvm_config { + if target_default_linker_linux_override.is_some() { + targets_with_user_linker_override.insert(triple.clone()); + } + + let default_linker_linux_override = match target_default_linker_linux_override { + Some(DefaultLinuxLinkerOverride::SelfContainedLldCc) => { + if rust_default_linker.is_some() { + panic!( + "cannot set both `default-linker` and `default-linker-linux` for target `{triple}`" + ); + } + if !triple.contains("linux-gnu") { + panic!( + "`default-linker-linux` can only be set for Linux GNU targets, not for `{triple}`" + ); + } + if !lld_enabled { + panic!( + "Trying to override the default Linux linker for `{triple}` to be self-contained LLD, but LLD is not being built. Enable it with rust.lld = true." + ); + } + DefaultLinuxLinkerOverride::SelfContainedLldCc + } + Some(DefaultLinuxLinkerOverride::Off) => DefaultLinuxLinkerOverride::Off, + None => DefaultLinuxLinkerOverride::default(), + }; + + if let Some(ref s) = target_llvm_config { if download_rustc_commit.is_some() && triple == *host_target.triple { panic!( "setting llvm_config for the host is incompatible with download-rustc" @@ -841,46 +903,48 @@ impl Config { } target.llvm_config = Some(src.join(s)); } - if let Some(patches) = cfg.llvm_has_rust_patches { + if let Some(patches) = target_llvm_has_rust_patches { assert!( - build_submodules == Some(false) || cfg.llvm_config.is_some(), + build_submodules == Some(false) || target_llvm_config.is_some(), "use of `llvm-has-rust-patches` is restricted to cases where either submodules are disabled or llvm-config been provided" ); target.llvm_has_rust_patches = Some(patches); } - if let Some(ref s) = cfg.llvm_filecheck { + if let Some(ref s) = target_llvm_filecheck { target.llvm_filecheck = Some(src.join(s)); } - target.llvm_libunwind = cfg.llvm_libunwind.as_ref().map(|v| { + target.llvm_libunwind = target_llvm_libunwind.as_ref().map(|v| { v.parse().unwrap_or_else(|_| { panic!("failed to parse target.{triple}.llvm-libunwind") }) }); - if let Some(s) = cfg.no_std { + if let Some(s) = target_no_std { target.no_std = s; } - target.cc = cfg.cc.map(PathBuf::from); - target.cxx = cfg.cxx.map(PathBuf::from); - target.ar = cfg.ar.map(PathBuf::from); - target.ranlib = cfg.ranlib.map(PathBuf::from); - target.linker = cfg.linker.map(PathBuf::from); - target.crt_static = cfg.crt_static; - target.musl_root = cfg.musl_root.map(PathBuf::from); - target.musl_libdir = cfg.musl_libdir.map(PathBuf::from); - target.wasi_root = cfg.wasi_root.map(PathBuf::from); - target.qemu_rootfs = cfg.qemu_rootfs.map(PathBuf::from); - target.runner = cfg.runner; - target.sanitizers = cfg.sanitizers; - target.profiler = cfg.profiler; - target.rpath = cfg.rpath; - target.optimized_compiler_builtins = cfg.optimized_compiler_builtins; - target.jemalloc = cfg.jemalloc; - if let Some(backends) = cfg.codegen_backends { + target.cc = target_cc.map(PathBuf::from); + target.cxx = target_cxx.map(PathBuf::from); + target.ar = target_ar.map(PathBuf::from); + target.ranlib = target_ranlib.map(PathBuf::from); + target.linker = target_linker.map(PathBuf::from); + target.crt_static = target_crt_static; + target.default_linker = target_default_linker; + target.default_linker_linux_override = default_linker_linux_override; + target.musl_root = target_musl_root.map(PathBuf::from); + target.musl_libdir = target_musl_libdir.map(PathBuf::from); + target.wasi_root = target_wasi_root.map(PathBuf::from); + target.qemu_rootfs = target_qemu_rootfs.map(PathBuf::from); + target.runner = target_runner; + target.sanitizers = target_sanitizers; + target.profiler = target_profiler; + target.rpath = target_rpath; + target.optimized_compiler_builtins = target_optimized_compiler_builtins; + target.jemalloc = target_jemalloc; + if let Some(backends) = target_codegen_backends { target.codegen_backends = Some(parse_codegen_backends(backends, &format!("target.{triple}"))) } - target.split_debuginfo = cfg.split_debuginfo.as_ref().map(|v| { + target.split_debuginfo = target_split_debuginfo.as_ref().map(|v| { v.parse().unwrap_or_else(|_| { panic!("invalid value for target.{triple}.split-debuginfo") }) @@ -897,28 +961,8 @@ impl Config { llvm_download_ci_llvm, llvm_assertions, ); - - // We make `x86_64-unknown-linux-gnu` use the self-contained linker by default, so we will - // build our internal lld and use it as the default linker, by setting the `rust.lld` config - // to true by default: - // - on the `x86_64-unknown-linux-gnu` target - // - when building our in-tree llvm (i.e. the target has not set an `llvm-config`), so that - // we're also able to build the corresponding lld - // - or when using an external llvm that's downloaded from CI, which also contains our prebuilt - // lld - // - otherwise, we'd be using an external llvm, and lld would not necessarily available and - // thus, disabled - // - similarly, lld will not be built nor used by default when explicitly asked not to, e.g. - // when the config sets `rust.lld = false` - let lld_enabled = if default_lld_opt_in_targets().contains(&host_target.triple.to_string()) - && hosts == [host_target] - { - let no_llvm_config = - target_config.get(&host_target).is_none_or(|config| config.llvm_config.is_none()); - rust_lld_enabled.unwrap_or(llvm_from_ci || no_llvm_config) - } else { - rust_lld_enabled.unwrap_or(false) - }; + let is_host_system_llvm = + is_system_llvm(&target_config, llvm_from_ci, host_target, host_target); if llvm_from_ci { let warn = |option: &str| { @@ -966,6 +1010,41 @@ impl Config { build_target.llvm_filecheck = Some(ci_llvm_bin.join(exe("FileCheck", host_target))); } + for (target, linker_override) in default_linux_linker_overrides() { + // If the user overrode the default Linux linker, do not apply bootstrap defaults + if targets_with_user_linker_override.contains(&target) { + continue; + } + + let default_linux_linker_override = match linker_override { + DefaultLinuxLinkerOverride::Off => continue, + DefaultLinuxLinkerOverride::SelfContainedLldCc => { + // If we automatically default to the self-contained LLD linker, + // we also need to handle the rust.lld option. + match rust_lld_enabled { + // If LLD was not enabled explicitly, we enable it, unless LLVM config has + // been set + None if !is_host_system_llvm => { + lld_enabled = true; + Some(DefaultLinuxLinkerOverride::SelfContainedLldCc) + } + None => None, + // If it was enabled already, we don't need to do anything + Some(true) => Some(DefaultLinuxLinkerOverride::SelfContainedLldCc), + // If it was explicitly disabled, we do not apply the + // linker override + Some(false) => None, + } + } + }; + if let Some(linker_override) = default_linux_linker_override { + target_config + .entry(TargetSelection::from_user(&target)) + .or_default() + .default_linker_linux_override = linker_override; + } + } + let initial_rustfmt = build_rustfmt.or_else(|| maybe_download_rustfmt(&dwn_ctx, &out)); if matches!(bootstrap_override_lld, BootstrapOverrideLld::SelfContained) @@ -977,7 +1056,7 @@ impl Config { ); } - if lld_enabled && is_system_llvm(&dwn_ctx, &target_config, llvm_from_ci, host_target) { + if lld_enabled && is_host_system_llvm { panic!("Cannot enable LLD with `rust.lld = true` when using external llvm-config."); } @@ -1771,8 +1850,7 @@ impl Config { /// /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. pub fn is_system_llvm(&self, target: TargetSelection) -> bool { - let dwn_ctx = DownloadContext::from(self); - is_system_llvm(dwn_ctx, &self.target_config, self.llvm_from_ci, target) + is_system_llvm(&self.target_config, self.llvm_from_ci, self.host_target, target) } /// Returns `true` if this is our custom, patched, version of LLVM. @@ -2375,16 +2453,15 @@ pub fn submodules_(submodules: &Option, rust_info: &channel::GitInfo) -> b /// In particular, we expect llvm sources to be available when this is false. /// /// NOTE: this is not the same as `!is_rust_llvm` when `llvm_has_patches` is set. -pub fn is_system_llvm<'a>( - dwn_ctx: impl AsRef>, +pub fn is_system_llvm( target_config: &HashMap, llvm_from_ci: bool, + host_target: TargetSelection, target: TargetSelection, ) -> bool { - let dwn_ctx = dwn_ctx.as_ref(); match target_config.get(&target) { Some(Target { llvm_config: Some(_), .. }) => { - let ci_llvm = llvm_from_ci && is_host_target(&dwn_ctx.host_target, &target); + let ci_llvm = llvm_from_ci && is_host_target(&host_target, &target); !ci_llvm } // We're building from the in-tree src/llvm-project sources. diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index ca9e0d0bc98e7..5a2c6e1698698 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -438,28 +438,3 @@ pub(crate) fn parse_codegen_backends( } found_backends } - -#[cfg(not(test))] -pub fn default_lld_opt_in_targets() -> Vec { - vec!["x86_64-unknown-linux-gnu".to_string()] -} - -#[cfg(test)] -thread_local! { - static TEST_LLD_OPT_IN_TARGETS: std::cell::RefCell>> = std::cell::RefCell::new(None); -} - -#[cfg(test)] -pub fn default_lld_opt_in_targets() -> Vec { - TEST_LLD_OPT_IN_TARGETS.with(|cell| cell.borrow().clone()).unwrap_or_default() -} - -#[cfg(test)] -pub fn with_lld_opt_in_targets(targets: Vec, f: impl FnOnce() -> R) -> R { - TEST_LLD_OPT_IN_TARGETS.with(|cell| { - let prev = cell.replace(Some(targets)); - let result = f(); - cell.replace(prev); - result - }) -} diff --git a/src/bootstrap/src/core/config/toml/target.rs b/src/bootstrap/src/core/config/toml/target.rs index 020602e6a1997..7b8d4f9601b53 100644 --- a/src/bootstrap/src/core/config/toml/target.rs +++ b/src/bootstrap/src/core/config/toml/target.rs @@ -9,6 +9,9 @@ //! * [`Target`]: This struct represents the processed and validated configuration for a //! build target, which is is stored in the main `Config` structure. +use std::collections::HashMap; + +use serde::de::Error; use serde::{Deserialize, Deserializer}; use crate::core::config::{ @@ -24,6 +27,7 @@ define_config! { ar: Option = "ar", ranlib: Option = "ranlib", default_linker: Option = "default-linker", + default_linker_linux: Option = "default-linker-linux-override", linker: Option = "linker", split_debuginfo: Option = "split-debuginfo", llvm_config: Option = "llvm-config", @@ -60,6 +64,7 @@ pub struct Target { pub ar: Option, pub ranlib: Option, pub default_linker: Option, + pub default_linker_linux_override: DefaultLinuxLinkerOverride, pub linker: Option, pub split_debuginfo: Option, pub sanitizers: Option, @@ -89,3 +94,60 @@ impl Target { target } } + +/// Overrides the default linker used on a Linux linker. +/// On Linux, the linker is usually invoked through `cc`, therefore this exists as a separate +/// configuration from simply setting `default-linker`, which corresponds to `-Clinker`. +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)] +pub enum DefaultLinuxLinkerOverride { + /// Do not apply any override and use the default linker for the given target. + #[default] + Off, + /// Use the self-contained `rust-lld` linker, invoked through `cc`. + /// Corresponds to `-Clinker-features=+lld -Clink-self-contained=+linker`. + SelfContainedLldCc, +} + +impl<'de> Deserialize<'de> for DefaultLinuxLinkerOverride { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let name = String::deserialize(deserializer)?; + match name.as_str() { + "off" => Ok(Self::Off), + "self-contained-lld-cc" => Ok(Self::SelfContainedLldCc), + other => Err(D::Error::unknown_variant(other, &["off", "self-contained-lld-cc"])), + } + } +} + +/// Set of linker overrides for selected Linux targets. +#[cfg(not(test))] +pub fn default_linux_linker_overrides() -> HashMap { + [("x86_64-unknown-linux-gnu".to_string(), DefaultLinuxLinkerOverride::SelfContainedLldCc)] + .into() +} + +#[cfg(test)] +thread_local! { + static TEST_LINUX_LINKER_OVERRIDES: std::cell::RefCell>> = std::cell::RefCell::new(None); +} + +#[cfg(test)] +pub fn default_linux_linker_overrides() -> HashMap { + TEST_LINUX_LINKER_OVERRIDES.with(|cell| cell.borrow().clone()).unwrap_or_default() +} + +#[cfg(test)] +pub fn with_default_linux_linker_overrides( + targets: HashMap, + f: impl FnOnce() -> R, +) -> R { + TEST_LINUX_LINKER_OVERRIDES.with(|cell| { + let prev = cell.replace(Some(targets)); + let result = f(); + cell.replace(prev); + result + }) +} diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index 853fc4e6623e9..ea5fc77c8a4a8 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -566,4 +566,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "`compiletest` is now always built with the stage 0 compiler, so `build.compiletest-use-stage0-libtest` has no effect.", }, + ChangeInfo { + change_id: 147157, + severity: ChangeSeverity::Warning, + summary: "`rust.lld = true` no longer automatically causes the `x86_64-unknown-linux-gnu` target to default into using the self-contained LLD linker. This target now uses the LLD linker by default. To opt out, set `target.x86_64-unknown-linux-gnu.default-linker-linux-override = 'off'`.", + }, ];