Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions bootstrap.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
12 changes: 9 additions & 3 deletions src/bootstrap/src/core/build_steps/compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand Down Expand Up @@ -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_DEFAULT_LINKER_SELF_CONTAINED_LLD_CC", "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 {
Expand Down
33 changes: 28 additions & 5 deletions src/bootstrap/src/core/builder/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -782,17 +784,38 @@ 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"
[build] llvm <host>
[build] rustc 0 <host> -> rustc 1 <host>
[build] rustc 0 <host> -> LldWrapper 1 <host>
");
});
},
);
}

#[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 <host>
[build] rustc 0 <host> -> rustc 1 <host>
");
},
);
}

#[test]
Expand Down
94 changes: 70 additions & 24 deletions src/bootstrap/src/core/config/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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, TomlTarget};
use crate::core::config::{
CompilerBuiltins, DebuginfoLevel, DryRun, GccCiMode, LlvmLibunwind, Merge, ReplaceOpt,
RustcLto, SplitDebuginfo, StringOrBool, threads_from_config,
Expand Down Expand Up @@ -829,6 +831,11 @@ 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 {
Expand All @@ -837,6 +844,7 @@ impl Config {
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,
Expand All @@ -860,6 +868,33 @@ impl Config {

let mut target = Target::from_triple(&triple);

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!(
Expand Down Expand Up @@ -893,6 +928,7 @@ impl Config {
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);
Expand All @@ -918,6 +954,38 @@ impl Config {
}
}

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
None => {
lld_enabled = true;
Some(DefaultLinuxLinkerOverride::SelfContainedLldCc)
}
// 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 llvm_from_ci = parse_download_ci_llvm(
&dwn_ctx,
&rust_info,
Expand All @@ -926,28 +994,6 @@ impl Config {
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)
};

if llvm_from_ci {
let warn = |option: &str| {
println!(
Expand Down
25 changes: 0 additions & 25 deletions src/bootstrap/src/core/config/toml/rust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,28 +438,3 @@ pub(crate) fn parse_codegen_backends(
}
found_backends
}

#[cfg(not(test))]
pub fn default_lld_opt_in_targets() -> Vec<String> {
vec!["x86_64-unknown-linux-gnu".to_string()]
}

#[cfg(test)]
thread_local! {
static TEST_LLD_OPT_IN_TARGETS: std::cell::RefCell<Option<Vec<String>>> = std::cell::RefCell::new(None);
}

#[cfg(test)]
pub fn default_lld_opt_in_targets() -> Vec<String> {
TEST_LLD_OPT_IN_TARGETS.with(|cell| cell.borrow().clone()).unwrap_or_default()
}

#[cfg(test)]
pub fn with_lld_opt_in_targets<R>(targets: Vec<String>, 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
})
}
62 changes: 62 additions & 0 deletions src/bootstrap/src/core/config/toml/target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand All @@ -24,6 +27,7 @@ define_config! {
ar: Option<String> = "ar",
ranlib: Option<String> = "ranlib",
default_linker: Option<PathBuf> = "default-linker",
default_linker_linux: Option<DefaultLinuxLinkerOverride> = "default-linker-linux-override",
linker: Option<String> = "linker",
split_debuginfo: Option<String> = "split-debuginfo",
llvm_config: Option<String> = "llvm-config",
Expand Down Expand Up @@ -60,6 +64,7 @@ pub struct Target {
pub ar: Option<PathBuf>,
pub ranlib: Option<PathBuf>,
pub default_linker: Option<PathBuf>,
pub default_linker_linux_override: DefaultLinuxLinkerOverride,
pub linker: Option<PathBuf>,
pub split_debuginfo: Option<SplitDebuginfo>,
pub sanitizers: Option<bool>,
Expand Down Expand Up @@ -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<D>(deserializer: D) -> Result<Self, D::Error>
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<String, DefaultLinuxLinkerOverride> {
[("x86_64-unknown-linux-gnu".to_string(), DefaultLinuxLinkerOverride::SelfContainedLldCc)]
.into()
}

#[cfg(test)]
thread_local! {
static TEST_LINUX_LINKER_OVERRIDES: std::cell::RefCell<Option<HashMap<String, DefaultLinuxLinkerOverride>>> = std::cell::RefCell::new(None);
}

#[cfg(test)]
pub fn default_linux_linker_overrides() -> HashMap<String, DefaultLinuxLinkerOverride> {
TEST_LINUX_LINKER_OVERRIDES.with(|cell| cell.borrow().clone()).unwrap_or_default()
}

#[cfg(test)]
pub fn with_default_linux_linker_overrides<R>(
targets: HashMap<String, DefaultLinuxLinkerOverride>,
f: impl FnOnce() -> R,
) -> R {
TEST_LINUX_LINKER_OVERRIDES.with(|cell| {
let prev = cell.replace(Some(targets));
let result = f();
cell.replace(prev);
result
})
}