Auto merge of #55641 - nagisa:optimize-attr, r=pnkfelix
Implement optimize(size) and optimize(speed) attributes This PR implements both `optimize(size)` and `optimize(speed)` attributes. While the functionality itself works fine now, this PR is not yet complete: the code might be messy in places and, most importantly, the compiletest must be improved with functionality to run tests with custom optimization levels. Otherwise the new attribute cannot be tested properly. Oh, and not all of the RFC is implemented – attribute propagation is not implemented for example. # TODO * [x] Improve compiletest so that tests can be written; * [x] Assign a proper error number (E9999 currently, no idea how to allocate a number properly); * [ ] Perhaps reduce the duplication in LLVM attribute assignment code…
This commit is contained in:
commit
42eb5ff404
32 changed files with 449 additions and 129 deletions
|
|
@ -5,7 +5,7 @@ use std::ffi::CString;
|
|||
use rustc::hir::{CodegenFnAttrFlags, CodegenFnAttrs};
|
||||
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use rustc::session::Session;
|
||||
use rustc::session::config::Sanitizer;
|
||||
use rustc::session::config::{Sanitizer, OptLevel};
|
||||
use rustc::ty::{self, TyCtxt, PolyFnSig};
|
||||
use rustc::ty::layout::HasTyCtxt;
|
||||
use rustc::ty::query::Providers;
|
||||
|
|
@ -20,7 +20,7 @@ use attributes;
|
|||
use llvm::{self, Attribute};
|
||||
use llvm::AttributePlace::Function;
|
||||
use llvm_util;
|
||||
pub use syntax::attr::{self, InlineAttr};
|
||||
pub use syntax::attr::{self, InlineAttr, OptimizeAttr};
|
||||
|
||||
use context::CodegenCx;
|
||||
use value::Value;
|
||||
|
|
@ -57,13 +57,6 @@ fn unwind(val: &'ll Value, can_unwind: bool) {
|
|||
Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind);
|
||||
}
|
||||
|
||||
/// Tell LLVM whether it should optimize function for size.
|
||||
#[inline]
|
||||
#[allow(dead_code)] // possibly useful function
|
||||
pub fn set_optimize_for_size(val: &'ll Value, optimize: bool) {
|
||||
Attribute::OptimizeForSize.toggle_llfn(Function, val, optimize);
|
||||
}
|
||||
|
||||
/// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue.
|
||||
#[inline]
|
||||
pub fn naked(val: &'ll Value, is_naked: bool) {
|
||||
|
|
@ -151,6 +144,28 @@ pub fn non_lazy_bind(sess: &Session, llfn: &'ll Value) {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn default_optimisation_attrs(sess: &Session, llfn: &'ll Value) {
|
||||
match sess.opts.optimize {
|
||||
OptLevel::Size => {
|
||||
llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
|
||||
},
|
||||
OptLevel::SizeMin => {
|
||||
llvm::Attribute::MinSize.apply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
|
||||
}
|
||||
OptLevel::No => {
|
||||
llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
|
||||
/// attributes.
|
||||
pub fn from_fn_attrs(
|
||||
|
|
@ -162,6 +177,22 @@ pub fn from_fn_attrs(
|
|||
let codegen_fn_attrs = id.map(|id| cx.tcx.codegen_fn_attrs(id))
|
||||
.unwrap_or_else(|| CodegenFnAttrs::new());
|
||||
|
||||
match codegen_fn_attrs.optimize {
|
||||
OptimizeAttr::None => {
|
||||
default_optimisation_attrs(cx.tcx.sess, llfn);
|
||||
}
|
||||
OptimizeAttr::Speed => {
|
||||
llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
|
||||
}
|
||||
OptimizeAttr::Size => {
|
||||
llvm::Attribute::MinSize.apply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
|
||||
}
|
||||
}
|
||||
|
||||
inline(cx, llfn, codegen_fn_attrs.inline);
|
||||
|
||||
// The `uwtable` attribute according to LLVM is:
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use rustc_codegen_ssa::back::symbol_export;
|
|||
use rustc_codegen_ssa::back::write::{ModuleConfig, CodegenContext, pre_lto_bitcode_filename};
|
||||
use rustc_codegen_ssa::back::lto::{SerializedModule, LtoModuleCodegen, ThinShared, ThinModule};
|
||||
use rustc_codegen_ssa::traits::*;
|
||||
use back::write::{self, DiagnosticHandlers, with_llvm_pmb, save_temp_bitcode, get_llvm_opt_level};
|
||||
use back::write::{self, DiagnosticHandlers, with_llvm_pmb, save_temp_bitcode, to_llvm_opt_settings};
|
||||
use errors::{FatalError, Handler};
|
||||
use llvm::archive_ro::ArchiveRO;
|
||||
use llvm::{self, True, False};
|
||||
|
|
@ -532,7 +532,7 @@ pub(crate) fn run_pass_manager(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|||
// Note that in general this shouldn't matter too much as you typically
|
||||
// only turn on ThinLTO when you're compiling with optimizations
|
||||
// otherwise.
|
||||
let opt_level = config.opt_level.map(get_llvm_opt_level)
|
||||
let opt_level = config.opt_level.map(|x| to_llvm_opt_settings(x).0)
|
||||
.unwrap_or(llvm::CodeGenOptLevel::None);
|
||||
let opt_level = match opt_level {
|
||||
llvm::CodeGenOptLevel::None => llvm::CodeGenOptLevel::Less,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ use rustc_codegen_ssa::back::write::{CodegenContext, ModuleConfig, run_assembler
|
|||
use rustc_codegen_ssa::traits::*;
|
||||
use base;
|
||||
use consts;
|
||||
use rustc::hir::def_id::LOCAL_CRATE;
|
||||
use rustc::session::config::{self, OutputType, Passes, Lto};
|
||||
use rustc::session::Session;
|
||||
use rustc::ty::TyCtxt;
|
||||
use time_graph::Timeline;
|
||||
use llvm::{self, DiagnosticInfo, PassManager, SMDiagnostic};
|
||||
use llvm_util;
|
||||
|
|
@ -81,42 +83,46 @@ pub fn write_output_file(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
|
||||
match optimize {
|
||||
config::OptLevel::No => llvm::CodeGenOptLevel::None,
|
||||
config::OptLevel::Less => llvm::CodeGenOptLevel::Less,
|
||||
config::OptLevel::Default => llvm::CodeGenOptLevel::Default,
|
||||
config::OptLevel::Aggressive => llvm::CodeGenOptLevel::Aggressive,
|
||||
_ => llvm::CodeGenOptLevel::Default,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize {
|
||||
match optimize {
|
||||
config::OptLevel::Size => llvm::CodeGenOptSizeDefault,
|
||||
config::OptLevel::SizeMin => llvm::CodeGenOptSizeAggressive,
|
||||
_ => llvm::CodeGenOptSizeNone,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_target_machine(
|
||||
tcx: TyCtxt,
|
||||
find_features: bool,
|
||||
) -> &'static mut llvm::TargetMachine {
|
||||
target_machine_factory(tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE), find_features)()
|
||||
.unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise() )
|
||||
}
|
||||
|
||||
pub fn create_informational_target_machine(
|
||||
sess: &Session,
|
||||
find_features: bool,
|
||||
) -> &'static mut llvm::TargetMachine {
|
||||
target_machine_factory(sess, find_features)().unwrap_or_else(|err| {
|
||||
target_machine_factory(sess, config::OptLevel::No, find_features)().unwrap_or_else(|err| {
|
||||
llvm_err(sess.diagnostic(), &err).raise()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
pub fn to_llvm_opt_settings(cfg: config::OptLevel) -> (llvm::CodeGenOptLevel, llvm::CodeGenOptSize)
|
||||
{
|
||||
use self::config::OptLevel::*;
|
||||
match cfg {
|
||||
No => (llvm::CodeGenOptLevel::None, llvm::CodeGenOptSizeNone),
|
||||
Less => (llvm::CodeGenOptLevel::Less, llvm::CodeGenOptSizeNone),
|
||||
Default => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeNone),
|
||||
Aggressive => (llvm::CodeGenOptLevel::Aggressive, llvm::CodeGenOptSizeNone),
|
||||
Size => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeDefault),
|
||||
SizeMin => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeAggressive),
|
||||
}
|
||||
}
|
||||
|
||||
// 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.
|
||||
pub fn target_machine_factory(sess: &Session, find_features: bool)
|
||||
pub fn target_machine_factory(sess: &Session, optlvl: config::OptLevel, find_features: bool)
|
||||
-> Arc<dyn Fn() -> Result<&'static mut llvm::TargetMachine, String> + Send + Sync>
|
||||
{
|
||||
let reloc_model = get_reloc_model(sess);
|
||||
|
||||
let opt_level = get_llvm_opt_level(sess.opts.optimize);
|
||||
let (opt_level, _) = to_llvm_opt_settings(optlvl);
|
||||
let use_softfp = sess.opts.cg.soft_float;
|
||||
|
||||
let ffunction_sections = sess.target.target.options.function_sections;
|
||||
|
|
@ -357,7 +363,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
|
|||
if !config.no_prepopulate_passes {
|
||||
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
|
||||
llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
|
||||
let opt_level = config.opt_level.map(get_llvm_opt_level)
|
||||
let opt_level = config.opt_level.map(|x| to_llvm_opt_settings(x).0)
|
||||
.unwrap_or(llvm::CodeGenOptLevel::None);
|
||||
let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal ||
|
||||
(cgcx.lto != Lto::Fat && cgcx.opts.debugging_opts.cross_lang_lto.enabled());
|
||||
|
|
@ -689,7 +695,8 @@ pub unsafe fn with_llvm_pmb(llmod: &llvm::Module,
|
|||
// reasonable defaults and prepare it to actually populate the pass
|
||||
// manager.
|
||||
let builder = llvm::LLVMPassManagerBuilderCreate();
|
||||
let opt_size = config.opt_size.map(get_llvm_opt_size).unwrap_or(llvm::CodeGenOptSizeNone);
|
||||
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 = config.pgo_gen.as_ref().map(|s| {
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ pub fn iter_globals(llmod: &'ll llvm::Module) -> ValueIter<'ll> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn compile_codegen_unit<'ll, 'tcx>(tcx: TyCtxt<'ll, 'tcx, 'tcx>,
|
||||
pub fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
cgu_name: InternedString)
|
||||
-> Stats {
|
||||
let start_time = Instant::now();
|
||||
|
|
@ -164,7 +164,7 @@ pub fn compile_codegen_unit<'ll, 'tcx>(tcx: TyCtxt<'ll, 'tcx, 'tcx>,
|
|||
let backend = LlvmCodegenBackend(());
|
||||
let cgu = tcx.codegen_unit(cgu_name);
|
||||
// Instantiate monomorphizations without filling out definitions yet...
|
||||
let llvm_module = backend.new_metadata(tcx.sess, &cgu_name.as_str());
|
||||
let llvm_module = backend.new_metadata(tcx, &cgu_name.as_str());
|
||||
let stats = {
|
||||
let cx = CodegenCx::new(tcx, cgu, &llvm_module);
|
||||
let mono_items = cx.codegen_unit
|
||||
|
|
|
|||
|
|
@ -144,16 +144,17 @@ pub fn is_pie_binary(sess: &Session) -> bool {
|
|||
}
|
||||
|
||||
pub unsafe fn create_module(
|
||||
sess: &Session,
|
||||
tcx: TyCtxt,
|
||||
llcx: &'ll llvm::Context,
|
||||
mod_name: &str,
|
||||
) -> &'ll llvm::Module {
|
||||
let sess = tcx.sess;
|
||||
let mod_name = SmallCStr::new(mod_name);
|
||||
let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx);
|
||||
|
||||
// Ensure the data-layout values hardcoded remain the defaults.
|
||||
if sess.target.target.options.is_builtin {
|
||||
let tm = ::back::write::create_target_machine(sess, false);
|
||||
let tm = ::back::write::create_target_machine(tcx, false);
|
||||
llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm);
|
||||
llvm::LLVMRustDisposeTargetMachine(tm);
|
||||
|
||||
|
|
|
|||
|
|
@ -65,19 +65,8 @@ fn declare_raw_fn(
|
|||
}
|
||||
}
|
||||
|
||||
match cx.tcx.sess.opts.cg.opt_level.as_ref().map(String::as_ref) {
|
||||
Some("s") => {
|
||||
llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
|
||||
},
|
||||
Some("z") => {
|
||||
llvm::Attribute::MinSize.apply_llfn(Function, llfn);
|
||||
llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
attributes::default_optimisation_attrs(cx.tcx.sess, llfn);
|
||||
attributes::non_lazy_bind(cx.sess(), llfn);
|
||||
|
||||
llfn
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ use rustc::dep_graph::DepGraph;
|
|||
use rustc::middle::allocator::AllocatorKind;
|
||||
use rustc::middle::cstore::{EncodedMetadata, MetadataLoader};
|
||||
use rustc::session::{Session, CompileIncomplete};
|
||||
use rustc::session::config::{OutputFilenames, OutputType, PrintRequest};
|
||||
use rustc::session::config::{OutputFilenames, OutputType, PrintRequest, OptLevel};
|
||||
use rustc::ty::{self, TyCtxt};
|
||||
use rustc::util::time_graph;
|
||||
use rustc::util::profiling::ProfileCategory;
|
||||
|
|
@ -121,8 +121,8 @@ mod va_arg;
|
|||
pub struct LlvmCodegenBackend(());
|
||||
|
||||
impl ExtraBackendMethods for LlvmCodegenBackend {
|
||||
fn new_metadata(&self, sess: &Session, mod_name: &str) -> ModuleLlvm {
|
||||
ModuleLlvm::new(sess, mod_name)
|
||||
fn new_metadata(&self, tcx: TyCtxt, mod_name: &str) -> ModuleLlvm {
|
||||
ModuleLlvm::new(tcx, mod_name)
|
||||
}
|
||||
fn write_metadata<'b, 'gcx>(
|
||||
&self,
|
||||
|
|
@ -144,10 +144,11 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
|
|||
fn target_machine_factory(
|
||||
&self,
|
||||
sess: &Session,
|
||||
optlvl: OptLevel,
|
||||
find_features: bool
|
||||
) -> Arc<dyn Fn() ->
|
||||
Result<&'static mut llvm::TargetMachine, String> + Send + Sync> {
|
||||
back::write::target_machine_factory(sess, find_features)
|
||||
back::write::target_machine_factory(sess, optlvl, find_features)
|
||||
}
|
||||
fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str {
|
||||
llvm_util::target_cpu(sess)
|
||||
|
|
@ -363,15 +364,15 @@ unsafe impl Send for ModuleLlvm { }
|
|||
unsafe impl Sync for ModuleLlvm { }
|
||||
|
||||
impl ModuleLlvm {
|
||||
fn new(sess: &Session, mod_name: &str) -> Self {
|
||||
fn new(tcx: TyCtxt, mod_name: &str) -> Self {
|
||||
unsafe {
|
||||
let llcx = llvm::LLVMRustContextCreate(sess.fewer_names());
|
||||
let llmod_raw = context::create_module(sess, llcx, mod_name) as *const _;
|
||||
let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names());
|
||||
let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _;
|
||||
|
||||
ModuleLlvm {
|
||||
llmod_raw,
|
||||
llcx,
|
||||
tm: create_target_machine(sess, false),
|
||||
tm: create_target_machine(tcx, false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ pub enum Attribute {
|
|||
SanitizeAddress = 21,
|
||||
SanitizeMemory = 22,
|
||||
NonLazyBind = 23,
|
||||
OptimizeNone = 24,
|
||||
}
|
||||
|
||||
/// LLVMIntPredicate
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use syntax_pos::symbol::Symbol;
|
||||
use back::write::create_target_machine;
|
||||
use back::write::create_informational_target_machine;
|
||||
use llvm;
|
||||
use rustc::session::Session;
|
||||
use rustc::session::config::PrintRequest;
|
||||
|
|
@ -223,7 +223,7 @@ pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
|
|||
}
|
||||
|
||||
pub fn target_features(sess: &Session) -> Vec<Symbol> {
|
||||
let target_machine = create_target_machine(sess, true);
|
||||
let target_machine = create_informational_target_machine(sess, true);
|
||||
target_feature_whitelist(sess)
|
||||
.iter()
|
||||
.filter_map(|&(feature, gate)| {
|
||||
|
|
@ -276,7 +276,7 @@ pub fn print_passes() {
|
|||
|
||||
pub(crate) fn print(req: PrintRequest, sess: &Session) {
|
||||
require_inited();
|
||||
let tm = create_target_machine(sess, true);
|
||||
let tm = create_informational_target_machine(sess, true);
|
||||
unsafe {
|
||||
match req {
|
||||
PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue