Enable AutoFDO.

This largely involves implementing the options debug-info-for-profiling
and profile-sample-use and forwarding them on to LLVM.

AutoFDO can be used on x86-64 Linux like this:
rustc -O -Cdebug-info-for-profiling main.rs -o main
perf record -b ./main
create_llvm_prof --binary=main --out=code.prof
rustc -O -Cprofile-sample-use=code.prof main.rs -o main2

Now `main2` will have feedback directed optimization applied to it.

The create_llvm_prof tool can be obtained from this github repository:
https://github.com/google/autofdo

Fixes #64892.
This commit is contained in:
Michael Benfield 2021-05-07 07:41:37 +00:00
parent d7539a6af0
commit a17193dbb9
12 changed files with 120 additions and 9 deletions

View file

@ -263,6 +263,10 @@ pub fn from_fn_attrs(cx: &CodegenCx<'ll, 'tcx>, llfn: &'ll Value, instance: ty::
attributes::emit_uwtable(llfn, true);
}
if cx.sess().opts.debugging_opts.profile_sample_use.is_some() {
llvm::AddFunctionAttrString(llfn, Function, cstr!("use-sample-profile"));
}
// FIXME: none of these three functions interact with source level attributes.
set_frame_pointer_type(cx, llfn);
set_instrument_function(cx, llfn);

View file

@ -370,6 +370,13 @@ fn get_pgo_use_path(config: &ModuleConfig) -> Option<CString> {
.map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap())
}
fn get_pgo_sample_use_path(config: &ModuleConfig) -> Option<CString> {
config
.pgo_sample_use
.as_ref()
.map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap())
}
pub(crate) fn should_use_new_llvm_pass_manager(config: &ModuleConfig) -> bool {
// The new pass manager is enabled by default for LLVM >= 13.
// This matches Clang, which also enables it since Clang 13.
@ -389,6 +396,7 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
let using_thin_buffers = opt_stage == llvm::OptStage::PreLinkThinLTO || config.bitcode_needed();
let pgo_gen_path = get_pgo_gen_path(config);
let pgo_use_path = get_pgo_use_path(config);
let pgo_sample_use_path = get_pgo_sample_use_path(config);
let is_lto = opt_stage == llvm::OptStage::ThinLTO || opt_stage == llvm::OptStage::FatLTO;
// Sanitizer instrumentation is only inserted during the pre-link optimization stage.
let sanitizer_options = if !is_lto {
@ -439,6 +447,8 @@ pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
config.instrument_coverage,
config.instrument_gcov,
pgo_sample_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
config.debug_info_for_profiling,
llvm_selfprofiler,
selfprofile_before_pass_callback,
selfprofile_after_pass_callback,
@ -544,6 +554,9 @@ pub(crate) unsafe fn optimize(
if config.instrument_coverage {
llvm::LLVMRustAddPass(mpm, find_pass("instrprof").unwrap());
}
if config.debug_info_for_profiling {
llvm::LLVMRustAddPass(mpm, find_pass("add-discriminators").unwrap());
}
add_sanitizer_passes(config, &mut extra_passes);
@ -1001,6 +1014,7 @@ pub unsafe fn with_llvm_pmb(
let inline_threshold = config.inline_threshold;
let pgo_gen_path = get_pgo_gen_path(config);
let pgo_use_path = get_pgo_use_path(config);
let pgo_sample_use_path = get_pgo_sample_use_path(config);
llvm::LLVMRustConfigurePassManagerBuilder(
builder,
@ -1011,6 +1025,7 @@ pub unsafe fn with_llvm_pmb(
prepare_for_thin_lto,
pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
pgo_sample_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
);
llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32);

View file

@ -2176,6 +2176,7 @@ extern "C" {
PrepareForThinLTO: bool,
PGOGenPath: *const c_char,
PGOUsePath: *const c_char,
PGOSampleUsePath: *const c_char,
);
pub fn LLVMRustAddLibraryInfo(
PM: &PassManager<'a>,
@ -2210,6 +2211,8 @@ extern "C" {
PGOUsePath: *const c_char,
InstrumentCoverage: bool,
InstrumentGCOV: bool,
PGOSampleUsePath: *const c_char,
DebugInfoForProfiling: bool,
llvm_selfprofiler: *mut c_void,
begin_callback: SelfProfileBeforePassCallback,
end_callback: SelfProfileAfterPassCallback,