Skip to content

Commit 8fc4a0d

Browse files
committed
rewrite pgo-branch-weights (1/2)
1 parent 3cb0030 commit 8fc4a0d

File tree

8 files changed

+224
-88
lines changed

8 files changed

+224
-88
lines changed

src/tools/run-make-support/src/lib.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
pub mod cc;
77
pub mod clang;
88
pub mod diff;
9-
pub mod llvm_readobj;
9+
pub mod llvm;
1010
pub mod run;
1111
pub mod rustc;
1212
pub mod rustdoc;
@@ -23,7 +23,7 @@ pub use wasmparser;
2323
pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc};
2424
pub use clang::{clang, Clang};
2525
pub use diff::{diff, Diff};
26-
pub use llvm_readobj::{llvm_readobj, LlvmReadobj};
26+
pub use llvm::{llvm_profdata, llvm_readobj, LlvmProfdata, LlvmReadobj};
2727
pub use run::{run, run_fail};
2828
pub use rustc::{aux_build, rustc, Rustc};
2929
pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
use std::env;
2+
use std::path::{Path, PathBuf};
3+
use std::process::Command;
4+
5+
use crate::handle_failed_output;
6+
7+
/// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available
8+
/// at `$LLVM_BIN_DIR/llvm-readobj`.
9+
pub fn llvm_readobj() -> LlvmReadobj {
10+
LlvmReadobj::new()
11+
}
12+
13+
/// Construct a new `llvm-profdata` invocation. This assumes that `llvm-profdata` is available
14+
/// at `$LLVM_BIN_DIR/llvm-profdata`.
15+
pub fn llvm_profdata() -> LlvmProfdata {
16+
LlvmProfdata::new()
17+
}
18+
19+
/// A `llvm-readobj` invocation builder.
20+
#[derive(Debug)]
21+
pub struct LlvmReadobj {
22+
cmd: Command,
23+
}
24+
25+
/// A `llvm-profdata` invocation builder.
26+
#[derive(Debug)]
27+
pub struct LlvmProfdata {
28+
cmd: Command,
29+
}
30+
31+
crate::impl_common_helpers!(LlvmReadobj);
32+
33+
/// Generate the path to the bin directory of LLVM.
34+
pub fn llvm_bin_dir() -> PathBuf {
35+
let llvm_bin_dir = env::var("LLVM_BIN_DIR")
36+
.expect("`LLVM_BIN_DIR` not specified, but this is required to find `llvm-readobj`");
37+
PathBuf::from(llvm_bin_dir)
38+
}
39+
40+
impl LlvmReadobj {
41+
/// Construct a new `llvm-readobj` invocation. This assumes that `llvm-readobj` is available
42+
/// at `$LLVM_BIN_DIR/llvm-readobj`.
43+
pub fn new() -> Self {
44+
let llvm_readobj = llvm_bin_dir().join("llvm-readobj");
45+
let cmd = Command::new(llvm_readobj);
46+
Self { cmd }
47+
}
48+
49+
/// Provide an input file.
50+
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
51+
self.cmd.arg(path.as_ref());
52+
self
53+
}
54+
55+
/// Pass `--file-header` to display file headers.
56+
pub fn file_header(&mut self) -> &mut Self {
57+
self.cmd.arg("--file-header");
58+
self
59+
}
60+
61+
/// Get the [`Output`][::std::process::Output] of the finished process.
62+
#[track_caller]
63+
pub fn command_output(&mut self) -> ::std::process::Output {
64+
self.cmd.output().expect("failed to get output of finished process")
65+
}
66+
}
67+
68+
impl LlvmProfdata {
69+
/// Construct a new `llvm-profdata` invocation. This assumes that `llvm-profdata` is available
70+
/// at `$LLVM_BIN_DIR/llvm-profdata`.
71+
pub fn new() -> Self {
72+
let llvm_profdata = llvm_bin_dir().join("llvm-profdata");
73+
let cmd = Command::new(llvm_profdata);
74+
Self { cmd }
75+
}
76+
77+
/// Provide an input file.
78+
pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
79+
self.cmd.arg("-o");
80+
self.cmd.arg(path.as_ref());
81+
self
82+
}
83+
84+
/// Specify the output file path.
85+
pub fn output<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
86+
self.cmd.arg(path.as_ref());
87+
self
88+
}
89+
90+
/// Take several profile data files generated by PGO instrumentation and merge them
91+
/// together into a single indexed profile data file.
92+
pub fn merge(&mut self) -> &mut Self {
93+
self.cmd.arg("merge");
94+
self
95+
}
96+
97+
/// Get the [`Output`][::std::process::Output] of the finished process.
98+
#[track_caller]
99+
pub fn command_output(&mut self) -> ::std::process::Output {
100+
self.cmd.output().expect("failed to get output of finished process")
101+
}
102+
}

src/tools/run-make-support/src/llvm_readobj.rs

-50
This file was deleted.

src/tools/run-make-support/src/run.rs

+35
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
use std::env;
22
use std::path::{Path, PathBuf};
33
use std::process::{Command, Output};
4+
use std::io::{BufReader, Read, Write};
5+
use std::fs::File;
6+
use std::ffi::OsStr;
47

58
use crate::is_windows;
69

@@ -37,6 +40,38 @@ fn run_common(name: &str) -> (Command, Output) {
3740
(cmd, output)
3841
}
3942

43+
fn read_bytes<R: Read>(mut reader: R) -> Result<Vec<u8>, std::io::Error> {
44+
let mut buffer = Vec::new();
45+
reader.read_to_end(&mut buffer)?;
46+
Ok(buffer)
47+
}
48+
49+
/// Run a command taking in standard input from a file, and passing an argument
50+
/// referring to a different file.
51+
pub fn stdin_command<P>(command: &str, stdin: P, argument: P)
52+
where
53+
P: AsRef<Path> + AsRef<OsStr> + std::convert::AsRef<std::ffi::OsStr>,
54+
{
55+
let file = File::open(stdin).unwrap();
56+
let reader = BufReader::new(file);
57+
58+
let mut child = Command::new(command)
59+
.stdin(std::process::Stdio::piped())
60+
.stdout(std::process::Stdio::piped())
61+
.stderr(std::process::Stdio::piped())
62+
.arg(argument)
63+
.spawn()
64+
.unwrap();
65+
66+
let mut child_stdin = child.stdin.take().unwrap();
67+
let byte_vec = read_bytes(reader).expect("failed to read bytes of standard input");
68+
let byte_slice = byte_vec.as_slice();
69+
child_stdin.write_all(byte_slice).unwrap();
70+
child_stdin.flush().unwrap();
71+
72+
child.wait_with_output().unwrap();
73+
}
74+
4075
/// Run a built binary and make sure it succeeds.
4176
#[track_caller]
4277
pub fn run(name: &str) -> Output {

src/tools/run-make-support/src/rustc.rs

+18
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,24 @@ impl Rustc {
125125
self
126126
}
127127

128+
/// Specify directory path used for profile generation
129+
pub fn profile_generate<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
130+
let mut arg = OsString::new();
131+
arg.push("-Cprofile-generate=");
132+
arg.push(path.as_ref());
133+
self.cmd.arg(&arg);
134+
self
135+
}
136+
137+
/// Specify directory path used for profile usage
138+
pub fn profile_use<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
139+
let mut arg = OsString::new();
140+
arg.push("-Cprofile-use=");
141+
arg.push(path.as_ref());
142+
self.cmd.arg(&arg);
143+
self
144+
}
145+
128146
/// Specify error format to use
129147
pub fn error_format(&mut self, format: &str) -> &mut Self {
130148
self.cmd.arg(format!("--error-format={format}"));

src/tools/tidy/src/allowed_run_make_makefiles.txt

-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,6 @@ run-make/pass-linker-flags/Makefile
199199
run-make/pass-non-c-like-enum-to-c/Makefile
200200
run-make/pdb-alt-path/Makefile
201201
run-make/pdb-buildinfo-cl-cmd/Makefile
202-
run-make/pgo-branch-weights/Makefile
203202
run-make/pgo-gen-lto/Makefile
204203
run-make/pgo-gen-no-imp-symbols/Makefile
205204
run-make/pgo-gen/Makefile

tests/run-make/pgo-branch-weights/Makefile

-35
This file was deleted.
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// This test generates an instrumented binary - a program which
2+
// will keep track of how many times it calls each function, a useful
3+
// feature for optimization. Then, an argument (aaaaaaaaaaaa2bbbbbbbbbbbb2bbbbbbbbbbbbbbbbcc)
4+
// is passed into the instrumented binary, which should react with a number of function calls
5+
// fully known in advance. (For example, the letter 'a' results in calling f1())
6+
7+
// If the test passes, the expected function call count was added to the use-phase LLVM-IR.
8+
// See https://github.com/rust-lang/rust/pull/66631
9+
10+
// FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works
11+
// properly. Since we only have GCC on the CI ignore the test for now.
12+
13+
//@ needs-profiler-support
14+
//@ ignore-windows-gnu
15+
//@ ignore-cross-compile
16+
17+
use run_make_support::{llvm_profdata, rustc, rustdoc, stdin_command, target, tmp_dir};
18+
19+
fn main() {
20+
// For some very small programs GNU ld seems to not properly handle
21+
// instrumentation sections correctly. Neither Gold nor LLD have that
22+
// problem.
23+
let link_args = (cfg!(target_os = "linux") && target().contains("x86"))
24+
.then_some("-Clink-args=-fuse-ld=gold");
25+
26+
let path_prof_data_dir = tmp_dir().join("prof_data_dir");
27+
let path_merged_profdata = path_prof_data_dir.join("merged.profdata");
28+
let invoc = rustc().input("opaque.rs");
29+
if let Some(link_args) = link_args {
30+
invoc.arg(link_args);
31+
}
32+
invoc.run();
33+
fs::create_dir_all(&path_prof_data_dir);
34+
let invoc = rustc()
35+
.input("interesting.rs")
36+
.profile_generate(&path_prof_data_dir)
37+
.opt()
38+
.codegen_units(1);
39+
if let Some(link_args) = link_args {
40+
invoc.arg(link_args);
41+
}
42+
invoc.run();
43+
let invoc = rustc().input("main.rs").profile_generate(&path_prof_data_dir).opt();
44+
if let Some(link_args) = link_args {
45+
invoc.arg(link_args);
46+
}
47+
invoc.run();
48+
run("main aaaaaaaaaaaa2bbbbbbbbbbbb2bbbbbbbbbbbbbbbbcc");
49+
llvm_profdata()
50+
.merge()
51+
.output(&path_merged_profdata)
52+
.input(path_prof_data_dir)
53+
.command_output();
54+
let invoc = rustc()
55+
.input("interesting.rs")
56+
.profile_use(path_merged_profdata)
57+
.opt()
58+
.codegen_units(1)
59+
.emit("llvm-ir");
60+
if let Some(link_args) = link_args {
61+
invoc.arg(link_args);
62+
}
63+
invoc.run();
64+
65+
let interesting_ll = tmp_dir().join("interesting.ll");
66+
stdin_command(env::var("LLVM_FILECHECK"), interesting_ll, "filecheck-patterns.txt");
67+
}

0 commit comments

Comments
 (0)