Skip to content

Commit 8debfc8

Browse files
committed
Auto merge of rust-lang#102487 - Kobzol:rustc-bolt, r=<try>
Use BOLT in CI to optimize `librustc_driver` Based on rust-lang#94381. r? `@ghost`
2 parents c7224e3 + bcff055 commit 8debfc8

File tree

6 files changed

+95
-30
lines changed

6 files changed

+95
-30
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/ci/docker/host-x86_64/dist-x86_64-linux/build-gcc.sh

+2-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ cd ../gcc-build
3030
hide_output ../gcc-$GCC/configure \
3131
--prefix=/rustroot \
3232
--enable-languages=c,c++ \
33-
--disable-gnu-unique-object
33+
--disable-gnu-unique-object \
34+
--enable-cxx-flags='-fno-reorder-blocks-and-partition'
3435
hide_output make -j$(nproc)
3536
hide_output make install
3637
ln -s gcc /rustroot/bin/cc

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

+10-4
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
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;
88

99
/// Instruments an artifact at the given `path` (in-place) with BOLT and then calls `func`.
1010
/// After this function finishes, the original file will be restored.
11-
pub fn with_bolt_instrumented<F: FnOnce() -> anyhow::Result<R>, R>(
11+
pub fn with_bolt_instrumented<F: FnOnce(&Utf8Path) -> anyhow::Result<R>, R>(
1212
path: &Utf8Path,
1313
func: F,
1414
) -> anyhow::Result<R> {
@@ -20,10 +20,16 @@ pub fn with_bolt_instrumented<F: FnOnce() -> anyhow::Result<R>, R>(
2020

2121
let instrumented_path = tempfile::NamedTempFile::new()?.into_temp_path();
2222

23+
let profile_dir =
24+
tempfile::TempDir::new().context("Could not create directory for BOLT profiles")?;
25+
let profile_prefix = profile_dir.path().join("prof.fdata");
26+
let profile_prefix = Utf8Path::from_path(&profile_prefix).unwrap();
27+
2328
// Instrument the original file with BOLT, saving the result into `instrumented_path`
2429
cmd(&["llvm-bolt"])
2530
.arg("-instrument")
2631
.arg(path)
32+
.arg(&format!("--instrumentation-file={profile_prefix}"))
2733
// Make sure that each process will write its profiles into a separate file
2834
.arg("--instrumentation-file-append-pid")
2935
.arg("-o")
@@ -36,11 +42,11 @@ pub fn with_bolt_instrumented<F: FnOnce() -> anyhow::Result<R>, R>(
3642

3743
// Run the function that will make use of the instrumented artifact.
3844
// The original file will be restored when `_backup_file` is dropped.
39-
func()
45+
func(profile_prefix)
4046
}
4147

4248
/// Optimizes the file at `path` with BOLT in-place using the given `profile`.
43-
pub fn bolt_optimize(path: &Utf8Path, profile: &LlvmBoltProfile) -> anyhow::Result<()> {
49+
pub fn bolt_optimize(path: &Utf8Path, profile: &BoltProfile) -> anyhow::Result<()> {
4450
// Copy the artifact to a new location, so that we do not use the same input and output file.
4551
// BOLT cannot handle optimizing when the input and output is the same file, because it performs
4652
// 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

+42-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ 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::{
16+
gather_bolt_profiles, gather_llvm_profiles, gather_rustc_profiles, llvm_benchmarks,
17+
};
1618
use crate::utils::io::{copy_directory, move_directory, reset_directory};
1719
use crate::utils::{
1820
clear_llvm_files, format_env_variables, print_binary_sizes, print_free_disk_space,
@@ -267,9 +269,13 @@ fn execute_pipeline(
267269
".so",
268270
)?;
269271

272+
log::info!("Optimizing {llvm_lib} with BOLT");
273+
270274
// Instrument it and gather profiles
271-
let profile = with_bolt_instrumented(&llvm_lib, || {
272-
stage.section("Gather profiles", |_| gather_llvm_bolt_profiles(env))
275+
let profile = with_bolt_instrumented(&llvm_lib, |llvm_profile_dir| {
276+
stage.section("Gather profiles", |_| {
277+
gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env), llvm_profile_dir)
278+
})
273279
})?;
274280
print_free_disk_space()?;
275281

@@ -287,6 +293,35 @@ fn execute_pipeline(
287293
None
288294
};
289295

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

299337
// Final stage: Assemble the dist artifacts
300338
// 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))?;
339+
timer.section("Stage 5 (final build)", |stage| dist.run(stage))?;
302340

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

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

+34-19
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: &Environment) -> CmdBuilder {
115+
init_compiler_benchmarks(env, &["Debug", "Opt"], &["Full"], LLVM_PGO_CRATES)
116+
}
117+
118+
pub fn rustc_benchmarks(env: &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,23 +180,25 @@ 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+
profile_prefix: &Utf8Path,
190+
) -> anyhow::Result<BoltProfile> {
191+
log::info!("Running benchmarks with BOLT instrumented {name}");
183192

184193
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")
194+
benchmarks.run().with_context(|| "Cannot gather {name} BOLT profiles")
188195
})?;
189196

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

194200
let profiles: Vec<_> =
195-
glob::glob(&format!("{profile_root}*"))?.collect::<Result<Vec<_>, _>>()?;
201+
glob::glob(&format!("{profile_prefix}*"))?.collect::<Result<Vec<_>, _>>()?;
196202

197203
let mut merge_args = vec!["merge-fdata"];
198204
merge_args.extend(profiles.iter().map(|p| p.to_str().unwrap()));
@@ -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)
@@ -216,8 +222,17 @@ pub fn gather_llvm_bolt_profiles(env: &Environment) -> anyhow::Result<LlvmBoltPr
216222
.collect::<Result<Vec<_>, _>>()?
217223
.into_iter()
218224
.sum::<u64>();
219-
log::info!("{profile_root}: {}", humansize::format_size(size, BINARY));
225+
log::info!("{profile_prefix}: {}", 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_prefix}*"))?.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)