Skip to content

Commit 42eb5ff

Browse files
committed
Auto merge of #55641 - nagisa:optimize-attr, r=pnkfelix
Implement optimize(size) and optimize(speed) attributes This PR implements both `optimize(size)` and `optimize(speed)` attributes. While the functionality itself works fine now, this PR is not yet complete: the code might be messy in places and, most importantly, the compiletest must be improved with functionality to run tests with custom optimization levels. Otherwise the new attribute cannot be tested properly. Oh, and not all of the RFC is implemented – attribute propagation is not implemented for example. # TODO * [x] Improve compiletest so that tests can be written; * [x] Assign a proper error number (E9999 currently, no idea how to allocate a number properly); * [ ] Perhaps reduce the duplication in LLVM attribute assignment code…
2 parents 9df043b + ce289c6 commit 42eb5ff

32 files changed

+448
-128
lines changed

src/librustc/dep_graph/dep_node.rs

+1
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,7 @@ define_dep_nodes!( <'tcx>
642642
[eval_always] CollectAndPartitionMonoItems,
643643
[] IsCodegenedItem(DefId),
644644
[] CodegenUnit(InternedString),
645+
[] BackendOptimizationLevel(CrateNum),
645646
[] CompileCodegenUnit(InternedString),
646647
[input] OutputFilenames,
647648
[] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>),

src/librustc/hir/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use syntax::source_map::Spanned;
2121
use rustc_target::spec::abi::Abi;
2222
use syntax::ast::{self, CrateSugar, Ident, Name, NodeId, DUMMY_NODE_ID, AsmDialect};
2323
use syntax::ast::{Attribute, Label, Lit, StrStyle, FloatTy, IntTy, UintTy};
24-
use syntax::attr::InlineAttr;
24+
use syntax::attr::{InlineAttr, OptimizeAttr};
2525
use syntax::ext::hygiene::SyntaxContext;
2626
use syntax::ptr::P;
2727
use syntax::symbol::{Symbol, keywords};
@@ -2416,6 +2416,8 @@ pub struct CodegenFnAttrs {
24162416
pub flags: CodegenFnAttrFlags,
24172417
/// Parsed representation of the `#[inline]` attribute
24182418
pub inline: InlineAttr,
2419+
/// Parsed representation of the `#[optimize]` attribute
2420+
pub optimize: OptimizeAttr,
24192421
/// The `#[export_name = "..."]` attribute, indicating a custom symbol a
24202422
/// function should be exported under
24212423
pub export_name: Option<Symbol>,
@@ -2476,6 +2478,7 @@ impl CodegenFnAttrs {
24762478
CodegenFnAttrs {
24772479
flags: CodegenFnAttrFlags::empty(),
24782480
inline: InlineAttr::None,
2481+
optimize: OptimizeAttr::None,
24792482
export_name: None,
24802483
link_name: None,
24812484
target_features: vec![],

src/librustc/ich/impls_hir.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1159,6 +1159,7 @@ impl<'a> ToStableHashKey<StableHashingContext<'a>> for hir::TraitCandidate {
11591159
impl_stable_hash_for!(struct hir::CodegenFnAttrs {
11601160
flags,
11611161
inline,
1162+
optimize,
11621163
export_name,
11631164
link_name,
11641165
target_features,
@@ -1183,6 +1184,14 @@ impl<'hir> HashStable<StableHashingContext<'hir>> for attr::InlineAttr {
11831184
}
11841185
}
11851186

1187+
impl<'hir> HashStable<StableHashingContext<'hir>> for attr::OptimizeAttr {
1188+
fn hash_stable<W: StableHasherResult>(&self,
1189+
hcx: &mut StableHashingContext<'hir>,
1190+
hasher: &mut StableHasher<W>) {
1191+
mem::discriminant(self).hash_stable(hcx, hasher);
1192+
}
1193+
}
1194+
11861195
impl_stable_hash_for!(struct hir::Freevar {
11871196
def,
11881197
span

src/librustc/session/config.rs

+2
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ pub enum OptLevel {
5858
SizeMin, // -Oz
5959
}
6060

61+
impl_stable_hash_via_hash!(OptLevel);
62+
6163
/// This is what the `LtoCli` values get mapped to after resolving defaults and
6264
/// and taking other command line options into account.
6365
#[derive(Clone, Copy, PartialEq, Hash, Debug)]

src/librustc/ty/query/config.rs

+6
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::dllimport_foreign_items<'tcx> {
967967
}
968968
}
969969

970+
impl<'tcx> QueryDescription<'tcx> for queries::backend_optimization_level<'tcx> {
971+
fn describe(_tcx: TyCtxt<'_, '_, '_>, _: CrateNum) -> Cow<'static, str> {
972+
"optimization level used by backend".into()
973+
}
974+
}
975+
970976
macro_rules! impl_disk_cacheable_query(
971977
($query_name:ident, |$key:tt| $cond:expr) => {
972978
impl<'tcx> QueryDescription<'tcx> for queries::$query_name<'tcx> {

src/librustc/ty/query/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use mir::mono::CodegenUnit;
2222
use mir;
2323
use mir::interpret::GlobalId;
2424
use session::{CompileResult, CrateDisambiguator};
25-
use session::config::{EntryFnType, OutputFilenames};
25+
use session::config::{EntryFnType, OutputFilenames, OptLevel};
2626
use traits::{self, Vtable};
2727
use traits::query::{
2828
CanonicalPredicateGoal, CanonicalProjectionGoal,
@@ -573,6 +573,7 @@ define_queries! { <'tcx>
573573
-> (Arc<DefIdSet>, Arc<Vec<Arc<CodegenUnit<'tcx>>>>),
574574
[] fn is_codegened_item: IsCodegenedItem(DefId) -> bool,
575575
[] fn codegen_unit: CodegenUnit(InternedString) -> Arc<CodegenUnit<'tcx>>,
576+
[] fn backend_optimization_level: BackendOptimizationLevel(CrateNum) -> OptLevel,
576577
},
577578

578579
Other {

src/librustc/ty/query/plumbing.rs

+3
Original file line numberDiff line numberDiff line change
@@ -1410,6 +1410,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
14101410
DepKind::UpstreamMonomorphizationsFor => {
14111411
force!(upstream_monomorphizations_for, def_id!());
14121412
}
1413+
DepKind::BackendOptimizationLevel => {
1414+
force!(backend_optimization_level, krate!());
1415+
}
14131416
}
14141417

14151418
true

src/librustc_codegen_llvm/attributes.rs

+40-9
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::ffi::CString;
55
use rustc::hir::{CodegenFnAttrFlags, CodegenFnAttrs};
66
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
77
use rustc::session::Session;
8-
use rustc::session::config::Sanitizer;
8+
use rustc::session::config::{Sanitizer, OptLevel};
99
use rustc::ty::{self, TyCtxt, PolyFnSig};
1010
use rustc::ty::layout::HasTyCtxt;
1111
use rustc::ty::query::Providers;
@@ -20,7 +20,7 @@ use attributes;
2020
use llvm::{self, Attribute};
2121
use llvm::AttributePlace::Function;
2222
use llvm_util;
23-
pub use syntax::attr::{self, InlineAttr};
23+
pub use syntax::attr::{self, InlineAttr, OptimizeAttr};
2424

2525
use context::CodegenCx;
2626
use value::Value;
@@ -57,13 +57,6 @@ fn unwind(val: &'ll Value, can_unwind: bool) {
5757
Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind);
5858
}
5959

60-
/// Tell LLVM whether it should optimize function for size.
61-
#[inline]
62-
#[allow(dead_code)] // possibly useful function
63-
pub fn set_optimize_for_size(val: &'ll Value, optimize: bool) {
64-
Attribute::OptimizeForSize.toggle_llfn(Function, val, optimize);
65-
}
66-
6760
/// Tell LLVM if this function should be 'naked', i.e., skip the epilogue and prologue.
6861
#[inline]
6962
pub fn naked(val: &'ll Value, is_naked: bool) {
@@ -151,6 +144,28 @@ pub fn non_lazy_bind(sess: &Session, llfn: &'ll Value) {
151144
}
152145
}
153146

147+
pub(crate) fn default_optimisation_attrs(sess: &Session, llfn: &'ll Value) {
148+
match sess.opts.optimize {
149+
OptLevel::Size => {
150+
llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
151+
llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
152+
llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
153+
},
154+
OptLevel::SizeMin => {
155+
llvm::Attribute::MinSize.apply_llfn(Function, llfn);
156+
llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
157+
llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
158+
}
159+
OptLevel::No => {
160+
llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
161+
llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn);
162+
llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
163+
}
164+
_ => {}
165+
}
166+
}
167+
168+
154169
/// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`)
155170
/// attributes.
156171
pub fn from_fn_attrs(
@@ -162,6 +177,22 @@ pub fn from_fn_attrs(
162177
let codegen_fn_attrs = id.map(|id| cx.tcx.codegen_fn_attrs(id))
163178
.unwrap_or_else(|| CodegenFnAttrs::new());
164179

180+
match codegen_fn_attrs.optimize {
181+
OptimizeAttr::None => {
182+
default_optimisation_attrs(cx.tcx.sess, llfn);
183+
}
184+
OptimizeAttr::Speed => {
185+
llvm::Attribute::MinSize.unapply_llfn(Function, llfn);
186+
llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn);
187+
llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
188+
}
189+
OptimizeAttr::Size => {
190+
llvm::Attribute::MinSize.apply_llfn(Function, llfn);
191+
llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
192+
llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn);
193+
}
194+
}
195+
165196
inline(cx, llfn, codegen_fn_attrs.inline);
166197

167198
// The `uwtable` attribute according to LLVM is:

src/librustc_codegen_llvm/back/lto.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use rustc_codegen_ssa::back::symbol_export;
33
use rustc_codegen_ssa::back::write::{ModuleConfig, CodegenContext, pre_lto_bitcode_filename};
44
use rustc_codegen_ssa::back::lto::{SerializedModule, LtoModuleCodegen, ThinShared, ThinModule};
55
use rustc_codegen_ssa::traits::*;
6-
use back::write::{self, DiagnosticHandlers, with_llvm_pmb, save_temp_bitcode, get_llvm_opt_level};
6+
use back::write::{self, DiagnosticHandlers, with_llvm_pmb, save_temp_bitcode, to_llvm_opt_settings};
77
use errors::{FatalError, Handler};
88
use llvm::archive_ro::ArchiveRO;
99
use llvm::{self, True, False};
@@ -532,7 +532,7 @@ pub(crate) fn run_pass_manager(cgcx: &CodegenContext<LlvmCodegenBackend>,
532532
// Note that in general this shouldn't matter too much as you typically
533533
// only turn on ThinLTO when you're compiling with optimizations
534534
// otherwise.
535-
let opt_level = config.opt_level.map(get_llvm_opt_level)
535+
let opt_level = config.opt_level.map(|x| to_llvm_opt_settings(x).0)
536536
.unwrap_or(llvm::CodeGenOptLevel::None);
537537
let opt_level = match opt_level {
538538
llvm::CodeGenOptLevel::None => llvm::CodeGenOptLevel::Less,

src/librustc_codegen_llvm/back/write.rs

+29-22
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ use rustc_codegen_ssa::back::write::{CodegenContext, ModuleConfig, run_assembler
55
use rustc_codegen_ssa::traits::*;
66
use base;
77
use consts;
8+
use rustc::hir::def_id::LOCAL_CRATE;
89
use rustc::session::config::{self, OutputType, Passes, Lto};
910
use rustc::session::Session;
11+
use rustc::ty::TyCtxt;
1012
use time_graph::Timeline;
1113
use llvm::{self, DiagnosticInfo, PassManager, SMDiagnostic};
1214
use llvm_util;
@@ -81,42 +83,46 @@ pub fn write_output_file(
8183
}
8284
}
8385

84-
pub(crate) fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel {
85-
match optimize {
86-
config::OptLevel::No => llvm::CodeGenOptLevel::None,
87-
config::OptLevel::Less => llvm::CodeGenOptLevel::Less,
88-
config::OptLevel::Default => llvm::CodeGenOptLevel::Default,
89-
config::OptLevel::Aggressive => llvm::CodeGenOptLevel::Aggressive,
90-
_ => llvm::CodeGenOptLevel::Default,
91-
}
92-
}
93-
94-
pub(crate) fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize {
95-
match optimize {
96-
config::OptLevel::Size => llvm::CodeGenOptSizeDefault,
97-
config::OptLevel::SizeMin => llvm::CodeGenOptSizeAggressive,
98-
_ => llvm::CodeGenOptSizeNone,
99-
}
86+
pub fn create_target_machine(
87+
tcx: TyCtxt,
88+
find_features: bool,
89+
) -> &'static mut llvm::TargetMachine {
90+
target_machine_factory(tcx.sess, tcx.backend_optimization_level(LOCAL_CRATE), find_features)()
91+
.unwrap_or_else(|err| llvm_err(tcx.sess.diagnostic(), &err).raise() )
10092
}
10193

102-
pub fn create_target_machine(
94+
pub fn create_informational_target_machine(
10395
sess: &Session,
10496
find_features: bool,
10597
) -> &'static mut llvm::TargetMachine {
106-
target_machine_factory(sess, find_features)().unwrap_or_else(|err| {
98+
target_machine_factory(sess, config::OptLevel::No, find_features)().unwrap_or_else(|err| {
10799
llvm_err(sess.diagnostic(), &err).raise()
108100
})
109101
}
110102

103+
104+
pub fn to_llvm_opt_settings(cfg: config::OptLevel) -> (llvm::CodeGenOptLevel, llvm::CodeGenOptSize)
105+
{
106+
use self::config::OptLevel::*;
107+
match cfg {
108+
No => (llvm::CodeGenOptLevel::None, llvm::CodeGenOptSizeNone),
109+
Less => (llvm::CodeGenOptLevel::Less, llvm::CodeGenOptSizeNone),
110+
Default => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeNone),
111+
Aggressive => (llvm::CodeGenOptLevel::Aggressive, llvm::CodeGenOptSizeNone),
112+
Size => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeDefault),
113+
SizeMin => (llvm::CodeGenOptLevel::Default, llvm::CodeGenOptSizeAggressive),
114+
}
115+
}
116+
111117
// If find_features is true this won't access `sess.crate_types` by assuming
112118
// that `is_pie_binary` is false. When we discover LLVM target features
113119
// `sess.crate_types` is uninitialized so we cannot access it.
114-
pub fn target_machine_factory(sess: &Session, find_features: bool)
120+
pub fn target_machine_factory(sess: &Session, optlvl: config::OptLevel, find_features: bool)
115121
-> Arc<dyn Fn() -> Result<&'static mut llvm::TargetMachine, String> + Send + Sync>
116122
{
117123
let reloc_model = get_reloc_model(sess);
118124

119-
let opt_level = get_llvm_opt_level(sess.opts.optimize);
125+
let (opt_level, _) = to_llvm_opt_settings(optlvl);
120126
let use_softfp = sess.opts.cg.soft_float;
121127

122128
let ffunction_sections = sess.target.target.options.function_sections;
@@ -357,7 +363,7 @@ pub(crate) unsafe fn optimize(cgcx: &CodegenContext<LlvmCodegenBackend>,
357363
if !config.no_prepopulate_passes {
358364
llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod);
359365
llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod);
360-
let opt_level = config.opt_level.map(get_llvm_opt_level)
366+
let opt_level = config.opt_level.map(|x| to_llvm_opt_settings(x).0)
361367
.unwrap_or(llvm::CodeGenOptLevel::None);
362368
let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal ||
363369
(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,
689695
// reasonable defaults and prepare it to actually populate the pass
690696
// manager.
691697
let builder = llvm::LLVMPassManagerBuilderCreate();
692-
let opt_size = config.opt_size.map(get_llvm_opt_size).unwrap_or(llvm::CodeGenOptSizeNone);
698+
let opt_size = config.opt_size.map(|x| to_llvm_opt_settings(x).1)
699+
.unwrap_or(llvm::CodeGenOptSizeNone);
693700
let inline_threshold = config.inline_threshold;
694701

695702
let pgo_gen_path = config.pgo_gen.as_ref().map(|s| {

src/librustc_codegen_llvm/base.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ pub fn iter_globals(llmod: &'ll llvm::Module) -> ValueIter<'ll> {
136136
}
137137
}
138138

139-
pub fn compile_codegen_unit<'ll, 'tcx>(tcx: TyCtxt<'ll, 'tcx, 'tcx>,
139+
pub fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
140140
cgu_name: InternedString)
141141
-> Stats {
142142
let start_time = Instant::now();
@@ -164,7 +164,7 @@ pub fn compile_codegen_unit<'ll, 'tcx>(tcx: TyCtxt<'ll, 'tcx, 'tcx>,
164164
let backend = LlvmCodegenBackend(());
165165
let cgu = tcx.codegen_unit(cgu_name);
166166
// Instantiate monomorphizations without filling out definitions yet...
167-
let llvm_module = backend.new_metadata(tcx.sess, &cgu_name.as_str());
167+
let llvm_module = backend.new_metadata(tcx, &cgu_name.as_str());
168168
let stats = {
169169
let cx = CodegenCx::new(tcx, cgu, &llvm_module);
170170
let mono_items = cx.codegen_unit

src/librustc_codegen_llvm/context.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,17 @@ pub fn is_pie_binary(sess: &Session) -> bool {
144144
}
145145

146146
pub unsafe fn create_module(
147-
sess: &Session,
147+
tcx: TyCtxt,
148148
llcx: &'ll llvm::Context,
149149
mod_name: &str,
150150
) -> &'ll llvm::Module {
151+
let sess = tcx.sess;
151152
let mod_name = SmallCStr::new(mod_name);
152153
let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx);
153154

154155
// Ensure the data-layout values hardcoded remain the defaults.
155156
if sess.target.target.options.is_builtin {
156-
let tm = ::back::write::create_target_machine(sess, false);
157+
let tm = ::back::write::create_target_machine(tcx, false);
157158
llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm);
158159
llvm::LLVMRustDisposeTargetMachine(tm);
159160

src/librustc_codegen_llvm/declare.rs

+1-12
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,8 @@ fn declare_raw_fn(
6565
}
6666
}
6767

68-
match cx.tcx.sess.opts.cg.opt_level.as_ref().map(String::as_ref) {
69-
Some("s") => {
70-
llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
71-
},
72-
Some("z") => {
73-
llvm::Attribute::MinSize.apply_llfn(Function, llfn);
74-
llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn);
75-
},
76-
_ => {},
77-
}
78-
68+
attributes::default_optimisation_attrs(cx.tcx.sess, llfn);
7969
attributes::non_lazy_bind(cx.sess(), llfn);
80-
8170
llfn
8271
}
8372

0 commit comments

Comments
 (0)