Skip to content

Commit

Permalink
Implement optimize(size) and optimize(speed)
Browse files Browse the repository at this point in the history
  • Loading branch information
nagisa committed Jan 24, 2019
1 parent 095b44c commit f38d0da
Show file tree
Hide file tree
Showing 26 changed files with 259 additions and 107 deletions.
1 change: 1 addition & 0 deletions src/librustc/dep_graph/dep_node.rs
Expand Up @@ -642,6 +642,7 @@ define_dep_nodes!( <'tcx>
[eval_always] CollectAndPartitionMonoItems,
[] IsCodegenedItem(DefId),
[] CodegenUnit(InternedString),
[] BackendOptimizationLevel(CrateNum),
[] CompileCodegenUnit(InternedString),
[input] OutputFilenames,
[] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>),
Expand Down
5 changes: 4 additions & 1 deletion src/librustc/hir/mod.rs
Expand Up @@ -21,7 +21,7 @@ use syntax::source_map::Spanned;
use rustc_target::spec::abi::Abi;
use syntax::ast::{self, CrateSugar, Ident, Name, NodeId, DUMMY_NODE_ID, AsmDialect};
use syntax::ast::{Attribute, Label, Lit, StrStyle, FloatTy, IntTy, UintTy};
use syntax::attr::InlineAttr;
use syntax::attr::{InlineAttr, OptimizeAttr};
use syntax::ext::hygiene::SyntaxContext;
use syntax::ptr::P;
use syntax::symbol::{Symbol, keywords};
Expand Down Expand Up @@ -2416,6 +2416,8 @@ pub struct CodegenFnAttrs {
pub flags: CodegenFnAttrFlags,
/// Parsed representation of the `#[inline]` attribute
pub inline: InlineAttr,
/// Parsed representation of the `#[optimize]` attribute
pub optimize: OptimizeAttr,
/// The `#[export_name = "..."]` attribute, indicating a custom symbol a
/// function should be exported under
pub export_name: Option<Symbol>,
Expand Down Expand Up @@ -2476,6 +2478,7 @@ impl CodegenFnAttrs {
CodegenFnAttrs {
flags: CodegenFnAttrFlags::empty(),
inline: InlineAttr::None,
optimize: OptimizeAttr::None,
export_name: None,
link_name: None,
target_features: vec![],
Expand Down
9 changes: 9 additions & 0 deletions src/librustc/ich/impls_hir.rs
Expand Up @@ -1159,6 +1159,7 @@ impl<'a> ToStableHashKey<StableHashingContext<'a>> for hir::TraitCandidate {
impl_stable_hash_for!(struct hir::CodegenFnAttrs {
flags,
inline,
optimize,
export_name,
link_name,
target_features,
Expand All @@ -1183,6 +1184,14 @@ impl<'hir> HashStable<StableHashingContext<'hir>> for attr::InlineAttr {
}
}

impl<'hir> HashStable<StableHashingContext<'hir>> for attr::OptimizeAttr {
fn hash_stable<W: StableHasherResult>(&self,
hcx: &mut StableHashingContext<'hir>,
hasher: &mut StableHasher<W>) {
mem::discriminant(self).hash_stable(hcx, hasher);
}
}

impl_stable_hash_for!(struct hir::Freevar {
def,
span
Expand Down
2 changes: 2 additions & 0 deletions src/librustc/session/config.rs
Expand Up @@ -58,6 +58,8 @@ pub enum OptLevel {
SizeMin, // -Oz
}

impl_stable_hash_via_hash!(OptLevel);

/// This is what the `LtoCli` values get mapped to after resolving defaults and
/// and taking other command line options into account.
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
Expand Down
6 changes: 6 additions & 0 deletions src/librustc/ty/query/config.rs
Expand Up @@ -967,6 +967,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::dllimport_foreign_items<'tcx> {
}
}

impl<'tcx> QueryDescription<'tcx> for queries::backend_optimization_level<'tcx> {
fn describe(_tcx: TyCtxt<'_, '_, '_>, _: CrateNum) -> Cow<'static, str> {
"optimization level used by backend".into()
}
}

macro_rules! impl_disk_cacheable_query(
($query_name:ident, |$key:tt| $cond:expr) => {
impl<'tcx> QueryDescription<'tcx> for queries::$query_name<'tcx> {
Expand Down
3 changes: 2 additions & 1 deletion src/librustc/ty/query/mod.rs
Expand Up @@ -22,7 +22,7 @@ use mir::mono::CodegenUnit;
use mir;
use mir::interpret::GlobalId;
use session::{CompileResult, CrateDisambiguator};
use session::config::{EntryFnType, OutputFilenames};
use session::config::{EntryFnType, OutputFilenames, OptLevel};
use traits::{self, Vtable};
use traits::query::{
CanonicalPredicateGoal, CanonicalProjectionGoal,
Expand Down Expand Up @@ -573,6 +573,7 @@ define_queries! { <'tcx>
-> (Arc<DefIdSet>, Arc<Vec<Arc<CodegenUnit<'tcx>>>>),
[] fn is_codegened_item: IsCodegenedItem(DefId) -> bool,
[] fn codegen_unit: CodegenUnit(InternedString) -> Arc<CodegenUnit<'tcx>>,
[] fn backend_optimization_level: BackendOptimizationLevel(CrateNum) -> OptLevel,
},

Other {
Expand Down
3 changes: 3 additions & 0 deletions src/librustc/ty/query/plumbing.rs
Expand Up @@ -1410,6 +1410,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
DepKind::UpstreamMonomorphizationsFor => {
force!(upstream_monomorphizations_for, def_id!());
}
DepKind::BackendOptimizationLevel => {
force!(backend_optimization_level, krate!());
}
}

true
Expand Down
44 changes: 35 additions & 9 deletions src/librustc_codegen_llvm/attributes.rs
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_codegen_llvm/back/lto.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
Expand Down
51 changes: 29 additions & 22 deletions src/librustc_codegen_llvm/back/write.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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_target_machine(
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;
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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| {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_codegen_llvm/base.rs
Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/librustc_codegen_llvm/context.rs
Expand Up @@ -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);

Expand Down
21 changes: 15 additions & 6 deletions src/librustc_codegen_llvm/declare.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit f38d0da

Please sign in to comment.