Skip to content

Commit c035a08

Browse files
committed
Generalize usage of LLD in bootstrap
Allow choosing between self-contained and externally provided LLD.
1 parent c003042 commit c035a08

File tree

8 files changed

+132
-53
lines changed

8 files changed

+132
-53
lines changed

src/bootstrap/bin/rustc.rs

+17-2
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

+16-8
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

+1-1
Original file line numberDiff line numberDiff line change
@@ -839,7 +839,7 @@ impl Step for Rustc {
839839
// is already on by default in MSVC optimized builds, which is interpreted as --icf=all:
840840
// https://github.com/llvm/llvm-project/blob/3329cec2f79185bafd678f310fafadba2a8c76d2/lld/COFF/Driver.cpp#L1746
841841
// https://github.com/rust-lang/rust/blob/f22819bcce4abaff7d1246a56eec493418f9f4ee/compiler/rustc_codegen_ssa/src/back/linker.rs#L827
842-
if builder.config.use_lld && !compiler.host.is_msvc() {
842+
if builder.config.lld_mode.is_used() && !compiler.host.is_msvc() {
843843
cargo.rustflag("-Clink-args=-Wl,--icf=all");
844844
}
845845

src/bootstrap/config.rs

+22-8
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.
@@ -207,7 +227,7 @@ pub struct Config {
207227
pub llvm_from_ci: bool,
208228
pub llvm_build_config: HashMap<String, String>,
209229

210-
pub use_lld: bool,
230+
pub lld_mode: LldMode,
211231
pub lld_enabled: bool,
212232
pub llvm_tools_enabled: bool,
213233

@@ -1476,13 +1496,7 @@ impl Config {
14761496
if let Some(true) = rust.incremental {
14771497
config.incremental = true;
14781498
}
1479-
set(
1480-
&mut config.use_lld,
1481-
match rust.lld_mode {
1482-
None | Some(LldMode::Unused) => None,
1483-
_ => Some(true),
1484-
},
1485-
);
1499+
set(&mut config.lld_mode, rust.lld_mode);
14861500
set(&mut config.lld_enabled, rust.lld);
14871501
set(&mut config.llvm_tools_enabled, rust.llvm_tools);
14881502
config.rustc_parallel = rust.parallel_compiler.unwrap_or(false);

src/bootstrap/config/tests.rs

+6-5
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

+56-15
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
};
@@ -1250,6 +1250,7 @@ impl Build {
12501250
if self.config.dry_run() {
12511251
return Some(PathBuf::new());
12521252
}
1253+
12531254
if let Some(linker) = self.config.target_config.get(&target).and_then(|c| c.linker.clone())
12541255
{
12551256
Some(linker)
@@ -1260,32 +1261,72 @@ impl Build {
12601261
} else if target != self.config.build && util::use_host_linker(target) && !target.is_msvc()
12611262
{
12621263
Some(self.cc(target))
1263-
} else if self.config.use_lld && !self.is_fuse_ld_lld(target) && self.build == target {
1264-
Some(self.initial_lld.clone())
1264+
} else if self.config.lld_mode.is_used()
1265+
&& self.is_lld_direct_linker(target)
1266+
&& self.build == target
1267+
{
1268+
match self.config.lld_mode {
1269+
LldMode::SelfContained => Some(self.initial_lld.clone()),
1270+
LldMode::External => Some("lld".into()),
1271+
LldMode::Unused => None,
1272+
}
12651273
} else {
12661274
None
12671275
}
12681276
}
12691277

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

1276-
fn lld_flags(&self, target: TargetSelection) -> impl Iterator<Item = String> {
1277-
let mut options = [None, None];
1284+
// Returns the gcc-ld directory of the snapshot compiler's rust-lld.
1285+
fn initial_lld_root(&self) -> PathBuf {
1286+
let mut rust_lld_path = self.initial_lld.clone();
1287+
rust_lld_path.pop();
1288+
rust_lld_path.join("gcc-ld")
1289+
}
12781290

1279-
if self.config.use_lld {
1280-
if self.is_fuse_ld_lld(target) {
1281-
options[0] = Some("-Clink-arg=-fuse-ld=lld".to_string());
1282-
}
1291+
fn lld_flags(&self, target: TargetSelection, test: bool) -> Vec<String> {
1292+
let mut flags = vec![];
1293+
1294+
if !self.is_lld_direct_linker(target) {
1295+
match self.config.lld_mode {
1296+
LldMode::SelfContained => {
1297+
// FIXME: replace with MCP510 (-Clinker-flavor + -Clink-self-contained)
1298+
// once gcc-ld is available in stage0-sysroot.
1299+
flags.push("-Clink-arg=-fuse-ld=lld".to_string());
1300+
flags.push(format!("-Clink-arg=-B{}", self.initial_lld_root().display()));
1301+
}
1302+
LldMode::External => {
1303+
flags.push("-Clink-arg=-fuse-ld=lld".to_string());
1304+
}
1305+
LldMode::Unused => {}
1306+
};
1307+
}
12831308

1284-
let no_threads = util::lld_flag_no_threads(target.contains("windows"));
1285-
options[1] = Some(format!("-Clink-arg=-Wl,{no_threads}"));
1309+
// For tests we want to use only a single thread.
1310+
// If we use an external LLD, we don't know if it's new enough to support the required
1311+
// threads flag. Therefore we invoke it to find it out.
1312+
// The self-contained lld should always be new enough.
1313+
if test {
1314+
let new_flags = if let LldMode::External = self.config.lld_mode {
1315+
util::is_lld_newer_than_10()
1316+
} else {
1317+
true
1318+
};
1319+
1320+
let flag = match (new_flags, target.contains("windows")) {
1321+
(true, true) => "/threads:1",
1322+
(true, false) => "--threads=1",
1323+
(false, true) => "/no-threads",
1324+
(false, false) => "--no-threads",
1325+
};
1326+
flags.push(format!("-Clink-arg=-Wl,{flag}"));
12861327
}
12871328

1288-
IntoIterator::into_iter(options).flatten()
1329+
flags
12891330
}
12901331

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

src/bootstrap/test.rs

+8-8
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

+6-6
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)