Skip to content

Commit

Permalink
Add support for new pass manager
Browse files Browse the repository at this point in the history
The new pass manager can be enabled using
-Z new-llvm-pass-manager=on.
  • Loading branch information
nikic committed Feb 12, 2020
1 parent 737f08b commit c6b0803
Show file tree
Hide file tree
Showing 7 changed files with 422 additions and 23 deletions.
14 changes: 14 additions & 0 deletions src/librustc_codegen_llvm/back/lto.rs
Expand Up @@ -584,6 +584,20 @@ pub(crate) fn run_pass_manager(
// tools/lto/LTOCodeGenerator.cpp
debug!("running the pass manager");
unsafe {
if write::should_use_new_llvm_pass_manager(config) {
let opt_stage = if thin { llvm::OptStage::ThinLTO } else { llvm::OptStage::FatLTO };
let opt_level = config.opt_level.unwrap_or(config::OptLevel::No);
// See comment below for why this is necessary.
let opt_level = if let config::OptLevel::No = opt_level {
config::OptLevel::Less
} else {
opt_level
};
write::optimize_with_new_llvm_pass_manager(module, config, opt_level, opt_stage);
debug!("lto done");
return;
}

let pm = llvm::LLVMCreatePassManager();
llvm::LLVMAddAnalysisPasses(module.module_llvm.tm, pm);

Expand Down
125 changes: 107 additions & 18 deletions src/librustc_codegen_llvm/back/write.rs
Expand Up @@ -111,6 +111,18 @@ pub fn to_llvm_opt_settings(
}
}

fn to_pass_builder_opt_level(cfg: config::OptLevel) -> llvm::PassBuilderOptLevel {
use config::OptLevel::*;
match cfg {
No => llvm::PassBuilderOptLevel::O0,
Less => llvm::PassBuilderOptLevel::O1,
Default => llvm::PassBuilderOptLevel::O2,
Aggressive => llvm::PassBuilderOptLevel::O3,
Size => llvm::PassBuilderOptLevel::Os,
SizeMin => llvm::PassBuilderOptLevel::Oz,
}
}

// If find_features is true this won't access `sess.crate_types` by assuming
// that `is_pie_binary` is false. When we discover LLVM target features
// `sess.crate_types` is uninitialized so we cannot access it.
Expand Down Expand Up @@ -303,6 +315,88 @@ unsafe extern "C" fn diagnostic_handler(info: &DiagnosticInfo, user: *mut c_void
}
}

fn get_pgo_gen_path(config: &ModuleConfig) -> Option<CString> {
match config.pgo_gen {
SwitchWithOptPath::Enabled(ref opt_dir_path) => {
let path = if let Some(dir_path) = opt_dir_path {
dir_path.join("default_%m.profraw")
} else {
PathBuf::from("default_%m.profraw")
};

Some(CString::new(format!("{}", path.display())).unwrap())
}
SwitchWithOptPath::Disabled => None,
}
}

fn get_pgo_use_path(config: &ModuleConfig) -> Option<CString> {
config
.pgo_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 {
// We only support the new pass manager starting with LLVM 9.
if llvm_util::get_major_version() < 9 {
return false;
}

// The new pass manager is disabled by default.
config.new_llvm_pass_manager.unwrap_or(false)
}

pub(crate) unsafe fn optimize_with_new_llvm_pass_manager(
module: &ModuleCodegen<ModuleLlvm>,
config: &ModuleConfig,
opt_level: config::OptLevel,
opt_stage: llvm::OptStage,
) {
let unroll_loops =
opt_level != config::OptLevel::Size && opt_level != config::OptLevel::SizeMin;
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 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 {
config.sanitizer.as_ref().map(|s| llvm::SanitizerOptions {
sanitize_memory: *s == Sanitizer::Memory,
sanitize_thread: *s == Sanitizer::Thread,
sanitize_address: *s == Sanitizer::Address,
sanitize_recover: config.sanitizer_recover.contains(s),
sanitize_memory_track_origins: config.sanitizer_memory_track_origins as c_int,
})
} else {
None
};

// FIXME: NewPM doesn't provide a facility to pass custom InlineParams.
// We would have to add upstream support for this first, before we can support
// config.inline_threshold and our more aggressive default thresholds.
// FIXME: NewPM uses an different and more explicit way to textually represent
// pass pipelines. It would probably make sense to expose this, but it would
// require a different format than the current -C passes.
llvm::LLVMRustOptimizeWithNewPassManager(
module.module_llvm.llmod(),
&*module.module_llvm.tm,
to_pass_builder_opt_level(opt_level),
opt_stage,
config.no_prepopulate_passes,
config.verify_llvm_ir,
using_thin_buffers,
config.merge_functions,
unroll_loops,
config.vectorize_slp,
config.vectorize_loop,
config.no_builtins,
sanitizer_options.as_ref(),
pgo_gen_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
pgo_use_path.as_ref().map_or(std::ptr::null(), |s| s.as_ptr()),
);
}

// Unsafe due to LLVM calls.
pub(crate) unsafe fn optimize(
cgcx: &CodegenContext<LlvmCodegenBackend>,
Expand All @@ -327,6 +421,17 @@ pub(crate) unsafe fn optimize(
}

if let Some(opt_level) = config.opt_level {
if should_use_new_llvm_pass_manager(config) {
let opt_stage = match cgcx.lto {
Lto::Fat => llvm::OptStage::PreLinkFatLTO,
Lto::Thin | Lto::ThinLocal => llvm::OptStage::PreLinkThinLTO,
_ if cgcx.opts.cg.linker_plugin_lto.enabled() => llvm::OptStage::PreLinkThinLTO,
_ => llvm::OptStage::PreLinkNoLTO,
};
optimize_with_new_llvm_pass_manager(module, config, opt_level, opt_stage);
return Ok(());
}

// Create the two optimizing pass managers. These mirror what clang
// does, and are by populated by LLVM's default PassManagerBuilder.
// Each manager has a different set of passes, but they also share
Expand Down Expand Up @@ -757,24 +862,8 @@ pub unsafe fn with_llvm_pmb(
let opt_size =
config.opt_size.map(|x| to_llvm_opt_settings(x).1).unwrap_or(llvm::CodeGenOptSizeNone);
let inline_threshold = config.inline_threshold;

let pgo_gen_path = match config.pgo_gen {
SwitchWithOptPath::Enabled(ref opt_dir_path) => {
let path = if let Some(dir_path) = opt_dir_path {
dir_path.join("default_%m.profraw")
} else {
PathBuf::from("default_%m.profraw")
};

Some(CString::new(format!("{}", path.display())).unwrap())
}
SwitchWithOptPath::Disabled => None,
};

let pgo_use_path = config
.pgo_use
.as_ref()
.map(|path_buf| CString::new(path_buf.to_string_lossy().as_bytes()).unwrap());
let pgo_gen_path = get_pgo_gen_path(config);
let pgo_use_path = get_pgo_use_path(config);

llvm::LLVMRustConfigurePassManagerBuilder(
builder,
Expand Down
49 changes: 49 additions & 0 deletions src/librustc_codegen_llvm/llvm/ffi.rs
Expand Up @@ -402,6 +402,38 @@ pub enum CodeGenOptLevel {
Aggressive,
}

/// LLVMRustPassBuilderOptLevel
#[repr(C)]
pub enum PassBuilderOptLevel {
O0,
O1,
O2,
O3,
Os,
Oz,
}

/// LLVMRustOptStage
#[derive(PartialEq)]
#[repr(C)]
pub enum OptStage {
PreLinkNoLTO,
PreLinkThinLTO,
PreLinkFatLTO,
ThinLTO,
FatLTO,
}

/// LLVMRustSanitizerOptions
#[repr(C)]
pub struct SanitizerOptions {
pub sanitize_memory: bool,
pub sanitize_thread: bool,
pub sanitize_address: bool,
pub sanitize_recover: bool,
pub sanitize_memory_track_origins: c_int,
}

/// LLVMRelocMode
#[derive(Copy, Clone, PartialEq)]
#[repr(C)]
Expand Down Expand Up @@ -1896,6 +1928,23 @@ extern "C" {
Output: *const c_char,
FileType: FileType,
) -> LLVMRustResult;
pub fn LLVMRustOptimizeWithNewPassManager(
M: &'a Module,
TM: &'a TargetMachine,
OptLevel: PassBuilderOptLevel,
OptStage: OptStage,
NoPrepopulatePasses: bool,
VerifyIR: bool,
UseThinLTOBuffers: bool,
MergeFunctions: bool,
UnrollLoops: bool,
SLPVectorize: bool,
LoopVectorize: bool,
DisableSimplifyLibCalls: bool,
SanitizerOptions: Option<&SanitizerOptions>,
PGOGenPath: *const c_char,
PGOUsePath: *const c_char,
);
pub fn LLVMRustPrintModule(
M: &'a Module,
Output: *const c_char,
Expand Down
3 changes: 3 additions & 0 deletions src/librustc_codegen_ssa/back/write.rs
Expand Up @@ -88,6 +88,7 @@ pub struct ModuleConfig {
pub vectorize_slp: bool,
pub merge_functions: bool,
pub inline_threshold: Option<usize>,
pub new_llvm_pass_manager: Option<bool>,
// Instead of creating an object file by doing LLVM codegen, just
// make the object file bitcode. Provides easy compatibility with
// emscripten's ecc compiler, when used as the linker.
Expand Down Expand Up @@ -132,6 +133,7 @@ impl ModuleConfig {
vectorize_slp: false,
merge_functions: false,
inline_threshold: None,
new_llvm_pass_manager: None,
}
}

Expand All @@ -140,6 +142,7 @@ impl ModuleConfig {
self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes;
self.no_builtins = no_builtins || sess.target.target.options.no_builtins;
self.inline_threshold = sess.opts.cg.inline_threshold;
self.new_llvm_pass_manager = sess.opts.debugging_opts.new_llvm_pass_manager;
self.obj_is_bitcode =
sess.target.target.options.obj_is_bitcode || sess.opts.cg.linker_plugin_lto.enabled();
let embed_bitcode =
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_session/options.rs
Expand Up @@ -968,4 +968,6 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"compile without linking"),
link_only: bool = (false, parse_bool, [TRACKED],
"link the `.rlink` file generated by `-Z no-link`"),
new_llvm_pass_manager: Option<bool> = (None, parse_opt_bool, [TRACKED],
"use new LLVM pass manager"),
}

0 comments on commit c6b0803

Please sign in to comment.