Skip to content

Commit bfc220a

Browse files
committed
Create BOLT build steps to avoid running BOLT multiple times on the same file
1 parent c5d65aa commit bfc220a

File tree

2 files changed

+127
-19
lines changed

2 files changed

+127
-19
lines changed

src/bootstrap/bolt.rs

+9-17
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,40 @@
1-
use std::path::{Path, PathBuf};
1+
use std::path::Path;
22
use std::process::Command;
33

4-
/// Uses the `llvm-bolt` binary to instrument the binary/library at the given `path` with BOLT.
4+
/// Uses the `llvm-bolt` binary to instrument the artifact at the given `path` with BOLT.
55
/// When the instrumented artifact is executed, it will generate BOLT profiles into
66
/// `/tmp/prof.fdata.<pid>.fdata`.
7-
/// Returns a path to the instrumented artifact, created in a temporary directory.
8-
pub fn instrument_with_bolt(path: &Path) -> PathBuf {
9-
let dir = std::env::temp_dir();
10-
let instrumented_path = dir.join(path.file_name().unwrap());
11-
7+
/// Creates the instrumented artifact at `output_path`.
8+
pub fn instrument_with_bolt(path: &Path, output_path: &Path) {
129
let status = Command::new("llvm-bolt")
1310
.arg("-instrument")
1411
.arg(&path)
1512
// Make sure that each process will write its profiles into a separate file
1613
.arg("--instrumentation-file-append-pid")
1714
.arg("-o")
18-
.arg(&instrumented_path)
15+
.arg(output_path)
1916
.status()
2017
.expect("Could not instrument artifact using BOLT");
2118

2219
if !status.success() {
2320
panic!("Could not instrument {} with BOLT, exit code {:?}", path.display(), status.code());
2421
}
25-
instrumented_path
2622
}
2723

28-
/// Uses the `llvm-bolt` binary to optimize the binary/library at the given `path` with BOLT,
24+
/// Uses the `llvm-bolt` binary to optimize the artifact at the given `path` with BOLT,
2925
/// using merged profiles from `profile_path`.
3026
///
3127
/// The recorded profiles have to be merged using the `merge-fdata` tool from LLVM and the merged
3228
/// profile path should be then passed to this function.
3329
///
34-
/// Returns a path to the optimized artifact, created in a temporary directory.
35-
pub fn optimize_with_bolt(path: &Path, profile_path: &Path) -> PathBuf {
36-
let dir = std::env::temp_dir();
37-
let optimized_path = dir.join(path.file_name().unwrap());
38-
30+
/// Creates the optimized artifact at `output_path`.
31+
pub fn optimize_with_bolt(path: &Path, profile_path: &Path, output_path: &Path) {
3932
let status = Command::new("llvm-bolt")
4033
.arg(&path)
4134
.arg("-data")
4235
.arg(&profile_path)
4336
.arg("-o")
44-
.arg(&optimized_path)
37+
.arg(output_path)
4538
// Reorder basic blocks within functions
4639
.arg("-reorder-blocks=ext-tsp")
4740
// Reorder functions within the binary
@@ -66,5 +59,4 @@ pub fn optimize_with_bolt(path: &Path, profile_path: &Path) -> PathBuf {
6659
if !status.success() {
6760
panic!("Could not optimize {} with BOLT, exit code {:?}", path.display(), status.code());
6861
}
69-
optimized_path
7062
}

src/bootstrap/dist.rs

+118-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use std::process::Command;
1818

1919
use object::read::archive::ArchiveFile;
2020
use object::BinaryFormat;
21+
use sha2::Digest;
2122

2223
use crate::bolt::{instrument_with_bolt, optimize_with_bolt};
2324
use crate::builder::{Builder, Kind, RunConfig, ShouldRun, Step};
@@ -1915,9 +1916,9 @@ fn install_llvm_file(builder: &Builder<'_>, source: &Path, destination: &Path) {
19151916
// We perform the instrumentation/optimization here, on the fly, just before they are being
19161917
// packaged into some destination directory.
19171918
let postprocessed = if builder.config.llvm_bolt_profile_generate {
1918-
instrument_with_bolt(source)
1919+
builder.ensure(BoltInstrument::new(source.to_path_buf()))
19191920
} else if let Some(path) = &builder.config.llvm_bolt_profile_use {
1920-
optimize_with_bolt(source, &Path::new(&path))
1921+
builder.ensure(BoltOptimize::new(source.to_path_buf(), path.into()))
19211922
} else {
19221923
source.to_path_buf()
19231924
};
@@ -2007,6 +2008,121 @@ pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection
20072008
}
20082009
}
20092010

2011+
/// Creates an output path to a BOLT-manipulated artifact for the given `file`.
2012+
/// The hash of the file is used to make sure that we don't mix BOLT artifacts amongst different
2013+
/// files with the same name.
2014+
///
2015+
/// We need to keep the file-name the same though, to make sure that copying the manipulated file
2016+
/// to a directory will not change the final file path.
2017+
fn create_bolt_output_path(builder: &Builder<'_>, file: &Path, hash: &str) -> PathBuf {
2018+
let directory = builder.out.join("bolt").join(hash);
2019+
t!(fs::create_dir_all(&directory));
2020+
directory.join(file.file_name().unwrap())
2021+
}
2022+
2023+
/// Instrument the provided file with BOLT.
2024+
/// Returns a path to the instrumented artifact.
2025+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2026+
pub struct BoltInstrument {
2027+
file: PathBuf,
2028+
hash: String,
2029+
}
2030+
2031+
impl BoltInstrument {
2032+
fn new(file: PathBuf) -> Self {
2033+
let mut hasher = sha2::Sha256::new();
2034+
hasher.update(t!(fs::read(&file)));
2035+
let hash = hex::encode(hasher.finalize().as_slice());
2036+
2037+
Self { file, hash }
2038+
}
2039+
}
2040+
2041+
impl Step for BoltInstrument {
2042+
type Output = PathBuf;
2043+
2044+
const ONLY_HOSTS: bool = false;
2045+
const DEFAULT: bool = false;
2046+
2047+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2048+
run.never()
2049+
}
2050+
2051+
fn make_run(_run: RunConfig<'_>) {}
2052+
2053+
fn run(self, builder: &Builder<'_>) -> PathBuf {
2054+
if builder.build.config.dry_run() {
2055+
return self.file.clone();
2056+
}
2057+
2058+
if builder.build.config.llvm_from_ci {
2059+
println!("warning: trying to use BOLT with LLVM from CI, this will probably not work");
2060+
}
2061+
2062+
println!("Instrumenting {} with BOLT", self.file.display());
2063+
2064+
let output_path = create_bolt_output_path(builder, &self.file, &self.hash);
2065+
if !output_path.is_file() {
2066+
instrument_with_bolt(&self.file, &output_path);
2067+
}
2068+
output_path
2069+
}
2070+
}
2071+
2072+
/// Optimize the provided file with BOLT.
2073+
/// Returns a path to the optimized artifact.
2074+
///
2075+
/// The hash is stored in the step to make sure that we don't optimize the same file
2076+
/// twice (even under different file paths).
2077+
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
2078+
pub struct BoltOptimize {
2079+
file: PathBuf,
2080+
profile: PathBuf,
2081+
hash: String,
2082+
}
2083+
2084+
impl BoltOptimize {
2085+
fn new(file: PathBuf, profile: PathBuf) -> Self {
2086+
let mut hasher = sha2::Sha256::new();
2087+
hasher.update(t!(fs::read(&file)));
2088+
hasher.update(t!(fs::read(&profile)));
2089+
let hash = hex::encode(hasher.finalize().as_slice());
2090+
2091+
Self { file, profile, hash }
2092+
}
2093+
}
2094+
2095+
impl Step for BoltOptimize {
2096+
type Output = PathBuf;
2097+
2098+
const ONLY_HOSTS: bool = false;
2099+
const DEFAULT: bool = false;
2100+
2101+
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
2102+
run.never()
2103+
}
2104+
2105+
fn make_run(_run: RunConfig<'_>) {}
2106+
2107+
fn run(self, builder: &Builder<'_>) -> PathBuf {
2108+
if builder.build.config.dry_run() {
2109+
return self.file.clone();
2110+
}
2111+
2112+
if builder.build.config.llvm_from_ci {
2113+
println!("warning: trying to use BOLT with LLVM from CI, this will probably not work");
2114+
}
2115+
2116+
println!("Optimizing {} with BOLT", self.file.display());
2117+
2118+
let output_path = create_bolt_output_path(builder, &self.file, &self.hash);
2119+
if !output_path.is_file() {
2120+
optimize_with_bolt(&self.file, &self.profile, &output_path);
2121+
}
2122+
output_path
2123+
}
2124+
}
2125+
20102126
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
20112127
pub struct LlvmTools {
20122128
pub target: TargetSelection,

0 commit comments

Comments
 (0)