Skip to content

Commit ba48850

Browse files
committed
Auto merge of #53245 - michaelwoerister:thinlto-rust-llvm, r=alexcrichton
[experimental]: Build LLVM with ThinLTO enabled (2nd attempt) This is #51207 revived. This time, I'd like to run actual performance tests to see if it improves compile times.
2 parents ca0de63 + 3cf6f0d commit ba48850

File tree

11 files changed

+144
-23
lines changed

11 files changed

+144
-23
lines changed

Diff for: config.toml.example

+10
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
# Indicates whether the LLVM build is a Release or Debug build
2222
#optimize = true
2323

24+
# Indicates whether LLVM should be built with ThinLTO. Note that this will
25+
# only succeed if you use clang, lld, llvm-ar, and llvm-ranlib in your C/C++
26+
# toolchain (see the `cc`, `cxx`, `linker`, `ar`, and `ranlib` options below).
27+
# More info at: https://clang.llvm.org/docs/ThinLTO.html#clang-bootstrap
28+
#thin-lto = false
29+
2430
# Indicates whether an LLVM Release build should include debug info
2531
#release-debuginfo = false
2632

@@ -388,6 +394,10 @@
388394
# Note: an absolute path should be used, otherwise LLVM build will break.
389395
#ar = "ar"
390396

397+
# Ranlib to be used to assemble static libraries compiled from C/C++ code.
398+
# Note: an absolute path should be used, otherwise LLVM build will break.
399+
#ranlib = "ranlib"
400+
391401
# Linker to be used to link Rust code. Note that the
392402
# default value is platform specific, and if not specified it may also depend on
393403
# what platform is crossing to what platform.

Diff for: src/bootstrap/builder.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -827,7 +827,7 @@ impl<'a> Builder<'a> {
827827
if let Some(ref error_format) = self.config.rustc_error_format {
828828
cargo.env("RUSTC_ERROR_FORMAT", error_format);
829829
}
830-
if cmd != "build" && cmd != "check" && want_rustdoc {
830+
if cmd != "build" && cmd != "check" && cmd != "rustc" && want_rustdoc {
831831
cargo.env("RUSTDOC_LIBDIR", self.sysroot_libdir(compiler, self.config.build));
832832
}
833833

@@ -988,7 +988,7 @@ impl<'a> Builder<'a> {
988988
}
989989
}
990990

991-
if cmd == "build"
991+
if (cmd == "build" || cmd == "rustc")
992992
&& mode == Mode::Std
993993
&& self.config.extended
994994
&& compiler.is_final_stage(self)

Diff for: src/bootstrap/check.rs

+5
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ impl Step for Std {
5050
println!("Checking std artifacts ({} -> {})", &compiler.host, target);
5151
run_cargo(builder,
5252
&mut cargo,
53+
vec![],
5354
&libstd_stamp(builder, compiler, target),
5455
true);
5556

@@ -98,6 +99,7 @@ impl Step for Rustc {
9899
println!("Checking compiler artifacts ({} -> {})", &compiler.host, target);
99100
run_cargo(builder,
100101
&mut cargo,
102+
vec![],
101103
&librustc_stamp(builder, compiler, target),
102104
true);
103105

@@ -149,6 +151,7 @@ impl Step for CodegenBackend {
149151
let _folder = builder.fold_output(|| format!("stage{}-rustc_codegen_llvm", compiler.stage));
150152
run_cargo(builder,
151153
&mut cargo,
154+
vec![],
152155
&codegen_backend_stamp(builder, compiler, target, backend),
153156
true);
154157
}
@@ -187,6 +190,7 @@ impl Step for Test {
187190
println!("Checking test artifacts ({} -> {})", &compiler.host, target);
188191
run_cargo(builder,
189192
&mut cargo,
193+
vec![],
190194
&libtest_stamp(builder, compiler, target),
191195
true);
192196

@@ -236,6 +240,7 @@ impl Step for Rustdoc {
236240
println!("Checking rustdoc artifacts ({} -> {})", &compiler.host, target);
237241
run_cargo(builder,
238242
&mut cargo,
243+
vec![],
239244
&rustdoc_stamp(builder, compiler, target),
240245
true);
241246

Diff for: src/bootstrap/compile.rs

+44-3
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ impl Step for Std {
117117
&compiler.host, target));
118118
run_cargo(builder,
119119
&mut cargo,
120+
vec![],
120121
&libstd_stamp(builder, compiler, target),
121122
false);
122123

@@ -396,6 +397,7 @@ impl Step for Test {
396397
&compiler.host, target));
397398
run_cargo(builder,
398399
&mut cargo,
400+
vec![],
399401
&libtest_stamp(builder, compiler, target),
400402
false);
401403

@@ -529,6 +531,7 @@ impl Step for Rustc {
529531
compiler.stage, &compiler.host, target));
530532
run_cargo(builder,
531533
&mut cargo,
534+
vec![],
532535
&librustc_stamp(builder, compiler, target),
533536
false);
534537

@@ -673,18 +676,47 @@ impl Step for CodegenBackend {
673676
let out_dir = builder.cargo_out(compiler, Mode::Codegen, target);
674677
builder.clear_if_dirty(&out_dir, &librustc_stamp(builder, compiler, target));
675678

676-
let mut cargo = builder.cargo(compiler, Mode::Codegen, target, "build");
679+
let mut cargo = builder.cargo(compiler, Mode::Codegen, target, "rustc");
677680
cargo.arg("--manifest-path")
678681
.arg(builder.src.join("src/librustc_codegen_llvm/Cargo.toml"));
679682
rustc_cargo_env(builder, &mut cargo);
680683

681684
let features = build_codegen_backend(&builder, &mut cargo, &compiler, target, backend);
682685

686+
let mut cargo_tails_args = vec![];
687+
688+
if builder.config.llvm_thin_lto {
689+
cargo_tails_args.push("--".to_string());
690+
691+
let num_jobs = builder.jobs();
692+
693+
if !target.contains("msvc") {
694+
// Here we assume that the linker is clang. If it's not, there'll
695+
// be linker errors.
696+
cargo_tails_args.push("-Clink-arg=-fuse-ld=lld".to_string());
697+
cargo_tails_args.push("-Clink-arg=-flto=thin".to_string());
698+
699+
if builder.config.llvm_optimize {
700+
cargo_tails_args.push("-Clink-arg=-O2".to_string());
701+
}
702+
703+
// Let's make LLD respect the `-j` option.
704+
let num_jobs_arg = format!("-Clink-arg=-Wl,--thinlto-jobs={}", num_jobs);
705+
cargo_tails_args.push(num_jobs_arg);
706+
} else {
707+
// Here we assume that the linker is lld-link.exe. lld-link.exe
708+
// does not need the extra arguments except for num_jobs
709+
let num_jobs_arg = format!("-Clink-arg=/opt:lldltojobs={}", num_jobs);
710+
cargo_tails_args.push(num_jobs_arg);
711+
}
712+
}
713+
683714
let tmp_stamp = out_dir.join(".tmp.stamp");
684715

685716
let _folder = builder.fold_output(|| format!("stage{}-rustc_codegen_llvm", compiler.stage));
686717
let files = run_cargo(builder,
687718
cargo.arg("--features").arg(features),
719+
cargo_tails_args,
688720
&tmp_stamp,
689721
false);
690722
if builder.config.dry_run {
@@ -1045,7 +1077,11 @@ fn stderr_isatty() -> bool {
10451077
}
10461078
}
10471079

1048-
pub fn run_cargo(builder: &Builder, cargo: &mut Command, stamp: &Path, is_check: bool)
1080+
pub fn run_cargo(builder: &Builder,
1081+
cargo: &mut Command,
1082+
tail_args: Vec<String>,
1083+
stamp: &Path,
1084+
is_check: bool)
10491085
-> Vec<PathBuf>
10501086
{
10511087
if builder.config.dry_run {
@@ -1066,7 +1102,7 @@ pub fn run_cargo(builder: &Builder, cargo: &mut Command, stamp: &Path, is_check:
10661102
// files we need to probe for later.
10671103
let mut deps = Vec::new();
10681104
let mut toplevel = Vec::new();
1069-
let ok = stream_cargo(builder, cargo, &mut |msg| {
1105+
let ok = stream_cargo(builder, cargo, tail_args, &mut |msg| {
10701106
let filenames = match msg {
10711107
CargoMessage::CompilerArtifact { filenames, .. } => filenames,
10721108
_ => return,
@@ -1191,6 +1227,7 @@ pub fn run_cargo(builder: &Builder, cargo: &mut Command, stamp: &Path, is_check:
11911227
pub fn stream_cargo(
11921228
builder: &Builder,
11931229
cargo: &mut Command,
1230+
tail_args: Vec<String>,
11941231
cb: &mut dyn FnMut(CargoMessage),
11951232
) -> bool {
11961233
if builder.config.dry_run {
@@ -1210,6 +1247,10 @@ pub fn stream_cargo(
12101247
cargo.env("RUSTC_COLOR", "1");
12111248
}
12121249

1250+
for arg in tail_args {
1251+
cargo.arg(arg);
1252+
}
1253+
12131254
builder.verbose(&format!("running: {:?}", cargo));
12141255
let mut child = match cargo.spawn() {
12151256
Ok(child) => child,

Diff for: src/bootstrap/config.rs

+6
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ pub struct Config {
7777
pub llvm_enabled: bool,
7878
pub llvm_assertions: bool,
7979
pub llvm_optimize: bool,
80+
pub llvm_thin_lto: bool,
8081
pub llvm_release_debuginfo: bool,
8182
pub llvm_version_check: bool,
8283
pub llvm_static_stdcpp: bool,
@@ -163,6 +164,7 @@ pub struct Target {
163164
pub cc: Option<PathBuf>,
164165
pub cxx: Option<PathBuf>,
165166
pub ar: Option<PathBuf>,
167+
pub ranlib: Option<PathBuf>,
166168
pub linker: Option<PathBuf>,
167169
pub ndk: Option<PathBuf>,
168170
pub crt_static: Option<bool>,
@@ -246,6 +248,7 @@ struct Llvm {
246248
ninja: Option<bool>,
247249
assertions: Option<bool>,
248250
optimize: Option<bool>,
251+
thin_lto: Option<bool>,
249252
release_debuginfo: Option<bool>,
250253
version_check: Option<bool>,
251254
static_libstdcpp: Option<bool>,
@@ -327,6 +330,7 @@ struct TomlTarget {
327330
cc: Option<String>,
328331
cxx: Option<String>,
329332
ar: Option<String>,
333+
ranlib: Option<String>,
330334
linker: Option<String>,
331335
android_ndk: Option<String>,
332336
crt_static: Option<bool>,
@@ -503,6 +507,7 @@ impl Config {
503507
set(&mut config.llvm_enabled, llvm.enabled);
504508
llvm_assertions = llvm.assertions;
505509
set(&mut config.llvm_optimize, llvm.optimize);
510+
set(&mut config.llvm_thin_lto, llvm.thin_lto);
506511
set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
507512
set(&mut config.llvm_version_check, llvm.version_check);
508513
set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
@@ -581,6 +586,7 @@ impl Config {
581586
target.cc = cfg.cc.clone().map(PathBuf::from);
582587
target.cxx = cfg.cxx.clone().map(PathBuf::from);
583588
target.ar = cfg.ar.clone().map(PathBuf::from);
589+
target.ranlib = cfg.ranlib.clone().map(PathBuf::from);
584590
target.linker = cfg.linker.clone().map(PathBuf::from);
585591
target.crt_static = cfg.crt_static.clone();
586592
target.musl_root = cfg.musl_root.clone().map(PathBuf::from);

Diff for: src/bootstrap/dist.rs

+38-8
Original file line numberDiff line numberDiff line change
@@ -1885,6 +1885,34 @@ impl Step for HashSign {
18851885
}
18861886
}
18871887

1888+
// Maybe add libLLVM.so to the lib-dir. It will only have been built if
1889+
// LLVM tools are linked dynamically.
1890+
// Note: This function does no yet support Windows but we also don't support
1891+
// linking LLVM tools dynamically on Windows yet.
1892+
fn maybe_install_llvm_dylib(builder: &Builder,
1893+
target: Interned<String>,
1894+
image: &Path) {
1895+
let src_libdir = builder
1896+
.llvm_out(target)
1897+
.join("lib");
1898+
1899+
// Usually libLLVM.so is a symlink to something like libLLVM-6.0.so.
1900+
// Since tools link to the latter rather than the former, we have to
1901+
// follow the symlink to find out what to distribute.
1902+
let llvm_dylib_path = src_libdir.join("libLLVM.so");
1903+
if llvm_dylib_path.exists() {
1904+
let llvm_dylib_path = llvm_dylib_path.canonicalize().unwrap_or_else(|e| {
1905+
panic!("dist: Error calling canonicalize path `{}`: {}",
1906+
llvm_dylib_path.display(), e);
1907+
});
1908+
1909+
let dst_libdir = image.join("lib");
1910+
t!(fs::create_dir_all(&dst_libdir));
1911+
1912+
builder.install(&llvm_dylib_path, &dst_libdir, 0o644);
1913+
}
1914+
}
1915+
18881916
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
18891917
pub struct LlvmTools {
18901918
pub stage: u32,
@@ -1929,18 +1957,18 @@ impl Step for LlvmTools {
19291957
drop(fs::remove_dir_all(&image));
19301958

19311959
// Prepare the image directory
1932-
let bindir = builder
1960+
let src_bindir = builder
19331961
.llvm_out(target)
19341962
.join("bin");
1935-
let dst = image.join("lib/rustlib")
1936-
.join(target)
1937-
.join("bin");
1938-
t!(fs::create_dir_all(&dst));
1963+
let dst_bindir = image.join("bin");
1964+
t!(fs::create_dir_all(&dst_bindir));
19391965
for tool in LLVM_TOOLS {
1940-
let exe = bindir.join(exe(tool, &target));
1941-
builder.install(&exe, &dst, 0o755);
1966+
let exe = src_bindir.join(exe(tool, &target));
1967+
builder.install(&exe, &dst_bindir, 0o755);
19421968
}
19431969

1970+
maybe_install_llvm_dylib(builder, target, &image);
1971+
19441972
// Prepare the overlay
19451973
let overlay = tmp.join("llvm-tools-overlay");
19461974
drop(fs::remove_dir_all(&overlay));
@@ -2025,7 +2053,6 @@ impl Step for Lldb {
20252053
let dst = image.join("lib");
20262054
t!(fs::create_dir_all(&dst));
20272055
for entry in t!(fs::read_dir(&libdir)) {
2028-
// let entry = t!(entry);
20292056
let entry = entry.unwrap();
20302057
if let Ok(name) = entry.file_name().into_string() {
20312058
if name.starts_with("liblldb.") && !name.ends_with(".a") {
@@ -2060,6 +2087,9 @@ impl Step for Lldb {
20602087
}
20612088
}
20622089

2090+
// Copy libLLVM.so to the lib dir as well, if needed.
2091+
maybe_install_llvm_dylib(builder, target, &image);
2092+
20632093
// Prepare the overlay
20642094
let overlay = tmp.join("lldb-overlay");
20652095
drop(fs::remove_dir_all(&overlay));

Diff for: src/bootstrap/lib.rs

+11
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ pub struct Build {
281281
cc: HashMap<Interned<String>, cc::Tool>,
282282
cxx: HashMap<Interned<String>, cc::Tool>,
283283
ar: HashMap<Interned<String>, PathBuf>,
284+
ranlib: HashMap<Interned<String>, PathBuf>,
284285
// Misc
285286
crates: HashMap<Interned<String>, Crate>,
286287
is_sudo: bool,
@@ -406,6 +407,7 @@ impl Build {
406407
cc: HashMap::new(),
407408
cxx: HashMap::new(),
408409
ar: HashMap::new(),
410+
ranlib: HashMap::new(),
409411
crates: HashMap::new(),
410412
lldb_version: None,
411413
lldb_python_dir: None,
@@ -772,6 +774,11 @@ impl Build {
772774
self.ar.get(&target).map(|p| &**p)
773775
}
774776

777+
/// Returns the path to the `ranlib` utility for the target specified.
778+
fn ranlib(&self, target: Interned<String>) -> Option<&Path> {
779+
self.ranlib.get(&target).map(|p| &**p)
780+
}
781+
775782
/// Returns the path to the C++ compiler for the target specified.
776783
fn cxx(&self, target: Interned<String>) -> Result<&Path, String> {
777784
match self.cxx.get(&target) {
@@ -1018,6 +1025,10 @@ impl Build {
10181025
self.rust_version()
10191026
}
10201027

1028+
fn llvm_link_tools_dynamically(&self, target: Interned<String>) -> bool {
1029+
(target.contains("linux-gnu") || target.contains("apple-darwin"))
1030+
}
1031+
10211032
/// Returns the `version` string associated with this compiler for Rust
10221033
/// itself.
10231034
///

0 commit comments

Comments
 (0)