Skip to content

Commit 996f609

Browse files
Rollup merge of rust-lang#158912 - Kobzol:bootstrap-pgo-config, r=jieyouxu
Introduce new bootstrap config section for PGO configuration The handling of PGO profiles in bootstrap was a bit messy, we had CLI flags, but those were hardcoded only for rustc and LLVM, and also a separate `rust.profile-use/profile-generate` options, though I'm not sure if anyone ever used them. I wanted to generalize this a bit, to allow experimenting with PGO optimizing also other parts of the toolchain (e.g. rustdoc, clippy, cargo). This PR introduces a new `[pgo]` section in the bootstrap config, which allows specifying PGO profiles using the config file, currently for `rustc` and LLVM. It can be also set through the command-line using e.g. `--set pgo.rustc.use=/tmp/foo`. Best reviewed commit by commit. r? @jieyouxu
2 parents b9c990d + 1f6b1d5 commit 996f609

11 files changed

Lines changed: 190 additions & 44 deletions

File tree

bootstrap.example.toml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,24 @@
973973
#dist.vendor = if "is a tarball source" || "is a git repository" { true } else { false }
974974

975975

976+
# =============================================================================
977+
# Profile-guided optimization options
978+
#
979+
# Configure the PGO profile to be used for compilation, or a path where the
980+
# profile will be written by an instrumented binary, for individual components
981+
# =============================================================================
982+
983+
# Use the following profile to PGO optimize the Rust compiler.
984+
#pgo.rustc.use = "/tmp/profiles/foo.profraw"
985+
# Instrument the Rust compiler, so that when executed, it will gather profiles
986+
# to this path.
987+
#pgo.rustc.generate = "/tmp/profiles/foo.profraw"
988+
# Use the following profile to PGO optimize LLVM.
989+
#pgo.llvm.use = "/tmp/profiles/foo.profraw"
990+
# Instrument LLVM, so that when executed, it will gather profiles
991+
# Note: for LLVM specifically, this should be a directory, not a file path.
992+
#pgo.llvm.generate = "/tmp/profiles"
993+
976994
# =============================================================================
977995
# Options for specific targets
978996
#

src/bootstrap/src/core/build_steps/compile.rs

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,22 +1293,20 @@ pub fn rustc_cargo(
12931293
cargo.rustflag("-Clink-args=-Wl,--icf=all");
12941294
}
12951295

1296-
if builder.config.rust_profile_use.is_some() && builder.config.rust_profile_generate.is_some() {
1297-
panic!("Cannot use and generate PGO profiles at the same time");
1298-
}
1299-
let is_collecting = if let Some(path) = &builder.config.rust_profile_generate {
1296+
let is_collecting = if let Some(path) = &builder.config.rust_pgo.generate_profile {
13001297
if build_compiler.stage == 1 {
1301-
cargo.rustflag(&format!("-Cprofile-generate={path}"));
1298+
cargo
1299+
.rustflag(&format!("-Cprofile-generate={}", path.to_str().expect("non-UTF8 path")));
13021300
// Apparently necessary to avoid overflowing the counters during
13031301
// a Cargo build profile
13041302
cargo.rustflag("-Cllvm-args=-vp-counters-per-site=4");
13051303
true
13061304
} else {
13071305
false
13081306
}
1309-
} else if let Some(path) = &builder.config.rust_profile_use {
1307+
} else if let Some(path) = &builder.config.rust_pgo.use_profile {
13101308
if build_compiler.stage == 1 {
1311-
cargo.rustflag(&format!("-Cprofile-use={path}"));
1309+
cargo.rustflag(&format!("-Cprofile-use={}", path.to_str().expect("non-UTF8 path")));
13121310
if builder.is_verbose() {
13131311
cargo.rustflag("-Cllvm-args=-pgo-warn-missing-function");
13141312
}
@@ -1464,7 +1462,7 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
14641462
// found. This is to avoid the linker errors about undefined references to
14651463
// `__llvm_profile_instrument_memop` when linking `rustc_driver`.
14661464
let mut llvm_linker_flags = String::new();
1467-
if builder.config.llvm_profile_generate
1465+
if builder.config.llvm_pgo.generate_profile.is_some()
14681466
&& target.is_msvc()
14691467
&& let Some(ref clang_cl_path) = builder.config.llvm_clang_cl
14701468
{

src/bootstrap/src/core/build_steps/dist.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3035,11 +3035,11 @@ impl Step for ReproducibleArtifacts {
30353035
fn run(self, builder: &Builder<'_>) -> Self::Output {
30363036
let mut added_anything = false;
30373037
let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple);
3038-
if let Some(path) = builder.config.rust_profile_use.as_ref() {
3038+
if let Some(path) = builder.config.rust_pgo.use_profile.as_ref() {
30393039
tarball.add_file(path, ".", FileType::Regular);
30403040
added_anything = true;
30413041
}
3042-
if let Some(path) = builder.config.llvm_profile_use.as_ref() {
3042+
if let Some(path) = builder.config.llvm_pgo.use_profile.as_ref() {
30433043
tarball.add_file(path, ".", FileType::Regular);
30443044
added_anything = true;
30453045
}

src/bootstrap/src/core/build_steps/llvm.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use build_helper::git::PathFreshness;
1919

2020
use crate::core::build_steps::llvm;
2121
use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, StepMetadata};
22-
use crate::core::config::{Config, TargetSelection};
22+
use crate::core::config::{Config, LlvmPgoGenerationMode, TargetSelection};
2323
use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash};
2424
use crate::utils::exec::command;
2525
use crate::utils::helpers::{
@@ -369,14 +369,17 @@ impl Step for Llvm {
369369
// This flag makes sure `FileCheck` is copied in the final binaries directory.
370370
cfg.define("LLVM_INSTALL_UTILS", "ON");
371371

372-
if builder.config.llvm_profile_generate {
372+
if let Some(mode) = builder.config.llvm_pgo.generate_profile.as_ref() {
373373
cfg.define("LLVM_BUILD_INSTRUMENTED", "IR");
374-
if let Ok(llvm_profile_dir) = std::env::var("LLVM_PROFILE_DIR") {
375-
cfg.define("LLVM_PROFILE_DATA_DIR", llvm_profile_dir);
374+
match mode {
375+
LlvmPgoGenerationMode::Implicit => {}
376+
LlvmPgoGenerationMode::Directory(llvm_profile_dir) => {
377+
cfg.define("LLVM_PROFILE_DATA_DIR", llvm_profile_dir);
378+
}
376379
}
377380
cfg.define("LLVM_BUILD_RUNTIME", "No");
378381
}
379-
if let Some(path) = builder.config.llvm_profile_use.as_ref() {
382+
if let Some(path) = builder.config.llvm_pgo.use_profile.as_ref() {
380383
cfg.define("LLVM_PROFDATA_FILE", path);
381384
}
382385

@@ -1326,7 +1329,7 @@ impl Step for Lld {
13261329
// when doing PGO on CI, cmake or clang-cl don't automatically link clang's
13271330
// profiler runtime in. In that case, we need to manually ask cmake to do it, to avoid
13281331
// linking errors, much like LLVM's cmake setup does in that situation.
1329-
if builder.config.llvm_profile_generate
1332+
if builder.config.llvm_pgo.generate_profile.is_some()
13301333
&& target.is_msvc()
13311334
&& let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref()
13321335
{

src/bootstrap/src/core/config/config.rs

Lines changed: 85 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use crate::core::config::toml::dist::Dist;
4141
use crate::core::config::toml::gcc::Gcc;
4242
use crate::core::config::toml::install::Install;
4343
use crate::core::config::toml::llvm::Llvm;
44+
use crate::core::config::toml::pgo::{Pgo, PgoConfig};
4445
use crate::core::config::toml::rust::{
4546
BootstrapOverrideLld, Rust, RustOptimize, check_incompatible_options_for_ci_rustc,
4647
parse_codegen_backends,
@@ -190,6 +191,7 @@ pub struct Config {
190191
pub llvm_cxxflags: Option<String>,
191192
pub llvm_ldflags: Option<String>,
192193
pub llvm_use_libcxx: bool,
194+
pub llvm_pgo: LlvmPgoConfig,
193195

194196
// gcc codegen options
195197
pub gcc_ci_mode: GccCiMode,
@@ -225,17 +227,14 @@ pub struct Config {
225227
pub rust_remap_debuginfo: bool,
226228
pub rust_new_symbol_mangling: Option<bool>,
227229
pub rust_annotate_moves_size_limit: Option<u64>,
228-
pub rust_profile_use: Option<String>,
229-
pub rust_profile_generate: Option<String>,
230230
pub rust_lto: RustcLto,
231231
pub rust_validate_mir_opts: Option<u32>,
232232
pub rust_std_features: BTreeSet<String>,
233233
pub rust_break_on_ice: bool,
234234
pub rust_parallel_frontend_threads: Option<u32>,
235235
pub rust_rustflags: Vec<String>,
236+
pub rust_pgo: PgoConfig,
236237

237-
pub llvm_profile_use: Option<String>,
238-
pub llvm_profile_generate: bool,
239238
pub llvm_libunwind_default: Option<LlvmLibunwind>,
240239
pub enable_bolt_settings: bool,
241240

@@ -456,8 +455,20 @@ impl Config {
456455

457456
// Now load the TOML config, as soon as possible
458457
let (mut toml, toml_path) = load_toml_config(&src, flags_config, &get_toml);
459-
460458
postprocess_toml(&mut toml, &src, toml_path.clone(), &exec_ctx, &flags_set, &get_toml);
459+
let TomlConfig {
460+
change_id: toml_change_id,
461+
build: toml_build,
462+
install: toml_install,
463+
llvm: toml_llvm,
464+
gcc: toml_gcc,
465+
rust: toml_rust,
466+
target: toml_target,
467+
dist: toml_dist,
468+
pgo: toml_pgo,
469+
profile: _,
470+
include: _,
471+
} = toml;
461472

462473
// Now override TOML values with flags, to make sure that we won't later override flags with
463474
// TOML values by accident instead, because flags have higher priority.
@@ -523,7 +534,7 @@ impl Config {
523534
ccache: build_ccache,
524535
exclude: build_exclude,
525536
compiletest_allow_stage0: build_compiletest_allow_stage0,
526-
} = toml.build.unwrap_or_default();
537+
} = toml_build.unwrap_or_default();
527538

528539
let Install {
529540
prefix: install_prefix,
@@ -533,7 +544,7 @@ impl Config {
533544
libdir: install_libdir,
534545
mandir: install_mandir,
535546
datadir: install_datadir,
536-
} = toml.install.unwrap_or_default();
547+
} = toml_install.unwrap_or_default();
537548

538549
let Rust {
539550
optimize: rust_optimize,
@@ -595,7 +606,7 @@ impl Config {
595606
std_features: rust_std_features,
596607
break_on_ice: rust_break_on_ice,
597608
rustflags: rust_rustflags,
598-
} = toml.rust.unwrap_or_default();
609+
} = toml_rust.unwrap_or_default();
599610

600611
let Llvm {
601612
optimize: llvm_optimize,
@@ -627,7 +638,7 @@ impl Config {
627638
enable_warnings: llvm_enable_warnings,
628639
download_ci_llvm: llvm_download_ci_llvm,
629640
build_config: llvm_build_config,
630-
} = toml.llvm.unwrap_or_default();
641+
} = toml_llvm.unwrap_or_default();
631642

632643
let Dist {
633644
sign_folder: dist_sign_folder,
@@ -637,12 +648,57 @@ impl Config {
637648
compression_profile: dist_compression_profile,
638649
include_mingw_linker: dist_include_mingw_linker,
639650
vendor: dist_vendor,
640-
} = toml.dist.unwrap_or_default();
651+
} = toml_dist.unwrap_or_default();
641652

642653
let Gcc {
643654
download_ci_gcc: gcc_download_ci_gcc,
644655
libgccjit_libs_dir: gcc_libgccjit_libs_dir,
645-
} = toml.gcc.unwrap_or_default();
656+
} = toml_gcc.unwrap_or_default();
657+
658+
let Pgo { rustc: pgo_rustc, llvm: pgo_llvm } = toml_pgo.unwrap_or_default();
659+
660+
// Backcompat: flags have priority over config
661+
if flags_rust_profile_use.is_some() || flags_rust_profile_generate.is_some() {
662+
eprintln!(
663+
"WARNING: the `--rust-profile-generate` and `--rust-profile-use` flags have been deprecated. Configure PGO through the config file instead, in the [pgo.rustc] section."
664+
);
665+
}
666+
if rust_profile_use.is_some() || rust_profile_generate.is_some() {
667+
eprintln!(
668+
"WARNING: the `rust.profile-generate` and `rust.profile-use` config options have been deprecated. Configure PGO through the config file instead, in the [pgo.rustc] section."
669+
);
670+
}
671+
if flags_llvm_profile_use.is_some() || flags_llvm_profile_generate {
672+
eprintln!(
673+
"WARNING: the `--llvm-profile-generate` and `--llvm-profile-use` flags have been deprecated. Configure PGO through the config file instead, in the [pgo.llvm] section."
674+
);
675+
}
676+
677+
let mut pgo_rustc = pgo_rustc.unwrap_or_default();
678+
pgo_rustc.use_profile =
679+
flags_rust_profile_use.or(pgo_rustc.use_profile).or(rust_profile_use);
680+
pgo_rustc.generate_profile =
681+
flags_rust_profile_generate.or(pgo_rustc.generate_profile).or(rust_profile_generate);
682+
if pgo_rustc.use_profile.is_some() && pgo_rustc.generate_profile.is_some() {
683+
panic!("Cannot use and generate rust PGO profiles at the same time");
684+
}
685+
686+
let pgo_llvm = pgo_llvm.unwrap_or_default();
687+
let pgo_llvm = LlvmPgoConfig {
688+
use_profile: flags_llvm_profile_use.or(pgo_llvm.use_profile),
689+
generate_profile: if flags_llvm_profile_generate {
690+
Some(if let Ok(llvm_profile_dir) = std::env::var("LLVM_PROFILE_DIR") {
691+
LlvmPgoGenerationMode::Directory(PathBuf::from(llvm_profile_dir))
692+
} else {
693+
LlvmPgoGenerationMode::Implicit
694+
})
695+
} else {
696+
pgo_llvm.generate_profile.map(LlvmPgoGenerationMode::Directory)
697+
},
698+
};
699+
if pgo_llvm.use_profile.is_some() && pgo_llvm.generate_profile.is_some() {
700+
panic!("Cannot use and generate LLVM PGO profiles at the same time");
701+
}
646702

647703
if rust_bootstrap_override_lld.is_some() && rust_bootstrap_override_lld_legacy.is_some() {
648704
panic!(
@@ -877,7 +933,7 @@ impl Config {
877933
// Linux targets for which the user explicitly overrode the used linker
878934
let mut targets_with_user_linker_override = HashSet::new();
879935

880-
if let Some(t) = toml.target {
936+
if let Some(t) = toml_target {
881937
for (triple, cfg) in t {
882938
let TomlTarget {
883939
cc: target_cc,
@@ -1337,7 +1393,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to
13371393
cargo_info,
13381394
cargo_native_static: build_cargo_native_static.unwrap_or(false),
13391395
ccache,
1340-
change_id: toml.change_id.inner,
1396+
change_id: toml_change_id.inner,
13411397
channel,
13421398
ci_env,
13431399
clippy_info,
@@ -1428,10 +1484,9 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to
14281484
),
14291485
llvm_offload: llvm_offload.unwrap_or(false),
14301486
llvm_optimize: llvm_optimize.unwrap_or(true),
1487+
llvm_pgo: pgo_llvm,
14311488
llvm_plugins: llvm_plugin.unwrap_or(false),
14321489
llvm_polly: llvm_polly.unwrap_or(false),
1433-
llvm_profile_generate: flags_llvm_profile_generate,
1434-
llvm_profile_use: flags_llvm_profile_use,
14351490
llvm_release_debuginfo: llvm_release_debuginfo.unwrap_or(false),
14361491
llvm_static_stdcpp: llvm_static_libstdcpp.unwrap_or(false),
14371492
llvm_targets,
@@ -1496,8 +1551,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to
14961551
.or(rust_overflow_checks)
14971552
.unwrap_or(rust_debug == Some(true)),
14981553
rust_parallel_frontend_threads: rust_parallel_frontend_threads.map(threads_from_config),
1499-
rust_profile_generate: flags_rust_profile_generate.or(rust_profile_generate),
1500-
rust_profile_use: flags_rust_profile_use.or(rust_profile_use),
1554+
rust_pgo: pgo_rustc,
15011555
rust_randomize_layout: rust_randomize_layout.unwrap_or(false),
15021556
rust_remap_debuginfo: rust_remap_debuginfo.unwrap_or(false),
15031557
rust_rpath: rust_rpath.unwrap_or(true),
@@ -2030,6 +2084,20 @@ fn compute_src_directory(src_dir: Option<PathBuf>, exec_ctx: &ExecutionContext)
20302084
None
20312085
}
20322086

2087+
#[derive(Clone)]
2088+
pub enum LlvmPgoGenerationMode {
2089+
/// Enable PGO instrumentation that will write profiles into a default path.
2090+
Implicit,
2091+
/// Enable PGO instrumentation that will write profiles into the specified directory.
2092+
Directory(PathBuf),
2093+
}
2094+
2095+
#[derive(Clone)]
2096+
pub struct LlvmPgoConfig {
2097+
pub use_profile: Option<PathBuf>,
2098+
pub generate_profile: Option<LlvmPgoGenerationMode>,
2099+
}
2100+
20332101
/// Loads bootstrap TOML config and returns the config together with a path from where
20342102
/// it was loaded.
20352103
/// `src` is the source root directory, and `config_path` is an optionally provided path to the

src/bootstrap/src/core/config/flags.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,18 +153,22 @@ pub struct Flags {
153153

154154
/// generate PGO profile with rustc build
155155
#[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
156-
pub rust_profile_generate: Option<String>,
156+
// FIXME: Remove this option at the end of 2026
157+
pub rust_profile_generate: Option<PathBuf>,
157158
/// use PGO profile for rustc build
159+
// FIXME: Remove this option at the end of 2026
158160
#[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
159-
pub rust_profile_use: Option<String>,
161+
pub rust_profile_use: Option<PathBuf>,
160162
/// use PGO profile for LLVM build
163+
// FIXME: Remove this option at the end of 2026
161164
#[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")]
162-
pub llvm_profile_use: Option<String>,
165+
pub llvm_profile_use: Option<PathBuf>,
163166
// LLVM doesn't support a custom location for generating profile
164167
// information.
165168
//
166169
// llvm_out/build/profiles/ is the location this writes to.
167170
/// generate PGO profile with llvm built for rustc
171+
// FIXME: Remove this option at the end of 2026
168172
#[arg(global = true, long)]
169173
pub llvm_profile_generate: bool,
170174
/// Enable BOLT link flags

0 commit comments

Comments
 (0)