Skip to content

Commit b1337d3

Browse files
committed
Add opt-level options for optimizing for size and minimum size. This attempts
to mimic the behavior of clang's options Os and Oz.
1 parent 8da2bca commit b1337d3

File tree

4 files changed

+69
-19
lines changed

4 files changed

+69
-19
lines changed

src/librustc/session/config.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ pub enum OptLevel {
4848
No, // -O0
4949
Less, // -O1
5050
Default, // -O2
51-
Aggressive // -O3
51+
Aggressive, // -O3
52+
Size, // -Os
53+
SizeMin, // -Oz
5254
}
5355

5456
#[derive(Clone, Copy, PartialEq)]
@@ -567,8 +569,8 @@ options! {CodegenOptions, CodegenSetter, basic_codegen_options,
567569
debuginfo: Option<usize> = (None, parse_opt_uint,
568570
"debug info emission level, 0 = no debug info, 1 = line tables only, \
569571
2 = full debug info with variable and type information"),
570-
opt_level: Option<usize> = (None, parse_opt_uint,
571-
"optimize with possible levels 0-3"),
572+
opt_level: Option<String> = (None, parse_opt_string,
573+
"optimize with possible levels 0-3, s, or z"),
572574
debug_assertions: Option<bool> = (None, parse_opt_bool,
573575
"explicitly enable the cfg(debug_assertions) directive"),
574576
inline_threshold: Option<usize> = (None, parse_opt_uint,
@@ -1125,15 +1127,17 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
11251127
}
11261128
OptLevel::Default
11271129
} else {
1128-
match cg.opt_level {
1130+
match cg.opt_level.as_ref().map(String::as_ref) {
11291131
None => OptLevel::No,
1130-
Some(0) => OptLevel::No,
1131-
Some(1) => OptLevel::Less,
1132-
Some(2) => OptLevel::Default,
1133-
Some(3) => OptLevel::Aggressive,
1132+
Some("0") => OptLevel::No,
1133+
Some("1") => OptLevel::Less,
1134+
Some("2") => OptLevel::Default,
1135+
Some("3") => OptLevel::Aggressive,
1136+
Some("s") => OptLevel::Size,
1137+
Some("z") => OptLevel::SizeMin,
11341138
Some(arg) => {
11351139
early_error(error_format, &format!("optimization level needs to be \
1136-
between 0-3 (instead was `{}`)",
1140+
between 0-3, s, or z (instead was `{}`)",
11371141
arg));
11381142
}
11391143
}

src/librustc_llvm/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ pub use self::FileType::*;
4444
pub use self::MetadataType::*;
4545
pub use self::AsmDialect::*;
4646
pub use self::CodeGenOptLevel::*;
47+
pub use self::CodeGenOptSize::*;
4748
pub use self::RelocMode::*;
4849
pub use self::CodeGenModel::*;
4950
pub use self::DiagnosticKind::*;
@@ -375,6 +376,14 @@ pub enum CodeGenOptLevel {
375376
CodeGenLevelAggressive = 3,
376377
}
377378

379+
#[derive(Copy, Clone, PartialEq)]
380+
#[repr(C)]
381+
pub enum CodeGenOptSize {
382+
CodeGenOptSizeNone = 0,
383+
CodeGenOptSizeDefault = 1,
384+
CodeGenOptSizeAggressive = 2,
385+
}
386+
378387
#[derive(Copy, Clone, PartialEq)]
379388
#[repr(C)]
380389
pub enum RelocMode {

src/librustc_trans/back/write.rs

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,15 @@ fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
140140
config::OptLevel::Less => llvm::CodeGenLevelLess,
141141
config::OptLevel::Default => llvm::CodeGenLevelDefault,
142142
config::OptLevel::Aggressive => llvm::CodeGenLevelAggressive,
143+
_ => llvm::CodeGenLevelDefault,
144+
}
145+
}
146+
147+
fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize {
148+
match optimize {
149+
config::OptLevel::Size => llvm::CodeGenOptSizeDefault,
150+
config::OptLevel::SizeMin => llvm::CodeGenOptSizeAggressive,
151+
_ => llvm::CodeGenOptSizeNone,
143152
}
144153
}
145154

@@ -237,6 +246,9 @@ pub struct ModuleConfig {
237246
/// absolutely no optimizations (used for the metadata module).
238247
opt_level: Option<llvm::CodeGenOptLevel>,
239248

249+
/// Some(level) to optimize binary size, or None to not affect program size.
250+
opt_size: Option<llvm::CodeGenOptSize>,
251+
240252
// Flags indicating which outputs to produce.
241253
emit_no_opt_bc: bool,
242254
emit_bc: bool,
@@ -268,6 +280,7 @@ impl ModuleConfig {
268280
tm: tm,
269281
passes: passes,
270282
opt_level: None,
283+
opt_size: None,
271284

272285
emit_no_opt_bc: false,
273286
emit_bc: false,
@@ -637,6 +650,7 @@ pub fn run_passes(sess: &Session,
637650
let mut metadata_config = ModuleConfig::new(tm, vec!());
638651

639652
modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize));
653+
modules_config.opt_size = Some(get_llvm_opt_size(sess.opts.optimize));
640654

641655
// Save all versions of the bytecode if we're saving our temporaries.
642656
if sess.opts.cg.save_temps {
@@ -991,36 +1005,48 @@ pub unsafe fn with_llvm_pmb(llmod: ModuleRef,
9911005
// reasonable defaults and prepare it to actually populate the pass
9921006
// manager.
9931007
let builder = llvm::LLVMPassManagerBuilderCreate();
994-
let opt = config.opt_level.unwrap_or(llvm::CodeGenLevelNone);
1008+
let opt_level = config.opt_level.unwrap_or(llvm::CodeGenLevelNone);
1009+
let opt_size = config.opt_size.unwrap_or(llvm::CodeGenOptSizeNone);
9951010
let inline_threshold = config.inline_threshold;
9961011

997-
llvm::LLVMRustConfigurePassManagerBuilder(builder, opt,
1012+
llvm::LLVMRustConfigurePassManagerBuilder(builder, opt_level,
9981013
config.merge_functions,
9991014
config.vectorize_slp,
10001015
config.vectorize_loop);
1016+
llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32);
1017+
1018+
if opt_size != llvm::CodeGenOptSizeNone {
1019+
llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(builder, 1);
1020+
}
10011021

10021022
llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins);
10031023

10041024
// Here we match what clang does (kinda). For O0 we only inline
10051025
// always-inline functions (but don't add lifetime intrinsics), at O1 we
10061026
// inline with lifetime intrinsics, and O2+ we add an inliner with a
10071027
// thresholds copied from clang.
1008-
match (opt, inline_threshold) {
1009-
(_, Some(t)) => {
1028+
match (opt_level, opt_size, inline_threshold) {
1029+
(_, _, Some(t)) => {
10101030
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t as u32);
10111031
}
1012-
(llvm::CodeGenLevelNone, _) => {
1032+
(llvm::CodeGenLevelAggressive, _, _) => {
1033+
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275);
1034+
}
1035+
(_, llvm::CodeGenOptSizeDefault, _) => {
1036+
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 75);
1037+
}
1038+
(_, llvm::CodeGenOptSizeAggressive, _) => {
1039+
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25);
1040+
}
1041+
(llvm::CodeGenLevelNone, _, _) => {
10131042
llvm::LLVMRustAddAlwaysInlinePass(builder, false);
10141043
}
1015-
(llvm::CodeGenLevelLess, _) => {
1044+
(llvm::CodeGenLevelLess, _, _) => {
10161045
llvm::LLVMRustAddAlwaysInlinePass(builder, true);
10171046
}
1018-
(llvm::CodeGenLevelDefault, _) => {
1047+
(llvm::CodeGenLevelDefault, _, _) => {
10191048
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225);
10201049
}
1021-
(llvm::CodeGenLevelAggressive, _) => {
1022-
llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275);
1023-
}
10241050
}
10251051

10261052
f(builder);

src/librustc_trans/declare.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ fn declare_raw_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty:
6969
llvm::SetFunctionAttribute(llfn, llvm::Attribute::NoRedZone)
7070
}
7171

72+
match ccx.tcx().sess.opts.cg.opt_level.as_ref().map(String::as_ref) {
73+
Some("s") => {
74+
llvm::SetFunctionAttribute(llfn, llvm::Attribute::OptimizeForSize);
75+
},
76+
Some("z") => {
77+
llvm::SetFunctionAttribute(llfn, llvm::Attribute::MinSize);
78+
llvm::SetFunctionAttribute(llfn, llvm::Attribute::OptimizeForSize);
79+
},
80+
_ => {},
81+
}
82+
7283
llfn
7384
}
7485

0 commit comments

Comments
 (0)