Implement optimize(size) and optimize(speed)

This commit is contained in:
Simonas Kazlauskas 2018-10-27 15:29:06 +03:00
parent 095b44c83b
commit f38d0da893
26 changed files with 260 additions and 108 deletions

View file

@ -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) {
@ -164,6 +157,39 @@ pub fn from_fn_attrs(
inline(cx, llfn, codegen_fn_attrs.inline);
match codegen_fn_attrs.optimize {
OptimizeAttr::None => {
match cx.tcx.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.apply_llfn(Function, 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);
}
}
// The `uwtable` attribute according to LLVM is:
//
// This attribute indicates that the ABI being targeted requires that an

View file

@ -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,

View file

@ -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| {

View file

@ -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

View file

@ -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);

View file

@ -15,7 +15,7 @@ use llvm;
use llvm::AttributePlace::Function;
use rustc::ty::{self, PolyFnSig};
use rustc::ty::layout::LayoutOf;
use rustc::session::config::Sanitizer;
use rustc::session::config::{Sanitizer, OptLevel};
use rustc_data_structures::small_c_str::SmallCStr;
use abi::{FnType, FnTypeExt};
use attributes;
@ -65,15 +65,24 @@ fn declare_raw_fn(
}
}
match cx.tcx.sess.opts.cg.opt_level.as_ref().map(String::as_ref) {
Some("s") => {
// FIXME(opt): this is kinda duplicated with similar code in attributes::from_fm_attrs…
match cx.tcx.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);
},
Some("z") => {
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.apply_llfn(Function, llfn);
}
_ => {}
}
attributes::non_lazy_bind(cx.sess(), llfn);

View file

@ -73,7 +73,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;
@ -122,8 +122,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,
@ -145,10 +145,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)
@ -364,15 +365,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),
}
}
}

View file

@ -115,6 +115,7 @@ pub enum Attribute {
SanitizeAddress = 21,
SanitizeMemory = 22,
NonLazyBind = 23,
OptimizeNone = 24,
}
/// LLVMIntPredicate

View file

@ -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),