Skip to content

Commit 9a003b0

Browse files
committed
Auto merge of #32386 - brandonedens:llvm_min_size, r=alexcrichton
Add CodeGen options to optimize for size. Add CodeGen options to annotate functions with the attributes OptimizeSize and/or MinSize used by LLVM to reduce .text size. Closes #32296
2 parents d80497e + 16eaecb commit 9a003b0

File tree

6 files changed

+86
-21
lines changed

6 files changed

+86
-21
lines changed

src/librustc/session/config.rs

+20-11
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,13 +1127,20 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
11251127
}
11261128
OptLevel::Default
11271129
} else {
1128-
match cg.opt_level {
1129-
None => OptLevel::No,
1130-
Some(0) => OptLevel::No,
1131-
Some(1) => OptLevel::Less,
1132-
Some(2) => OptLevel::Default,
1133-
Some(3) => OptLevel::Aggressive,
1134-
Some(arg) => {
1130+
match (cg.opt_level.as_ref().map(String::as_ref),
1131+
nightly_options::is_nightly_build()) {
1132+
(None, _) => OptLevel::No,
1133+
(Some("0"), _) => OptLevel::No,
1134+
(Some("1"), _) => OptLevel::Less,
1135+
(Some("2"), _) => OptLevel::Default,
1136+
(Some("3"), _) => OptLevel::Aggressive,
1137+
(Some("s"), true) => OptLevel::Size,
1138+
(Some("z"), true) => OptLevel::SizeMin,
1139+
(Some("s"), false) | (Some("z"), false) => {
1140+
early_error(error_format, &format!("the optimizations s or z are only \
1141+
accepted on the nightly compiler"));
1142+
},
1143+
(Some(arg), _) => {
11351144
early_error(error_format, &format!("optimization level needs to be \
11361145
between 0-3 (instead was `{}`)",
11371146
arg));
@@ -1304,7 +1313,7 @@ pub mod nightly_options {
13041313
is_nightly_build() && matches.opt_strs("Z").iter().any(|x| *x == "unstable-options")
13051314
}
13061315

1307-
fn is_nightly_build() -> bool {
1316+
pub fn is_nightly_build() -> bool {
13081317
match get_unstable_features_setting() {
13091318
UnstableFeatures::Allow | UnstableFeatures::Cheat => true,
13101319
_ => false,

src/librustc_llvm/lib.rs

+9
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

+36-10
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

+11
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

src/test/run-make/debug-assertions/Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ all:
1111
$(call RUN,debug) good
1212
$(RUSTC) debug.rs -C opt-level=3
1313
$(call RUN,debug) good
14+
$(RUSTC) debug.rs -C opt-level=s
15+
$(call RUN,debug) good
16+
$(RUSTC) debug.rs -C opt-level=z
17+
$(call RUN,debug) good
1418
$(RUSTC) debug.rs -O
1519
$(call RUN,debug) good
1620
$(RUSTC) debug.rs

src/test/run-make/emit/Makefile

+6
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ all:
55
$(RUSTC) -Copt-level=1 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs
66
$(RUSTC) -Copt-level=2 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs
77
$(RUSTC) -Copt-level=3 --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs
8+
$(RUSTC) -Copt-level=s --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs
9+
$(RUSTC) -Copt-level=z --emit=llvm-bc,llvm-ir,asm,obj,link test-24876.rs
810
$(RUSTC) -Copt-level=0 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs
911
$(call RUN,test-26235) || exit 1
1012
$(RUSTC) -Copt-level=1 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs
@@ -13,3 +15,7 @@ all:
1315
$(call RUN,test-26235) || exit 1
1416
$(RUSTC) -Copt-level=3 --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs
1517
$(call RUN,test-26235) || exit 1
18+
$(RUSTC) -Copt-level=s --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs
19+
$(call RUN,test-26235) || exit 1
20+
$(RUSTC) -Copt-level=z --emit=llvm-bc,llvm-ir,asm,obj,link test-26235.rs
21+
$(call RUN,test-26235) || exit 1

0 commit comments

Comments
 (0)