Skip to content

Commit 75d5a96

Browse files
committed
Generalize usage of LLD in bootstrap
Allow choosing between self-contained and externally provided LLD.
1 parent 8f1e28f commit 75d5a96

File tree

8 files changed

+132
-53
lines changed

8 files changed

+132
-53
lines changed

src/bootstrap/bin/rustc.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,23 @@ fn main() {
115115
if let Ok(host_linker) = env::var("RUSTC_HOST_LINKER") {
116116
cmd.arg(format!("-Clinker={host_linker}"));
117117
}
118-
if env::var_os("RUSTC_HOST_FUSE_LD_LLD").is_some() {
119-
cmd.arg("-Clink-args=-fuse-ld=lld");
118+
if let Ok(lld) = env::var("RUSTC_HOST_LLD") {
119+
match lld.as_str() {
120+
"external" => {
121+
cmd.arg("-Clink-args=-fuse-ld=lld");
122+
}
123+
"self-contained" => {
124+
let mut arg = std::ffi::OsString::from("-Clink-arg=-B");
125+
arg.push(
126+
env::var_os("RUSTC_HOST_LLD_ROOT")
127+
.expect("RUSTC_HOST_LLD_ROOT env. var missing"),
128+
);
129+
cmd.arg(arg);
130+
131+
cmd.arg("-Clink-args=-fuse-ld=lld");
132+
}
133+
_ => panic!("Unknown host lld value {lld}"),
134+
}
120135
}
121136

122137
if let Ok(s) = env::var("RUSTC_HOST_CRT_STATIC") {

src/bootstrap/builder.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use std::process::Command;
1313
use std::time::{Duration, Instant};
1414

1515
use crate::cache::{Cache, Interned, INTERNER};
16-
use crate::config::{DryRun, SplitDebuginfo, TargetSelection};
16+
use crate::config::{DryRun, LldMode, SplitDebuginfo, TargetSelection};
1717
use crate::doc;
1818
use crate::flags::{Color, Subcommand};
1919
use crate::install;
@@ -1654,20 +1654,28 @@ impl<'a> Builder<'a> {
16541654
if let Some(host_linker) = self.linker(compiler.host) {
16551655
cargo.env("RUSTC_HOST_LINKER", host_linker);
16561656
}
1657-
if self.is_fuse_ld_lld(compiler.host) {
1658-
cargo.env("RUSTC_HOST_FUSE_LD_LLD", "1");
1657+
1658+
if !self.is_lld_direct_linker(compiler.host) {
1659+
match self.config.lld_mode {
1660+
LldMode::External => {
1661+
cargo.env("RUSTC_HOST_LLD", "external");
1662+
}
1663+
LldMode::SelfContained => {
1664+
cargo.env("RUSTC_HOST_LLD", "self-contained");
1665+
cargo.env("RUSTC_HOST_LLD_ROOT", self.initial_lld_root());
1666+
}
1667+
LldMode::Unused => {}
1668+
}
16591669
}
16601670

16611671
if let Some(target_linker) = self.linker(target) {
16621672
let target = crate::envify(&target.triple);
16631673
cargo.env(&format!("CARGO_TARGET_{target}_LINKER"), target_linker);
16641674
}
1665-
if self.is_fuse_ld_lld(target) {
1666-
rustflags.arg("-Clink-args=-fuse-ld=lld");
1667-
}
1668-
self.lld_flags(target).for_each(|flag| {
1675+
for flag in self.lld_flags(target, false) {
1676+
rustflags.arg(&flag);
16691677
rustdocflags.arg(&flag);
1670-
});
1678+
}
16711679

16721680
if !(["build", "check", "clippy", "fix", "rustc"].contains(&cmd)) && want_rustdoc {
16731681
cargo.env("RUSTDOC_LIBDIR", self.rustc_libdir(compiler));

src/bootstrap/compile.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -822,7 +822,7 @@ impl Step for Rustc {
822822
// is already on by default in MSVC optimized builds, which is interpreted as --icf=all:
823823
// https://github.com/llvm/llvm-project/blob/3329cec2f79185bafd678f310fafadba2a8c76d2/lld/COFF/Driver.cpp#L1746
824824
// https://github.com/rust-lang/rust/blob/f22819bcce4abaff7d1246a56eec493418f9f4ee/compiler/rustc_codegen_ssa/src/back/linker.rs#L827
825-
if builder.config.use_lld && !compiler.host.is_msvc() {
825+
if builder.config.lld_mode.is_used() && !compiler.host.is_msvc() {
826826
cargo.rustflag("-Clink-args=-Wl,--icf=all");
827827
}
828828

src/bootstrap/config.rs

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,17 @@ impl Display for DebuginfoLevel {
102102
}
103103
}
104104

105+
/// LLD in bootstrap works like this:
106+
/// - Self-contained lld: use `rust-lld` from the compiler's sysroot
107+
/// - External: use an external `lld` binary
108+
///
109+
/// It is configured depending on the target:
110+
/// 1) Everything except MSVC
111+
/// - Self-contained: -Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker
112+
/// - External: -Clinker-favor=gnu-lld-cc
113+
/// 2) MSVC
114+
/// - Self-contained: -Clinker=<path to rust-lld>
115+
/// - External: -Clinker=lld
105116
#[derive(Default, Clone)]
106117
pub enum LldMode {
107118
/// Do not use LLD
@@ -115,6 +126,15 @@ pub enum LldMode {
115126
External,
116127
}
117128

129+
impl LldMode {
130+
pub fn is_used(&self) -> bool {
131+
match self {
132+
LldMode::SelfContained | LldMode::External => true,
133+
LldMode::Unused => false,
134+
}
135+
}
136+
}
137+
118138
/// Global configuration for the entire build and/or bootstrap.
119139
///
120140
/// This structure is parsed from `config.toml`, and some of the fields are inferred from `git` or build-time parameters.
@@ -206,7 +226,7 @@ pub struct Config {
206226
pub llvm_from_ci: bool,
207227
pub llvm_build_config: HashMap<String, String>,
208228

209-
pub use_lld: bool,
229+
pub lld_mode: LldMode,
210230
pub lld_enabled: bool,
211231
pub llvm_tools_enabled: bool,
212232

@@ -1462,13 +1482,7 @@ impl Config {
14621482
if let Some(true) = rust.incremental {
14631483
config.incremental = true;
14641484
}
1465-
set(
1466-
&mut config.use_lld,
1467-
match rust.lld_mode {
1468-
None | Some(LldMode::Unused) => None,
1469-
_ => Some(true),
1470-
},
1471-
);
1485+
set(&mut config.lld_mode, rust.lld_mode);
14721486
set(&mut config.lld_enabled, rust.lld);
14731487
set(&mut config.llvm_tools_enabled, rust.llvm_tools);
14741488
config.rustc_parallel = rust.parallel_compiler.unwrap_or(false);

src/bootstrap/config/tests.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::config::TomlConfig;
1+
use crate::config::{LldMode, TomlConfig};
22

33
use super::{Config, Flags};
44
use clap::CommandFactory;
@@ -199,8 +199,9 @@ fn invalid_rust_optimize() {
199199

200200
#[test]
201201
fn rust_lld() {
202-
assert!(!parse("").use_lld);
203-
assert!(parse("rust.use-lld = \"self-contained\"").use_lld);
204-
assert!(parse("rust.use-lld = \"lld\"").use_lld);
205-
assert!(parse("rust.use-lld = true").use_lld);
202+
assert!(matches!(parse("").lld_mode, LldMode::Unused));
203+
assert!(matches!(parse("rust.use-lld = \"self-contained\"").lld_mode, LldMode::SelfContained));
204+
assert!(matches!(parse("rust.use-lld = \"lld\"").lld_mode, LldMode::External));
205+
assert!(matches!(parse("rust.use-lld = true").lld_mode, LldMode::External));
206+
assert!(matches!(parse("rust.use-lld = false").lld_mode, LldMode::Unused));
206207
}

src/bootstrap/lib.rs

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ use filetime::FileTime;
3434
use once_cell::sync::OnceCell;
3535

3636
use crate::builder::Kind;
37-
use crate::config::{LlvmLibunwind, TargetSelection};
37+
use crate::config::{LldMode, LlvmLibunwind, TargetSelection};
3838
use crate::util::{
3939
dir_is_empty, exe, libdir, mtime, output, run, run_suppressed, symlink_dir, try_run_suppressed,
4040
};
@@ -1242,6 +1242,7 @@ impl Build {
12421242
if self.config.dry_run() {
12431243
return Some(PathBuf::new());
12441244
}
1245+
12451246
if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.clone())
12461247
{
12471248
Some(linker)
@@ -1252,32 +1253,72 @@ impl Build {
12521253
} else if target != self.config.build && util::use_host_linker(target) && !target.is_msvc()
12531254
{
12541255
Some(self.cc(target))
1255-
} else if self.config.use_lld && !self.is_fuse_ld_lld(target) && self.build == target {
1256-
Some(self.initial_lld.clone())
1256+
} else if self.config.lld_mode.is_used()
1257+
&& self.is_lld_direct_linker(target)
1258+
&& self.build == target
1259+
{
1260+
match self.config.lld_mode {
1261+
LldMode::SelfContained => Some(self.initial_lld.clone()),
1262+
LldMode::External => Some("lld".into()),
1263+
LldMode::Unused => None,
1264+
}
12571265
} else {
12581266
None
12591267
}
12601268
}
12611269

1262-
// LLD is used through `-fuse-ld=lld` rather than directly.
1270+
// Is LLD configured directly through `-Clinker`?
12631271
// Only MSVC targets use LLD directly at the moment.
1264-
fn is_fuse_ld_lld(&self, target: TargetSelection) -> bool {
1265-
self.config.use_lld && !target.is_msvc()
1272+
fn is_lld_direct_linker(&self, target: TargetSelection) -> bool {
1273+
target.is_msvc()
12661274
}
12671275

1268-
fn lld_flags(&self, target: TargetSelection) -> impl Iterator<Item = String> {
1269-
let mut options = [None, None];
1276+
// Returns the gcc-ld directory of the snapshot compiler's rust-lld.
1277+
fn initial_lld_root(&self) -> PathBuf {
1278+
let mut rust_lld_path = self.initial_lld.clone();
1279+
rust_lld_path.pop();
1280+
rust_lld_path.join("gcc-ld")
1281+
}
12701282

1271-
if self.config.use_lld {
1272-
if self.is_fuse_ld_lld(target) {
1273-
options[0] = Some("-Clink-arg=-fuse-ld=lld".to_string());
1274-
}
1283+
fn lld_flags(&self, target: TargetSelection, test: bool) -> Vec<String> {
1284+
let mut flags = vec![];
1285+
1286+
if !self.is_lld_direct_linker(target) {
1287+
match self.config.lld_mode {
1288+
LldMode::SelfContained => {
1289+
// FIXME: replace with MCP510 (-Clinker-flavor + -Clink-self-contained)
1290+
// once gcc-ld is available in stage0-sysroot.
1291+
flags.push("-Clink-arg=-fuse-ld=lld".to_string());
1292+
flags.push(format!("-Clink-arg=-B{}", self.initial_lld_root().display()));
1293+
}
1294+
LldMode::External => {
1295+
flags.push("-Clink-arg=-fuse-ld=lld".to_string());
1296+
}
1297+
LldMode::Unused => {}
1298+
};
1299+
}
12751300

1276-
let no_threads = util::lld_flag_no_threads(target.contains("windows"));
1277-
options[1] = Some(format!("-Clink-arg=-Wl,{no_threads}"));
1301+
// For tests we want to use only a single thread.
1302+
// If we use an external LLD, we don't know if it's new enough to support the required
1303+
// threads flag. Therefore we invoke it to find it out.
1304+
// The self-contained lld should always be new enough.
1305+
if test {
1306+
let new_flags = if let LldMode::External = self.config.lld_mode {
1307+
util::is_lld_newer_than_10()
1308+
} else {
1309+
true
1310+
};
1311+
1312+
let flag = match (new_flags, target.contains("windows")) {
1313+
(true, true) => "/threads:1",
1314+
(true, false) => "--threads=1",
1315+
(false, true) => "/no-threads",
1316+
(false, false) => "--no-threads",
1317+
};
1318+
flags.push(format!("-Clink-arg=-Wl,{flag}"));
12781319
}
12791320

1280-
IntoIterator::into_iter(options).flatten()
1321+
flags
12811322
}
12821323

12831324
/// Returns if this target should statically link the C runtime, if specified

src/bootstrap/test.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -856,12 +856,12 @@ impl Step for RustdocTheme {
856856
if let Some(linker) = builder.linker(self.compiler.host) {
857857
cmd.env("RUSTDOC_LINKER", linker);
858858
}
859-
if builder.is_fuse_ld_lld(self.compiler.host) {
860-
cmd.env(
861-
"RUSTDOC_LLD_NO_THREADS",
862-
util::lld_flag_no_threads(self.compiler.host.contains("windows")),
863-
);
864-
}
859+
// if builder.is_fuse_ld_lld(self.compiler.host) {
860+
// cmd.env(
861+
// "RUSTDOC_LLD_NO_THREADS",
862+
// util::lld_flag_no_threads(self.compiler.host.contains("windows")),
863+
// );
864+
// }
865865
builder.run_delaying_failure(&mut cmd);
866866
}
867867
}
@@ -1637,14 +1637,14 @@ note: if you're sure you want to do this, please open an issue as to why. In the
16371637

16381638
let mut hostflags = flags.clone();
16391639
hostflags.push(format!("-Lnative={}", builder.test_helpers_out(compiler.host).display()));
1640-
hostflags.extend(builder.lld_flags(compiler.host));
1640+
hostflags.extend(builder.lld_flags(compiler.host, true));
16411641
for flag in hostflags {
16421642
cmd.arg("--host-rustcflags").arg(flag);
16431643
}
16441644

16451645
let mut targetflags = flags;
16461646
targetflags.push(format!("-Lnative={}", builder.test_helpers_out(target).display()));
1647-
targetflags.extend(builder.lld_flags(target));
1647+
targetflags.extend(builder.lld_flags(target, true));
16481648
for flag in targetflags {
16491649
cmd.arg("--target-rustcflags").arg(flag);
16501650
}

src/bootstrap/util.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -479,17 +479,17 @@ pub fn get_clang_cl_resource_dir(clang_cl_path: &str) -> PathBuf {
479479
clang_rt_dir.to_path_buf()
480480
}
481481

482-
pub fn lld_flag_no_threads(is_windows: bool) -> &'static str {
483-
static LLD_NO_THREADS: OnceCell<(&'static str, &'static str)> = OnceCell::new();
484-
let (windows, other) = LLD_NO_THREADS.get_or_init(|| {
482+
/// Finds out if a global `lld` binary is a newer version than version 10.
483+
pub fn is_lld_newer_than_10() -> bool {
484+
static LLD_NO_THREADS: OnceCell<bool> = OnceCell::new();
485+
*LLD_NO_THREADS.get_or_init(|| {
485486
let out = output(Command::new("lld").arg("-flavor").arg("ld").arg("--version"));
486487
let newer = match (out.find(char::is_numeric), out.find('.')) {
487488
(Some(b), Some(e)) => out.as_str()[b..e].parse::<i32>().ok().unwrap_or(14) > 10,
488489
_ => true,
489490
};
490-
if newer { ("/threads:1", "--threads=1") } else { ("/no-threads", "--no-threads") }
491-
});
492-
if is_windows { windows } else { other }
491+
newer
492+
})
493493
}
494494

495495
pub fn dir_is_empty(dir: &Path) -> bool {

0 commit comments

Comments
 (0)