about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorSimonas Kazlauskas <git@kazlauskas.me>2018-10-27 15:29:06 +0300
committerSimonas Kazlauskas <git@kazlauskas.me>2019-01-24 20:13:50 +0200
commitf38d0da89389c45067d37ba15e783c024088a09a (patch)
tree75260423c9caa95661d32a8bbdef5888f8591fa4 /src
parent095b44c83b540bb4dbf74be1cae604f4bae87989 (diff)
downloadrust-f38d0da89389c45067d37ba15e783c024088a09a.tar.gz
rust-f38d0da89389c45067d37ba15e783c024088a09a.zip
Implement optimize(size) and optimize(speed)
Diffstat (limited to 'src')
-rw-r--r--src/librustc/dep_graph/dep_node.rs1
-rw-r--r--src/librustc/hir/mod.rs5
-rw-r--r--src/librustc/ich/impls_hir.rs9
-rw-r--r--src/librustc/session/config.rs2
-rw-r--r--src/librustc/ty/query/config.rs6
-rw-r--r--src/librustc/ty/query/mod.rs3
-rw-r--r--src/librustc/ty/query/plumbing.rs3
-rw-r--r--src/librustc_codegen_llvm/attributes.rs44
-rw-r--r--src/librustc_codegen_llvm/back/lto.rs4
-rw-r--r--src/librustc_codegen_llvm/back/write.rs51
-rw-r--r--src/librustc_codegen_llvm/base.rs4
-rw-r--r--src/librustc_codegen_llvm/context.rs5
-rw-r--r--src/librustc_codegen_llvm/declare.rs21
-rw-r--r--src/librustc_codegen_llvm/lib.rs17
-rw-r--r--src/librustc_codegen_llvm/llvm/ffi.rs1
-rw-r--r--src/librustc_codegen_llvm/llvm_util.rs6
-rw-r--r--src/librustc_codegen_ssa/back/write.rs3
-rw-r--r--src/librustc_codegen_ssa/base.rs37
-rw-r--r--src/librustc_codegen_ssa/traits/backend.rs5
-rw-r--r--src/librustc_typeck/collect.rs115
-rw-r--r--src/librustc_typeck/diagnostics.rs1
-rw-r--r--src/libsyntax/attr/builtin.rs7
-rw-r--r--src/libsyntax/attr/mod.rs4
-rw-r--r--src/libsyntax/feature_gate.rs9
-rw-r--r--src/rustllvm/RustWrapper.cpp2
-rw-r--r--src/rustllvm/rustllvm.h1
26 files changed, 259 insertions, 107 deletions
diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs
index 4cfebaa8b5b..05f331145af 100644
--- a/src/librustc/dep_graph/dep_node.rs
+++ b/src/librustc/dep_graph/dep_node.rs
@@ -642,6 +642,7 @@ define_dep_nodes!( <'tcx>
     [eval_always] CollectAndPartitionMonoItems,
     [] IsCodegenedItem(DefId),
     [] CodegenUnit(InternedString),
+    [] BackendOptimizationLevel(CrateNum),
     [] CompileCodegenUnit(InternedString),
     [input] OutputFilenames,
     [] NormalizeProjectionTy(CanonicalProjectionGoal<'tcx>),
diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs
index b58b1d359f9..657e6e5dcde 100644
--- a/src/librustc/hir/mod.rs
+++ b/src/librustc/hir/mod.rs
@@ -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};
@@ -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>,
@@ -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![],
diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs
index 159067663d4..9022cabe779 100644
--- a/src/librustc/ich/impls_hir.rs
+++ b/src/librustc/ich/impls_hir.rs
@@ -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,
@@ -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
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index cdbef568a60..d4bc50a6fc6 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -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)]
diff --git a/src/librustc/ty/query/config.rs b/src/librustc/ty/query/config.rs
index ae41ca0cbb5..c4757574ffe 100644
--- a/src/librustc/ty/query/config.rs
+++ b/src/librustc/ty/query/config.rs
@@ -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> {
diff --git a/src/librustc/ty/query/mod.rs b/src/librustc/ty/query/mod.rs
index 10f35719cc8..ec1103b0ae5 100644
--- a/src/librustc/ty/query/mod.rs
+++ b/src/librustc/ty/query/mod.rs
@@ -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,
@@ -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 {
diff --git a/src/librustc/ty/query/plumbing.rs b/src/librustc/ty/query/plumbing.rs
index 1cc9bbd22b0..541f5b75aa5 100644
--- a/src/librustc/ty/query/plumbing.rs
+++ b/src/librustc/ty/query/plumbing.rs
@@ -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
diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs
index 226b03c99c0..5a39e4f8b7f 100644
--- a/src/librustc_codegen_llvm/attributes.rs
+++ b/src/librustc_codegen_llvm/attributes.rs
@@ -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;
@@ -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;
@@ -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) {
@@ -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
diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs
index 5d5f1ceceb8..3e51078dc64 100644
--- a/src/librustc_codegen_llvm/back/lto.rs
+++ b/src/librustc_codegen_llvm/back/lto.rs
@@ -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};
@@ -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,
diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs
index c0a4f5aa440..47637f3c5d7 100644
--- a/src/librustc_codegen_llvm/back/write.rs
+++ b/src/librustc_codegen_llvm/back/write.rs
@@ -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;
@@ -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;
@@ -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());
@@ -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| {
diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs
index 6a0d5fa1e1f..6e1ef440a3f 100644
--- a/src/librustc_codegen_llvm/base.rs
+++ b/src/librustc_codegen_llvm/base.rs
@@ -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();
@@ -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
diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs
index 2b03e99161d..1d7f14b02e1 100644
--- a/src/librustc_codegen_llvm/context.rs
+++ b/src/librustc_codegen_llvm/context.rs
@@ -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);
 
diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs
index aa2a0016b3e..166c6ab9ae9 100644
--- a/src/librustc_codegen_llvm/declare.rs
+++ b/src/librustc_codegen_llvm/declare.rs
@@ -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;
@@ -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);
diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs
index 272f34b0b3f..662b84b3a91 100644
--- a/src/librustc_codegen_llvm/lib.rs
+++ b/src/librustc_codegen_llvm/lib.rs
@@ -73,7 +73,7 @@ use rustc::dep_graph::DepGraph;
 use rustc::middle::allocator::AllocatorKind;
 use rustc::middle::cstore::{EncodedMetadata, MetadataLoader};
 use rustc::session::{Session, CompileIncomplete};
-use rustc::session::config::{OutputFilenames, OutputType, PrintRequest};
+use rustc::session::config::{OutputFilenames, OutputType, PrintRequest, OptLevel};
 use rustc::ty::{self, TyCtxt};
 use rustc::util::time_graph;
 use rustc::util::profiling::ProfileCategory;
@@ -122,8 +122,8 @@ mod va_arg;
 pub struct LlvmCodegenBackend(());
 
 impl ExtraBackendMethods for LlvmCodegenBackend {
-    fn new_metadata(&self, sess: &Session, mod_name: &str) -> ModuleLlvm {
-        ModuleLlvm::new(sess, mod_name)
+    fn new_metadata(&self, tcx: TyCtxt, mod_name: &str) -> ModuleLlvm {
+        ModuleLlvm::new(tcx, mod_name)
     }
     fn write_metadata<'b, 'gcx>(
         &self,
@@ -145,10 +145,11 @@ impl ExtraBackendMethods for LlvmCodegenBackend {
     fn target_machine_factory(
         &self,
         sess: &Session,
+        optlvl: OptLevel,
         find_features: bool
     ) -> Arc<dyn Fn() ->
         Result<&'static mut llvm::TargetMachine, String> + Send + Sync> {
-        back::write::target_machine_factory(sess, find_features)
+        back::write::target_machine_factory(sess, optlvl, find_features)
     }
     fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str {
         llvm_util::target_cpu(sess)
@@ -364,15 +365,15 @@ unsafe impl Send for ModuleLlvm { }
 unsafe impl Sync for ModuleLlvm { }
 
 impl ModuleLlvm {
-    fn new(sess: &Session, mod_name: &str) -> Self {
+    fn new(tcx: TyCtxt, mod_name: &str) -> Self {
         unsafe {
-            let llcx = llvm::LLVMRustContextCreate(sess.fewer_names());
-            let llmod_raw = context::create_module(sess, llcx, mod_name) as *const _;
+            let llcx = llvm::LLVMRustContextCreate(tcx.sess.fewer_names());
+            let llmod_raw = context::create_module(tcx, llcx, mod_name) as *const _;
 
             ModuleLlvm {
                 llmod_raw,
                 llcx,
-                tm: create_target_machine(sess, false),
+                tm: create_target_machine(tcx, false),
             }
         }
     }
diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs
index 11e34f600c2..fe2aed09ebc 100644
--- a/src/librustc_codegen_llvm/llvm/ffi.rs
+++ b/src/librustc_codegen_llvm/llvm/ffi.rs
@@ -115,6 +115,7 @@ pub enum Attribute {
     SanitizeAddress = 21,
     SanitizeMemory  = 22,
     NonLazyBind     = 23,
+    OptimizeNone    = 24,
 }
 
 /// LLVMIntPredicate
diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs
index dc70ebcf943..e2d0e558d3b 100644
--- a/src/librustc_codegen_llvm/llvm_util.rs
+++ b/src/librustc_codegen_llvm/llvm_util.rs
@@ -1,5 +1,5 @@
 use syntax_pos::symbol::Symbol;
-use back::write::create_target_machine;
+use back::write::create_informational_target_machine;
 use llvm;
 use rustc::session::Session;
 use rustc::session::config::PrintRequest;
@@ -223,7 +223,7 @@ pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str {
 }
 
 pub fn target_features(sess: &Session) -> Vec<Symbol> {
-    let target_machine = create_target_machine(sess, true);
+    let target_machine = create_informational_target_machine(sess, true);
     target_feature_whitelist(sess)
         .iter()
         .filter_map(|&(feature, gate)| {
@@ -276,7 +276,7 @@ pub fn print_passes() {
 
 pub(crate) fn print(req: PrintRequest, sess: &Session) {
     require_inited();
-    let tm = create_target_machine(sess, true);
+    let tm = create_informational_target_machine(sess, true);
     unsafe {
         match req {
             PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm),
diff --git a/src/librustc_codegen_ssa/back/write.rs b/src/librustc_codegen_ssa/back/write.rs
index 39bdc70f832..67d4d408bab 100644
--- a/src/librustc_codegen_ssa/back/write.rs
+++ b/src/librustc_codegen_ssa/back/write.rs
@@ -982,6 +982,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         None
     };
 
+    let ol = tcx.backend_optimization_level(LOCAL_CRATE);
     let cgcx = CodegenContext::<B> {
         backend: backend.clone(),
         crate_types: sess.crate_types.borrow().clone(),
@@ -1005,7 +1006,7 @@ fn start_executing_work<B: ExtraBackendMethods>(
         regular_module_config: modules_config,
         metadata_module_config: metadata_config,
         allocator_module_config: allocator_config,
-        tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, false)),
+        tm_factory: TargetMachineFactory(backend.target_machine_factory(tcx.sess, ol, false)),
         total_cgus,
         msvc_imps_needed: msvc_imps_needed(tcx),
         target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(),
diff --git a/src/librustc_codegen_ssa/base.rs b/src/librustc_codegen_ssa/base.rs
index 38caacba4d0..84e55ce0f22 100644
--- a/src/librustc_codegen_ssa/base.rs
+++ b/src/librustc_codegen_ssa/base.rs
@@ -551,7 +551,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
                                                             &["crate"],
                                                             Some("metadata")).as_str()
                                                                              .to_string();
-    let metadata_llvm_module = backend.new_metadata(tcx.sess, &metadata_cgu_name);
+    let metadata_llvm_module = backend.new_metadata(tcx, &metadata_cgu_name);
     let metadata = time(tcx.sess, "write metadata", || {
         backend.write_metadata(tcx, &metadata_llvm_module)
     });
@@ -636,7 +636,7 @@ pub fn codegen_crate<B: ExtraBackendMethods>(
                                                        &["crate"],
                                                        Some("allocator")).as_str()
                                                                          .to_string();
-        let modules = backend.new_metadata(tcx.sess, &llmod_id);
+        let modules = backend.new_metadata(tcx, &llmod_id);
         time(tcx.sess, "write allocator module", || {
             backend.codegen_allocator(tcx, &modules, kind)
         });
@@ -897,6 +897,39 @@ fn is_codegened_item(tcx: TyCtxt, id: DefId) -> bool {
 }
 
 pub fn provide_both(providers: &mut Providers) {
+    providers.backend_optimization_level = |tcx, cratenum| {
+        let for_speed = match tcx.sess.opts.optimize {
+            // If globally no optimisation is done, #[optimize] has no effect.
+            //
+            // This is done because if we ended up "upgrading" to `-O2` here, we’d populate the
+            // pass manager and it is likely that some module-wide passes (such as inliner or
+            // cross-function constant propagation) would ignore the `optnone` annotation we put
+            // on the functions, thus necessarily involving these functions into optimisations.
+            config::OptLevel::No => return config::OptLevel::No,
+            // If globally optimise-speed is already specified, just use that level.
+            config::OptLevel::Less => return config::OptLevel::Less,
+            config::OptLevel::Default => return config::OptLevel::Default,
+            config::OptLevel::Aggressive => return config::OptLevel::Aggressive,
+            // If globally optimize-for-size has been requested, use -O2 instead (if optimize(size)
+            // are present).
+            config::OptLevel::Size => config::OptLevel::Default,
+            config::OptLevel::SizeMin => config::OptLevel::Default,
+        };
+
+        let (defids, _) = tcx.collect_and_partition_mono_items(cratenum);
+        for id in &*defids {
+            let hir::CodegenFnAttrs { optimize, .. } = tcx.codegen_fn_attrs(*id);
+            match optimize {
+                attr::OptimizeAttr::None => continue,
+                attr::OptimizeAttr::Size => continue,
+                attr::OptimizeAttr::Speed => {
+                    return for_speed;
+                }
+            }
+        }
+        return tcx.sess.opts.optimize;
+    };
+
     providers.dllimport_foreign_items = |tcx, krate| {
         let module_map = tcx.foreign_modules(krate);
         let module_map = module_map.iter()
diff --git a/src/librustc_codegen_ssa/traits/backend.rs b/src/librustc_codegen_ssa/traits/backend.rs
index 8c026059dc4..73c7614d913 100644
--- a/src/librustc_codegen_ssa/traits/backend.rs
+++ b/src/librustc_codegen_ssa/traits/backend.rs
@@ -6,7 +6,7 @@ use super::CodegenObject;
 use rustc::middle::allocator::AllocatorKind;
 use rustc::middle::cstore::EncodedMetadata;
 use rustc::mir::mono::Stats;
-use rustc::session::Session;
+use rustc::session::{Session, config};
 use rustc::ty::TyCtxt;
 use rustc_codegen_utils::codegen_backend::CodegenBackend;
 use std::sync::Arc;
@@ -32,7 +32,7 @@ impl<'tcx, T> Backend<'tcx> for T where
 }
 
 pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send {
-    fn new_metadata(&self, sess: &Session, mod_name: &str) -> Self::Module;
+    fn new_metadata(&self, sess: TyCtxt, mod_name: &str) -> Self::Module;
     fn write_metadata<'b, 'gcx>(
         &self,
         tcx: TyCtxt<'b, 'gcx, 'gcx>,
@@ -50,6 +50,7 @@ pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Se
     fn target_machine_factory(
         &self,
         sess: &Session,
+        opt_level: config::OptLevel,
         find_features: bool,
     ) -> Arc<dyn Fn() -> Result<Self::TargetMachine, String> + Send + Sync>;
     fn target_cpu<'b>(&self, sess: &'b Session) -> &'b str;
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 93f14a2ea6f..2256dbcec00 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -36,7 +36,7 @@ use rustc_target::spec::abi;
 
 use syntax::ast;
 use syntax::ast::{Ident, MetaItemKind};
-use syntax::attr::{InlineAttr, list_contains_name, mark_used};
+use syntax::attr::{InlineAttr, OptimizeAttr, list_contains_name, mark_used};
 use syntax::source_map::Spanned;
 use syntax::feature_gate;
 use syntax::symbol::{keywords, Symbol};
@@ -2286,49 +2286,6 @@ fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> Codegen
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
         } else if attr.check_name("thread_local") {
             codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
-        } else if attr.check_name("inline") {
-            codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
-                if attr.path != "inline" {
-                    return ia;
-                }
-                let meta = match attr.meta() {
-                    Some(meta) => meta.node,
-                    None => return ia,
-                };
-                match meta {
-                    MetaItemKind::Word => {
-                        mark_used(attr);
-                        InlineAttr::Hint
-                    }
-                    MetaItemKind::List(ref items) => {
-                        mark_used(attr);
-                        inline_span = Some(attr.span);
-                        if items.len() != 1 {
-                            span_err!(
-                                tcx.sess.diagnostic(),
-                                attr.span,
-                                E0534,
-                                "expected one argument"
-                            );
-                            InlineAttr::None
-                        } else if list_contains_name(&items[..], "always") {
-                            InlineAttr::Always
-                        } else if list_contains_name(&items[..], "never") {
-                            InlineAttr::Never
-                        } else {
-                            span_err!(
-                                tcx.sess.diagnostic(),
-                                items[0].span,
-                                E0535,
-                                "invalid argument"
-                            );
-
-                            InlineAttr::None
-                        }
-                    }
-                    _ => ia,
-                }
-            });
         } else if attr.check_name("export_name") {
             if let Some(s) = attr.value_str() {
                 if s.as_str().contains("\0") {
@@ -2378,6 +2335,76 @@ fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> Codegen
         }
     }
 
+    codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
+        if attr.path != "inline" {
+            return ia;
+        }
+        match attr.meta().map(|i| i.node) {
+            Some(MetaItemKind::Word) => {
+                mark_used(attr);
+                InlineAttr::Hint
+            }
+            Some(MetaItemKind::List(ref items)) => {
+                mark_used(attr);
+                inline_span = Some(attr.span);
+                if items.len() != 1 {
+                    span_err!(
+                        tcx.sess.diagnostic(),
+                        attr.span,
+                        E0534,
+                        "expected one argument"
+                    );
+                    InlineAttr::None
+                } else if list_contains_name(&items[..], "always") {
+                    InlineAttr::Always
+                } else if list_contains_name(&items[..], "never") {
+                    InlineAttr::Never
+                } else {
+                    span_err!(
+                        tcx.sess.diagnostic(),
+                        items[0].span,
+                        E0535,
+                        "invalid argument"
+                    );
+
+                    InlineAttr::None
+                }
+            }
+            Some(MetaItemKind::NameValue(_)) => ia,
+            None => ia,
+        }
+    });
+
+    codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| {
+        if attr.path != "optimize" {
+            return ia;
+        }
+        let err = |sp, s| span_err!(tcx.sess.diagnostic(), sp, E0720, "{}", s);
+        match attr.meta().map(|i| i.node) {
+            Some(MetaItemKind::Word) => {
+                err(attr.span, "expected one argument");
+                ia
+            }
+            Some(MetaItemKind::List(ref items)) => {
+                mark_used(attr);
+                inline_span = Some(attr.span);
+                if items.len() != 1 {
+                    err(attr.span, "expected one argument");
+                    OptimizeAttr::None
+                } else if list_contains_name(&items[..], "size") {
+                    OptimizeAttr::Size
+                } else if list_contains_name(&items[..], "speed") {
+                    OptimizeAttr::Speed
+                } else {
+                    err(items[0].span, "invalid argument");
+                    OptimizeAttr::None
+                }
+            }
+            Some(MetaItemKind::NameValue(_)) => ia,
+            None => ia,
+        }
+    });
+
     // If a function uses #[target_feature] it can't be inlined into general
     // purpose functions as they wouldn't have the right target features
     // enabled. For that reason we also forbid #[inline(always)] as it can't be
diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs
index c0a8dd87ee2..21dcdaf4fa0 100644
--- a/src/librustc_typeck/diagnostics.rs
+++ b/src/librustc_typeck/diagnostics.rs
@@ -4719,4 +4719,5 @@ register_diagnostics! {
     E0645, // trait aliases not finished
     E0698, // type inside generator must be known in this context
     E0719, // duplicate values for associated type binding
+    E0720, // Malformed #[optimize] attribute
 }
diff --git a/src/libsyntax/attr/builtin.rs b/src/libsyntax/attr/builtin.rs
index 15e480496f7..1a4b45e76b4 100644
--- a/src/libsyntax/attr/builtin.rs
+++ b/src/libsyntax/attr/builtin.rs
@@ -63,6 +63,13 @@ pub enum InlineAttr {
     Never,
 }
 
+#[derive(Copy, Clone, Hash, PartialEq, RustcEncodable, RustcDecodable)]
+pub enum OptimizeAttr {
+    None,
+    Speed,
+    Size,
+}
+
 #[derive(Copy, Clone, PartialEq)]
 pub enum UnwindAttr {
     Allowed,
diff --git a/src/libsyntax/attr/mod.rs b/src/libsyntax/attr/mod.rs
index e5ce6a3f19a..58be7c3e085 100644
--- a/src/libsyntax/attr/mod.rs
+++ b/src/libsyntax/attr/mod.rs
@@ -4,8 +4,8 @@ mod builtin;
 
 pub use self::builtin::{
     cfg_matches, contains_feature_attr, eval_condition, find_crate_name, find_deprecation,
-    find_repr_attrs, find_stability, find_unwind_attr, Deprecation, InlineAttr, IntType, ReprAttr,
-    RustcDeprecation, Stability, StabilityLevel, UnwindAttr,
+    find_repr_attrs, find_stability, find_unwind_attr, Deprecation, InlineAttr, OptimizeAttr,
+    IntType, ReprAttr, RustcDeprecation, Stability, StabilityLevel, UnwindAttr,
 };
 pub use self::IntType::*;
 pub use self::ReprAttr::*;
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index 9b54e8f9c1f..abbcf24fca5 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -462,6 +462,9 @@ declare_features! (
 
     // Re-Rebalance coherence
     (active, re_rebalance_coherence, "1.32.0", Some(55437), None),
+
+    // #[optimize(X)]
+    (active, optimize_attribute, "1.34.0", Some(54882), None),
 );
 
 declare_features! (
@@ -1215,6 +1218,12 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu
                            "#[alloc_error_handler] is an unstable feature",
                            cfg_fn!(alloc_error_handler))),
 
+    // RFC 2412
+    ("optimize", Whitelisted, Gated(Stability::Unstable,
+                               "optimize_attribute",
+                               "#[optimize] attribute is an unstable feature",
+                               cfg_fn!(optimize_attribute))),
+
     // Crate level attributes
     ("crate_name", CrateLevel, template!(NameValueStr: "name"), Ungated),
     ("crate_type", CrateLevel, template!(NameValueStr: "bin|lib|..."), Ungated),
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 9d3e6f93b0c..474e771c1f0 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -188,6 +188,8 @@ static Attribute::AttrKind fromRust(LLVMRustAttribute Kind) {
     return Attribute::SanitizeMemory;
   case NonLazyBind:
     return Attribute::NonLazyBind;
+  case OptimizeNone:
+    return Attribute::OptimizeNone;
   }
   report_fatal_error("bad AttributeKind");
 }
diff --git a/src/rustllvm/rustllvm.h b/src/rustllvm/rustllvm.h
index 1ed281e8b1b..933266b4025 100644
--- a/src/rustllvm/rustllvm.h
+++ b/src/rustllvm/rustllvm.h
@@ -84,6 +84,7 @@ enum LLVMRustAttribute {
   SanitizeAddress = 21,
   SanitizeMemory = 22,
   NonLazyBind = 23,
+  OptimizeNone = 24,
 };
 
 typedef struct OpaqueRustString *RustStringRef;