Skip to content

Commit 13a86f4

Browse files
committed
Auto merge of #48346 - emilio:pgo, r=alexcrichton
Add basic PGO support. This PR adds two mutually exclusive options for profile usage and generation using LLVM's instruction profile generation (the same as clang uses), `-C pgo-use` and `-C pgo-gen`. See each commit for details.
2 parents 5e4603f + 1e1d907 commit 13a86f4

File tree

18 files changed

+212
-10
lines changed

18 files changed

+212
-10
lines changed

src/libprofiler_builtins/build.rs

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ fn main() {
2727
"InstrProfilingFile.c",
2828
"InstrProfilingMerge.c",
2929
"InstrProfilingMergeFile.c",
30+
"InstrProfilingNameVar.c",
3031
"InstrProfilingPlatformDarwin.c",
3132
"InstrProfilingPlatformLinux.c",
3233
"InstrProfilingPlatformOther.c",
@@ -42,6 +43,8 @@ fn main() {
4243
cfg.define("strdup", Some("_strdup"));
4344
cfg.define("open", Some("_open"));
4445
cfg.define("fdopen", Some("_fdopen"));
46+
cfg.define("getpid", Some("_getpid"));
47+
cfg.define("fileno", Some("_fileno"));
4548
} else {
4649
// Turn off various features of gcc and such, mostly copying
4750
// compiler-rt's build system already
@@ -50,6 +53,7 @@ fn main() {
5053
cfg.flag("-fomit-frame-pointer");
5154
cfg.flag("-ffreestanding");
5255
cfg.define("VISIBILITY_HIDDEN", None);
56+
cfg.define("COMPILER_RT_HAS_UNAME", Some("1"));
5357
}
5458

5559
for src in profile_sources {

src/librustc/session/config.rs

+23
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,14 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
12491249
"extra arguments to prepend to the linker invocation (space separated)"),
12501250
profile: bool = (false, parse_bool, [TRACKED],
12511251
"insert profiling code"),
1252+
pgo_gen: Option<String> = (None, parse_opt_string, [TRACKED],
1253+
"Generate PGO profile data, to a given file, or to the default \
1254+
location if it's empty."),
1255+
pgo_use: String = (String::new(), parse_string, [TRACKED],
1256+
"Use PGO profile data from the given profile file."),
1257+
disable_instrumentation_preinliner: bool =
1258+
(false, parse_bool, [TRACKED], "Disable the instrumentation pre-inliner, \
1259+
useful for profiling / PGO."),
12521260
relro_level: Option<RelroLevel> = (None, parse_relro_level, [TRACKED],
12531261
"choose which RELRO level to use"),
12541262
nll: bool = (false, parse_bool, [UNTRACKED],
@@ -1773,6 +1781,13 @@ pub fn build_session_options_and_crate_config(
17731781
);
17741782
}
17751783

1784+
if debugging_opts.pgo_gen.is_some() && !debugging_opts.pgo_use.is_empty() {
1785+
early_error(
1786+
error_format,
1787+
"options `-Z pgo-gen` and `-Z pgo-use` are exclusive",
1788+
);
1789+
}
1790+
17761791
let mut output_types = BTreeMap::new();
17771792
if !debugging_opts.parse_only {
17781793
for list in matches.opt_strs("emit") {
@@ -2886,6 +2901,14 @@ mod tests {
28862901
opts.debugging_opts.tls_model = Some(String::from("tls model"));
28872902
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());
28882903

2904+
opts = reference.clone();
2905+
opts.debugging_opts.pgo_gen = Some(String::from("abc"));
2906+
assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
2907+
2908+
opts = reference.clone();
2909+
opts.debugging_opts.pgo_use = String::from("abc");
2910+
assert_ne!(reference.dep_tracking_hash(), opts.dep_tracking_hash());
2911+
28892912
opts = reference.clone();
28902913
opts.cg.metadata = vec![String::from("A"), String::from("B")];
28912914
assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash());

src/librustc_llvm/diagnostic.rs

+5
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ impl InlineAsmDiagnostic {
121121
pub enum Diagnostic {
122122
Optimization(OptimizationDiagnostic),
123123
InlineAsm(InlineAsmDiagnostic),
124+
PGO(DiagnosticInfoRef),
124125

125126
/// LLVM has other types that we do not wrap here.
126127
UnknownDiagnostic(DiagnosticInfoRef),
@@ -160,6 +161,10 @@ impl Diagnostic {
160161
Optimization(OptimizationDiagnostic::unpack(OptimizationFailure, di))
161162
}
162163

164+
Dk::PGOProfile => {
165+
PGO(di)
166+
}
167+
163168
_ => UnknownDiagnostic(di),
164169
}
165170
}

src/librustc_llvm/ffi.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ pub enum DiagnosticKind {
322322
OptimizationRemarkAnalysisAliasing,
323323
OptimizationRemarkOther,
324324
OptimizationFailure,
325+
PGOProfile,
325326
}
326327

327328
/// LLVMRustArchiveKind
@@ -1646,7 +1647,9 @@ extern "C" {
16461647
OptLevel: CodeGenOptLevel,
16471648
MergeFunctions: bool,
16481649
SLPVectorize: bool,
1649-
LoopVectorize: bool);
1650+
LoopVectorize: bool,
1651+
PGOGenPath: *const c_char,
1652+
PGOUsePath: *const c_char);
16501653
pub fn LLVMRustAddLibraryInfo(PM: PassManagerRef,
16511654
M: ModuleRef,
16521655
DisableSimplifyLibCalls: bool);
@@ -1741,6 +1744,7 @@ extern "C" {
17411744
pub fn LLVMRustModuleCost(M: ModuleRef) -> u64;
17421745

17431746
pub fn LLVMRustThinLTOAvailable() -> bool;
1747+
pub fn LLVMRustPGOAvailable() -> bool;
17441748
pub fn LLVMRustWriteThinBitcodeToFile(PMR: PassManagerRef,
17451749
M: ModuleRef,
17461750
BC: *const c_char) -> bool;

src/librustc_metadata/creader.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,9 @@ impl<'a> CrateLoader<'a> {
784784
}
785785

786786
fn inject_profiler_runtime(&mut self) {
787-
if self.sess.opts.debugging_opts.profile {
787+
if self.sess.opts.debugging_opts.profile ||
788+
self.sess.opts.debugging_opts.pgo_gen.is_some()
789+
{
788790
info!("loading profiler");
789791

790792
let symbol = Symbol::intern("profiler_builtins");

src/librustc_trans/attributes.rs

+5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@ pub fn set_probestack(cx: &CodegenCx, llfn: ValueRef) {
9292
_ => {}
9393
}
9494

95+
// probestack doesn't play nice either with pgo-gen.
96+
if cx.sess().opts.debugging_opts.pgo_gen.is_some() {
97+
return;
98+
}
99+
95100
// Flag our internal `__rust_probestack` function as the stack probe symbol.
96101
// This is defined in the `compiler-builtins` crate for each architecture.
97102
llvm::AddFunctionAttrStringValue(

src/librustc_trans/back/link.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1085,6 +1085,10 @@ fn link_args(cmd: &mut Linker,
10851085
cmd.build_static_executable();
10861086
}
10871087

1088+
if sess.opts.debugging_opts.pgo_gen.is_some() {
1089+
cmd.pgo_gen();
1090+
}
1091+
10881092
// FIXME (#2397): At some point we want to rpath our guesses as to
10891093
// where extern libraries might live, based on the
10901094
// addl_lib_search_paths

src/librustc_trans/back/linker.rs

+30
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ pub trait Linker {
117117
fn partial_relro(&mut self);
118118
fn no_relro(&mut self);
119119
fn optimize(&mut self);
120+
fn pgo_gen(&mut self);
120121
fn debuginfo(&mut self);
121122
fn no_default_libraries(&mut self);
122123
fn build_dylib(&mut self, out_filename: &Path);
@@ -280,6 +281,24 @@ impl<'a> Linker for GccLinker<'a> {
280281
}
281282
}
282283

284+
fn pgo_gen(&mut self) {
285+
if !self.sess.target.target.options.linker_is_gnu { return }
286+
287+
// If we're doing PGO generation stuff and on a GNU-like linker, use the
288+
// "-u" flag to properly pull in the profiler runtime bits.
289+
//
290+
// This is because LLVM otherwise won't add the needed initialization
291+
// for us on Linux (though the extra flag should be harmless if it
292+
// does).
293+
//
294+
// See https://reviews.llvm.org/D14033 and https://reviews.llvm.org/D14030.
295+
//
296+
// Though it may be worth to try to revert those changes upstream, since
297+
// the overhead of the initialization should be minor.
298+
self.cmd.arg("-u");
299+
self.cmd.arg("__llvm_profile_runtime");
300+
}
301+
283302
fn debuginfo(&mut self) {
284303
match self.sess.opts.debuginfo {
285304
DebugInfoLevel::NoDebugInfo => {
@@ -520,6 +539,10 @@ impl<'a> Linker for MsvcLinker<'a> {
520539
// Needs more investigation of `/OPT` arguments
521540
}
522541

542+
fn pgo_gen(&mut self) {
543+
// Nothing needed here.
544+
}
545+
523546
fn debuginfo(&mut self) {
524547
// This will cause the Microsoft linker to generate a PDB file
525548
// from the CodeView line tables in the object files.
@@ -723,6 +746,10 @@ impl<'a> Linker for EmLinker<'a> {
723746
self.cmd.args(&["--memory-init-file", "0"]);
724747
}
725748

749+
fn pgo_gen(&mut self) {
750+
// noop, but maybe we need something like the gnu linker?
751+
}
752+
726753
fn debuginfo(&mut self) {
727754
// Preserve names or generate source maps depending on debug info
728755
self.cmd.arg(match self.sess.opts.debuginfo {
@@ -888,6 +915,9 @@ impl Linker for WasmLd {
888915
fn optimize(&mut self) {
889916
}
890917

918+
fn pgo_gen(&mut self) {
919+
}
920+
891921
fn debuginfo(&mut self) {
892922
}
893923

src/librustc_trans/back/symbol_export.rs

+14
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,20 @@ fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
223223
}
224224
}
225225

226+
if tcx.sess.opts.debugging_opts.pgo_gen.is_some() {
227+
// These are weak symbols that point to the profile version and the
228+
// profile name, which need to be treated as exported so LTO doesn't nix
229+
// them.
230+
const PROFILER_WEAK_SYMBOLS: [&'static str; 2] = [
231+
"__llvm_profile_raw_version",
232+
"__llvm_profile_filename",
233+
];
234+
for sym in &PROFILER_WEAK_SYMBOLS {
235+
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(sym));
236+
symbols.push((exported_symbol, SymbolExportLevel::C));
237+
}
238+
}
239+
226240
if tcx.sess.crate_types.borrow().contains(&config::CrateTypeDylib) {
227241
let symbol_name = metadata_symbol_name(tcx);
228242
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name));

src/librustc_trans/back/write.rs

+39-7
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,9 @@ pub struct ModuleConfig {
240240
/// Some(level) to optimize binary size, or None to not affect program size.
241241
opt_size: Option<llvm::CodeGenOptSize>,
242242

243+
pgo_gen: Option<String>,
244+
pgo_use: String,
245+
243246
// Flags indicating which outputs to produce.
244247
emit_no_opt_bc: bool,
245248
emit_bc: bool,
@@ -274,6 +277,9 @@ impl ModuleConfig {
274277
opt_level: None,
275278
opt_size: None,
276279

280+
pgo_gen: None,
281+
pgo_use: String::new(),
282+
277283
emit_no_opt_bc: false,
278284
emit_bc: false,
279285
emit_bc_compressed: false,
@@ -492,8 +498,13 @@ unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_vo
492498
opt.message));
493499
}
494500
}
495-
496-
_ => (),
501+
llvm::diagnostic::PGO(diagnostic_ref) => {
502+
let msg = llvm::build_string(|s| {
503+
llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s)
504+
}).expect("non-UTF8 PGO diagnostic");
505+
diag_handler.warn(&msg);
506+
}
507+
llvm::diagnostic::UnknownDiagnostic(..) => {},
497508
}
498509
}
499510

@@ -932,6 +943,9 @@ pub fn start_async_translation(tcx: TyCtxt,
932943
modules_config.passes.push("insert-gcov-profiling".to_owned())
933944
}
934945

946+
modules_config.pgo_gen = sess.opts.debugging_opts.pgo_gen.clone();
947+
modules_config.pgo_use = sess.opts.debugging_opts.pgo_use.clone();
948+
935949
modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize));
936950
modules_config.opt_size = Some(get_llvm_opt_size(sess.opts.optimize));
937951

@@ -2046,18 +2060,36 @@ pub unsafe fn with_llvm_pmb(llmod: ModuleRef,
20462060
config: &ModuleConfig,
20472061
opt_level: llvm::CodeGenOptLevel,
20482062
f: &mut FnMut(llvm::PassManagerBuilderRef)) {
2063+
use std::ptr;
2064+
20492065
// Create the PassManagerBuilder for LLVM. We configure it with
20502066
// reasonable defaults and prepare it to actually populate the pass
20512067
// manager.
20522068
let builder = llvm::LLVMPassManagerBuilderCreate();
20532069
let opt_size = config.opt_size.unwrap_or(llvm::CodeGenOptSizeNone);
20542070
let inline_threshold = config.inline_threshold;
20552071

2056-
llvm::LLVMRustConfigurePassManagerBuilder(builder,
2057-
opt_level,
2058-
config.merge_functions,
2059-
config.vectorize_slp,
2060-
config.vectorize_loop);
2072+
let pgo_gen_path = config.pgo_gen.as_ref().map(|s| {
2073+
let s = if s.is_empty() { "default_%m.profraw" } else { s };
2074+
CString::new(s.as_bytes()).unwrap()
2075+
});
2076+
2077+
let pgo_use_path = if config.pgo_use.is_empty() {
2078+
None
2079+
} else {
2080+
Some(CString::new(config.pgo_use.as_bytes()).unwrap())
2081+
};
2082+
2083+
llvm::LLVMRustConfigurePassManagerBuilder(
2084+
builder,
2085+
opt_level,
2086+
config.merge_functions,
2087+
config.vectorize_slp,
2088+
config.vectorize_loop,
2089+
pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
2090+
pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
2091+
);
2092+
20612093
llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32);
20622094

20632095
if opt_size != llvm::CodeGenOptSizeNone {

src/librustc_trans/base.rs

+7
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,13 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
708708
}
709709
}
710710

711+
if (tcx.sess.opts.debugging_opts.pgo_gen.is_some() ||
712+
!tcx.sess.opts.debugging_opts.pgo_use.is_empty()) &&
713+
unsafe { !llvm::LLVMRustPGOAvailable() }
714+
{
715+
tcx.sess.fatal("this compiler's LLVM does not support PGO");
716+
}
717+
711718
let crate_hash = tcx.crate_hash(LOCAL_CRATE);
712719
let link_meta = link::build_link_meta(crate_hash);
713720

src/librustc_trans/llvm_util.rs

+3
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ unsafe fn configure_llvm(sess: &Session) {
6161
add("rustc"); // fake program name
6262
if sess.time_llvm_passes() { add("-time-passes"); }
6363
if sess.print_llvm_passes() { add("-debug-pass=Structure"); }
64+
if sess.opts.debugging_opts.disable_instrumentation_preinliner {
65+
add("-disable-preinline");
66+
}
6467

6568
for arg in &sess.opts.cg.llvm_args {
6669
add(&(*arg));

0 commit comments

Comments
 (0)