diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 9f29f9050e6a6..b8dd750d3f1c2 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -48,7 +48,9 @@ pub enum OptLevel { No, // -O0 Less, // -O1 Default, // -O2 - Aggressive // -O3 + Aggressive, // -O3 + Size, // -Os + SizeMin, // -Oz } #[derive(Clone, Copy, PartialEq)] @@ -567,8 +569,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options, debuginfo: Option = (None, parse_opt_uint, "debug info emission level, 0 = no debug info, 1 = line tables only, \ 2 = full debug info with variable and type information"), - opt_level: Option = (None, parse_opt_uint, - "optimize with possible levels 0-3"), + opt_level: Option = (None, parse_opt_string, + "optimize with possible levels 0-3, s, or z"), debug_assertions: Option = (None, parse_opt_bool, "explicitly enable the cfg(debug_assertions) directive"), inline_threshold: Option = (None, parse_opt_uint, @@ -1125,13 +1127,20 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { } OptLevel::Default } else { - match cg.opt_level { - None => OptLevel::No, - Some(0) => OptLevel::No, - Some(1) => OptLevel::Less, - Some(2) => OptLevel::Default, - Some(3) => OptLevel::Aggressive, - Some(arg) => { + match (cg.opt_level.as_ref().map(String::as_ref), + nightly_options::is_nightly_build()) { + (None, _) => OptLevel::No, + (Some("0"), _) => OptLevel::No, + (Some("1"), _) => OptLevel::Less, + (Some("2"), _) => OptLevel::Default, + (Some("3"), _) => OptLevel::Aggressive, + (Some("s"), true) => OptLevel::Size, + (Some("z"), true) => OptLevel::SizeMin, + (Some("s"), false) | (Some("z"), false) => { + early_error(error_format, &format!("the optimizations s or z are only \ + accepted on the nightly compiler")); + }, + (Some(arg), _) => { early_error(error_format, &format!("optimization level needs to be \ between 0-3 (instead was `{}`)", arg)); @@ -1304,7 +1313,7 @@ pub mod nightly_options { is_nightly_build() && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options") } - fn is_nightly_build() -> bool { + pub fn is_nightly_build() -> bool { match get_unstable_features_setting() { UnstableFeatures::Allow | UnstableFeatures::Cheat => true, _ => false, diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 526b6bf68be24..ea0d8eae75d75 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -44,6 +44,7 @@ pub use self::FileType::*; pub use self::MetadataType::*; pub use self::AsmDialect::*; pub use self::CodeGenOptLevel::*; +pub use self::CodeGenOptSize::*; pub use self::RelocMode::*; pub use self::CodeGenModel::*; pub use self::DiagnosticKind::*; @@ -375,6 +376,14 @@ pub enum CodeGenOptLevel { CodeGenLevelAggressive = 3, } +#[derive(Copy, Clone, PartialEq)] +#[repr(C)] +pub enum CodeGenOptSize { + CodeGenOptSizeNone = 0, + CodeGenOptSizeDefault = 1, + CodeGenOptSizeAggressive = 2, +} + #[derive(Copy, Clone, PartialEq)] #[repr(C)] pub enum RelocMode { diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs index 8a915f044053a..777245c4e0cae 100644 --- a/src/librustc_trans/back/write.rs +++ b/src/librustc_trans/back/write.rs @@ -140,6 +140,15 @@ fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { config::OptLevel::Less => llvm::CodeGenLevelLess, config::OptLevel::Default => llvm::CodeGenLevelDefault, config::OptLevel::Aggressive => llvm::CodeGenLevelAggressive, + _ => llvm::CodeGenLevelDefault, + } +} + +fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize { + match optimize { + config::OptLevel::Size => llvm::CodeGenOptSizeDefault, + config::OptLevel::SizeMin => llvm::CodeGenOptSizeAggressive, + _ => llvm::CodeGenOptSizeNone, } } @@ -237,6 +246,9 @@ pub struct ModuleConfig { /// absolutely no optimizations (used for the metadata module). opt_level: Option, + /// Some(level) to optimize binary size, or None to not affect program size. + opt_size: Option, + // Flags indicating which outputs to produce. emit_no_opt_bc: bool, emit_bc: bool, @@ -268,6 +280,7 @@ impl ModuleConfig { tm: tm, passes: passes, opt_level: None, + opt_size: None, emit_no_opt_bc: false, emit_bc: false, @@ -637,6 +650,7 @@ pub fn run_passes(sess: &Session, let mut metadata_config = ModuleConfig::new(tm, vec!()); modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize)); + modules_config.opt_size = Some(get_llvm_opt_size(sess.opts.optimize)); // Save all versions of the bytecode if we're saving our temporaries. if sess.opts.cg.save_temps { @@ -991,13 +1005,19 @@ pub unsafe fn with_llvm_pmb(llmod: ModuleRef, // reasonable defaults and prepare it to actually populate the pass // manager. let builder = llvm::LLVMPassManagerBuilderCreate(); - let opt = config.opt_level.unwrap_or(llvm::CodeGenLevelNone); + let opt_level = config.opt_level.unwrap_or(llvm::CodeGenLevelNone); + let opt_size = config.opt_size.unwrap_or(llvm::CodeGenOptSizeNone); let inline_threshold = config.inline_threshold; - llvm::LLVMRustConfigurePassManagerBuilder(builder, opt, + llvm::LLVMRustConfigurePassManagerBuilder(builder, opt_level, config.merge_functions, config.vectorize_slp, config.vectorize_loop); + llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32); + + if opt_size != llvm::CodeGenOptSizeNone { + llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(builder, 1); + } llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins); @@ -1005,22 +1025,28 @@ pub unsafe fn with_llvm_pmb(llmod: ModuleRef, // always-inline functions (but don't add lifetime intrinsics), at O1 we // inline with lifetime intrinsics, and O2+ we add an inliner with a // thresholds copied from clang. - match (opt, inline_threshold) { - (_, Some(t)) => { + match (opt_level, opt_size, inline_threshold) { + (_, _, Some(t)) => { llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t as u32); } - (llvm::CodeGenLevelNone, _) => { + (llvm::CodeGenLevelAggressive, _, _) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275); + } + (_, llvm::CodeGenOptSizeDefault, _) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 75); + } + (_, llvm::CodeGenOptSizeAggressive, _) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25); + } + (llvm::CodeGenLevelNone, _, _) => { llvm::LLVMRustAddAlwaysInlinePass(builder, false); } - (llvm::CodeGenLevelLess, _) => { + (llvm::CodeGenLevelLess, _, _) => { llvm::LLVMRustAddAlwaysInlinePass(builder, true); } - (llvm::CodeGenLevelDefault, _) => { + (llvm::CodeGenLevelDefault, _, _) => { llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225); } - (llvm::CodeGenLevelAggressive, _) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275); - } } f(builder); diff --git a/src/librustc_trans/declare.rs b/src/librustc_trans/declare.rs index eb520fe744a3d..9a4d20ca3010c 100644 --- a/src/librustc_trans/declare.rs +++ b/src/librustc_trans/declare.rs @@ -69,6 +69,17 @@ fn declare_raw_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: llvm::SetFunctionAttribute(llfn, llvm::Attribute::NoRedZone) } + match ccx.tcx().sess.opts.cg.opt_level.as_ref().map(String::as_ref) { + Some("s") => { + llvm::SetFunctionAttribute(llfn, llvm::Attribute::OptimizeForSize); + }, + Some("z") => { + llvm::SetFunctionAttribute(llfn, llvm::Attribute::MinSize); + llvm::SetFunctionAttribute(llfn, llvm::Attribute::OptimizeForSize); + }, + _ => {}, + } + llfn } diff --git a/src/test/run-make/debug-assertions/Makefile b/src/test/run-make/debug-assertions/Makefile index 7129756276864..76ada90f1e25a 100644 --- a/src/test/run-make/debug-assertions/Makefile +++ b/src/test/run-make/debug-assertions/Makefile @@ -11,6 +11,10 @@ all: $(call RUN,debug) good $(RUSTC) debug.rs -C opt-level=3 $(call RUN,debug) good + $(RUSTC) debug.rs -C opt-level=s + $(call RUN,debug) good + $(RUSTC) debug.rs -C opt-level=z + $(call RUN,debug) good $(RUSTC) debug.rs -O $(call RUN,debug) good $(RUSTC) debug.rs diff --git a/src/test/run-make/emit/Makefile b/src/test/run-make/emit/Makefile index be34028fe1d01..e0b57107e5b7b 100644 --- a/src/test/run-make/emit/Makefile +++ b/src/test/run-make/emit/Makefile @@ -5,6 +5,8 @@ all: $(RUSTC) -Copt-level=1 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs $(RUSTC) -Copt-level=2 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs $(RUSTC) -Copt-level=3 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs + $(RUSTC) -Copt-level=s --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs + $(RUSTC) -Copt-level=z --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs $(RUSTC) -Copt-level=0 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs $(call RUN,test-26235) || exit 1 $(RUSTC) -Copt-level=1 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs @@ -13,3 +15,7 @@ all: $(call RUN,test-26235) || exit 1 $(RUSTC) -Copt-level=3 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs $(call RUN,test-26235) || exit 1 + $(RUSTC) -Copt-level=s --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs + $(call RUN,test-26235) || exit 1 + $(RUSTC) -Copt-level=z --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs + $(call RUN,test-26235) || exit 1