Skip to content

Commit 7e1fb23

Browse files
committed
Use BOLT to optimize librustc_driver
1 parent 0fd7ce9 commit 7e1fb23

File tree

5 files changed

+79
-23
lines changed

5 files changed

+79
-23
lines changed

src/bootstrap/compile.rs

+5
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,11 @@ impl Step for Rustc {
887887
cargo.arg("-p").arg(krate);
888888
}
889889

890+
if compiler.stage == 1 {
891+
// Relocations are required for BOLT to work.
892+
cargo.rustflag(&format!("-Clink-args=-Wl,-q"));
893+
}
894+
890895
let _guard = builder.msg_sysroot_tool(
891896
Kind::Build,
892897
compiler.stage,

src/tools/opt-dist/src/bolt.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use anyhow::Context;
22

33
use crate::exec::cmd;
4-
use crate::training::LlvmBoltProfile;
4+
use crate::training::BoltProfile;
55
use camino::{Utf8Path, Utf8PathBuf};
66

77
use crate::utils::io::copy_file;
@@ -40,7 +40,7 @@ pub fn with_bolt_instrumented<F: FnOnce() -> anyhow::Result<R>, R>(
4040
}
4141

4242
/// Optimizes the file at `path` with BOLT in-place using the given `profile`.
43-
pub fn bolt_optimize(path: &Utf8Path, profile: &LlvmBoltProfile) -> anyhow::Result<()> {
43+
pub fn bolt_optimize(path: &Utf8Path, profile: &BoltProfile) -> anyhow::Result<()> {
4444
// Copy the artifact to a new location, so that we do not use the same input and output file.
4545
// BOLT cannot handle optimizing when the input and output is the same file, because it performs
4646
// in-place patching.

src/tools/opt-dist/src/exec.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::environment::Environment;
22
use crate::metrics::{load_metrics, record_metrics};
33
use crate::timer::TimerSection;
4-
use crate::training::{LlvmBoltProfile, LlvmPGOProfile, RustcPGOProfile};
4+
use crate::training::{BoltProfile, LlvmPGOProfile, RustcPGOProfile};
55
use camino::{Utf8Path, Utf8PathBuf};
66
use std::collections::BTreeMap;
77
use std::fs::File;
@@ -159,7 +159,7 @@ impl Bootstrap {
159159
self
160160
}
161161

162-
pub fn with_bolt_profile(mut self, profile: LlvmBoltProfile) -> Self {
162+
pub fn with_bolt_profile(mut self, profile: BoltProfile) -> Self {
163163
self.cmd = self.cmd.arg("--reproducible-artifact").arg(profile.0.as_str());
164164
self
165165
}

src/tools/opt-dist/src/main.rs

+39-3
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::environment::{Environment, EnvironmentBuilder};
1212
use crate::exec::{cmd, Bootstrap};
1313
use crate::tests::run_tests;
1414
use crate::timer::Timer;
15-
use crate::training::{gather_llvm_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles};
15+
use crate::training::{gather_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles};
1616
use crate::utils::io::{copy_directory, move_directory, reset_directory};
1717
use crate::utils::{
1818
clear_llvm_files, format_env_variables, print_binary_sizes, print_free_disk_space,
@@ -267,9 +267,13 @@ fn execute_pipeline(
267267
".so",
268268
)?;
269269

270+
log::info!("Optimizing {llvm_lib} with BOLT");
271+
270272
// Instrument it and gather profiles
271273
let profile = with_bolt_instrumented(&llvm_lib, || {
272-
stage.section("Gather profiles", |_| gather_llvm_bolt_profiles(env))
274+
stage.section("Gather profiles", |_| {
275+
gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env))
276+
})
273277
})?;
274278
print_free_disk_space()?;
275279

@@ -287,6 +291,35 @@ fn execute_pipeline(
287291
None
288292
};
289293

294+
let rustc_bolt_profile = if env.supports_bolt() {
295+
// Stage 4: Build BOLT instrumented rustc
296+
timer.section("Stage 4 (Rustc BOLT)", |stage| {
297+
// Find the path to the `librustc_driver.so` file
298+
let rustc_lib = io::find_file_in_dir(
299+
&env.build_artifacts().join("stage2").join("lib"),
300+
"librustc_driver",
301+
".so",
302+
)?;
303+
304+
log::info!("Optimizing {rustc_lib} with BOLT");
305+
306+
// Instrument it and gather profiles
307+
let profile = with_bolt_instrumented(&rustc_lib, || {
308+
stage.section("Gather profiles", |_| {
309+
gather_bolt_profiles(env, "rustc", rustc_benchmarks(env))
310+
})
311+
})?;
312+
print_free_disk_space()?;
313+
314+
// Now optimize the library with BOLT.
315+
bolt_optimize(&rustc_lib, &profile).context("Could not optimize rustc with BOLT")?;
316+
317+
Ok(Some(profile))
318+
})?
319+
} else {
320+
None
321+
};
322+
290323
let mut dist = Bootstrap::dist(env, &dist_args)
291324
.llvm_pgo_optimize(&llvm_pgo_profile)
292325
.rustc_pgo_optimize(&rustc_pgo_profile)
@@ -295,10 +328,13 @@ fn execute_pipeline(
295328
if let Some(llvm_bolt_profile) = llvm_bolt_profile {
296329
dist = dist.with_bolt_profile(llvm_bolt_profile);
297330
}
331+
if let Some(rustc_bolt_profile) = rustc_bolt_profile {
332+
dist = dist.with_bolt_profile(rustc_bolt_profile);
333+
}
298334

299335
// Final stage: Assemble the dist artifacts
300336
// The previous PGO optimized rustc build and PGO optimized LLVM builds should be reused.
301-
timer.section("Stage 4 (final build)", |stage| dist.run(stage))?;
337+
timer.section("Stage 5 (final build)", |stage| dist.run(stage))?;
302338

303339
// After dist has finished, run a subset of the test suite on the optimized artifacts to discover
304340
// possible regressions.

src/tools/opt-dist/src/training.rs

+31-16
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ const RUSTC_PGO_CRATES: &[&str] = &[
2727
"bitmaps-3.1.0",
2828
];
2929

30-
const LLVM_BOLT_CRATES: &[&str] = LLVM_PGO_CRATES;
31-
3230
fn init_compiler_benchmarks(
3331
env: &Environment,
3432
profiles: &[&str],
@@ -113,6 +111,14 @@ fn log_profile_stats(
113111
Ok(())
114112
}
115113

114+
pub fn llvm_benchmarks(env: &dyn Environment) -> CmdBuilder {
115+
init_compiler_benchmarks(env, &["Debug", "Opt"], &["Full"], LLVM_PGO_CRATES)
116+
}
117+
118+
pub fn rustc_benchmarks(env: &dyn Environment) -> CmdBuilder {
119+
init_compiler_benchmarks(env, &["Check", "Debug", "Opt"], &["All"], RUSTC_PGO_CRATES)
120+
}
121+
116122
pub struct LlvmPGOProfile(pub Utf8PathBuf);
117123

118124
pub fn gather_llvm_profiles(
@@ -122,9 +128,7 @@ pub fn gather_llvm_profiles(
122128
log::info!("Running benchmarks with PGO instrumented LLVM");
123129

124130
with_log_group("Running benchmarks", || {
125-
init_compiler_benchmarks(env, &["Debug", "Opt"], &["Full"], LLVM_PGO_CRATES)
126-
.run()
127-
.context("Cannot gather LLVM PGO profiles")
131+
llvm_benchmarks(env).run().context("Cannot gather LLVM PGO profiles")
128132
})?;
129133

130134
let merged_profile = env.artifact_dir().join("llvm-pgo.profdata");
@@ -157,7 +161,7 @@ pub fn gather_rustc_profiles(
157161
// Here we're profiling the `rustc` frontend, so we also include `Check`.
158162
// The benchmark set includes various stress tests that put the frontend under pressure.
159163
with_log_group("Running benchmarks", || {
160-
init_compiler_benchmarks(env, &["Check", "Debug", "Opt"], &["All"], RUSTC_PGO_CRATES)
164+
rustc_benchmarks(env)
161165
.env("LLVM_PROFILE_FILE", profile_template.as_str())
162166
.run()
163167
.context("Cannot gather rustc PGO profiles")
@@ -176,20 +180,22 @@ pub fn gather_rustc_profiles(
176180
Ok(RustcPGOProfile(merged_profile))
177181
}
178182

179-
pub struct LlvmBoltProfile(pub Utf8PathBuf);
183+
pub struct BoltProfile(pub Utf8PathBuf);
180184

181-
pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltProfile> {
182-
log::info!("Running benchmarks with BOLT instrumented LLVM");
185+
pub fn gather_bolt_profiles(
186+
env: &Environment,
187+
name: &str,
188+
benchmarks: CmdBuilder,
189+
) -> anyhow::Result<BoltProfile> {
190+
log::info!("Running benchmarks with BOLT instrumented {name}");
183191

184192
with_log_group("Running benchmarks", || {
185-
init_compiler_benchmarks(env, &["Check", "Debug", "Opt"], &["Full"], LLVM_BOLT_CRATES)
186-
.run()
187-
.context("Cannot gather LLVM BOLT profiles")
193+
benchmarks.run().with_context(|| "Cannot gather {name} BOLT profiles")
188194
})?;
189195

190-
let merged_profile = env.artifact_dir().join("llvm-bolt.profdata");
196+
let merged_profile = env.artifact_dir().join(format!("{name}-bolt.profdata"));
191197
let profile_root = Utf8PathBuf::from("/tmp/prof.fdata");
192-
log::info!("Merging LLVM BOLT profiles to {merged_profile}");
198+
log::info!("Merging {name} BOLT profiles to {merged_profile}");
193199

194200
let profiles: Vec<_> =
195201
glob::glob(&format!("{profile_root}*"))?.collect::<Result<Vec<_>, _>>()?;
@@ -204,7 +210,7 @@ pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltPr
204210
.context("Cannot merge BOLT profiles")
205211
})?;
206212

207-
log::info!("LLVM BOLT statistics");
213+
log::info!("{name} BOLT statistics");
208214
log::info!(
209215
"{merged_profile}: {}",
210216
humansize::format_size(std::fs::metadata(merged_profile.as_std_path())?.len(), BINARY)
@@ -219,5 +225,14 @@ pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltPr
219225
log::info!("{profile_root}: {}", humansize::format_size(size, BINARY));
220226
log::info!("Profile file count: {}", profiles.len());
221227

222-
Ok(LlvmBoltProfile(merged_profile))
228+
// Delete the gathered profiles
229+
for profile in glob::glob(&format!("{profile_root}*"))?.into_iter() {
230+
if let Ok(profile) = profile {
231+
if let Err(error) = std::fs::remove_file(&profile) {
232+
log::error!("Cannot delete BOLT profile {}: {error:?}", profile.display());
233+
}
234+
}
235+
}
236+
237+
Ok(BoltProfile(merged_profile))
223238
}

0 commit comments

Comments
 (0)