From f38d0da89389c45067d37ba15e783c024088a09a Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 27 Oct 2018 15:29:06 +0300 Subject: [PATCH 1/5] Implement optimize(size) and optimize(speed) --- src/librustc/dep_graph/dep_node.rs | 1 + src/librustc/hir/mod.rs | 5 +- src/librustc/ich/impls_hir.rs | 9 ++ src/librustc/session/config.rs | 2 + src/librustc/ty/query/config.rs | 6 ++ src/librustc/ty/query/mod.rs | 3 +- src/librustc/ty/query/plumbing.rs | 3 + src/librustc_codegen_llvm/attributes.rs | 44 ++++++-- src/librustc_codegen_llvm/back/lto.rs | 4 +- src/librustc_codegen_llvm/back/write.rs | 53 +++++----- src/librustc_codegen_llvm/base.rs | 4 +- src/librustc_codegen_llvm/context.rs | 5 +- src/librustc_codegen_llvm/declare.rs | 21 ++-- src/librustc_codegen_llvm/lib.rs | 17 +-- src/librustc_codegen_llvm/llvm/ffi.rs | 1 + src/librustc_codegen_llvm/llvm_util.rs | 6 +- src/librustc_codegen_ssa/back/write.rs | 3 +- src/librustc_codegen_ssa/base.rs | 37 ++++++- src/librustc_codegen_ssa/traits/backend.rs | 5 +- src/librustc_typeck/collect.rs | 115 +++++++++++++-------- src/librustc_typeck/diagnostics.rs | 1 + src/libsyntax/attr/builtin.rs | 7 ++ src/libsyntax/attr/mod.rs | 4 +- src/libsyntax/feature_gate.rs | 9 ++ src/rustllvm/RustWrapper.cpp | 2 + src/rustllvm/rustllvm.h | 1 + 26 files changed, 260 insertions(+), 108 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, @@ -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> 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> for attr::InlineAttr { } } +impl<'hir> HashStable> for attr::OptimizeAttr { + fn hash_stable(&self, + hcx: &mut StableHashingContext<'hir>, + hasher: &mut StableHasher) { + 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, Arc>>>), [] fn is_codegened_item: IsCodegenedItem(DefId) -> bool, [] fn codegen_unit: CodegenUnit(InternedString) -> Arc>, + [] 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, // 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_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 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, 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 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 { - 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( None }; + let ol = tcx.backend_optimization_level(LOCAL_CRATE); let cgcx = CodegenContext:: { backend: backend.clone(), crate_types: sess.crate_types.borrow().clone(), @@ -1005,7 +1006,7 @@ fn start_executing_work( 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( &["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( &["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 Result + 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; From dcf5482cdacd166268f9672f34697dd1da05ceea Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 3 Nov 2018 11:13:10 +0200 Subject: [PATCH 2/5] Add an idealistic test for optimize attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alas it does not currently work, because of limitations in compiletest… --- src/test/codegen/optimize-attr-1.rs | 49 +++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/test/codegen/optimize-attr-1.rs diff --git a/src/test/codegen/optimize-attr-1.rs b/src/test/codegen/optimize-attr-1.rs new file mode 100644 index 00000000000..b742a361da9 --- /dev/null +++ b/src/test/codegen/optimize-attr-1.rs @@ -0,0 +1,49 @@ +// revisions: NO-OPT SIZE-OPT SPEED-OPT +// [NO-OPT]compile-flags: -Copt-level=0 +// [SIZE-OPT]compile-flags: -Copt-level=s +// [SPEED-OPT]compile-flags: -Copt-level=3 + +#![feature(optimize_attribute)] +#![crate_type="rlib"] + +// NO-OPT: Function Attrs:{{.*}}optnone +// NO-OPT-NOT: {{optsize|minsize}} +// NO-OPT-NEXT: @nothing +// NO-OPT: ret i32 %1 +// +// SIZE-OPT: Function Attrs:{{.*}}optsize +// SIZE-OPT-NOT: {{minsize|optnone}} +// SIZE-OPT-NEXT: @nothing +// SIZE-OPT-NEXT: start +// SIZE-OPT-NEXT: ret i32 4 +// +// SPEED-OPT: Function Attrs: +// SPEED-OPT-NOT: {{minsize|optnone|optsize}} +// SPEED-OPT-NEXT: @nothing +// SPEED-OPT-NEXT: start +// SPEED-OPT-NEXT: ret i32 4 +#[no_mangle] +pub fn nothing() -> i32 { + 2 + 2 +} + +// CHECK: Function Attrs:{{.*}} minsize{{.*}}optsize +// CHECK-NEXT: @size +// CHECK-NEXT: start +// CHECK-NEXT: ret i32 4 +#[optimize(size)] +#[no_mangle] +pub fn size() -> i32 { + 2 + 2 +} + +// CHECK: Function Attrs: +// CHECK-NOT: {{minsize|optsize|optnone}} +// CHECK-NEXT: @speed +// CHECK-NEXT: start +// CHECK-NEXT: ret i32 4 +#[optimize(speed)] +#[no_mangle] +pub fn speed() -> i32 { + 2 + 2 +} From 4d97b2889345d69fa72233b787b30cff73232465 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 18 Nov 2018 20:51:56 +0200 Subject: [PATCH 3/5] Support revisions for codegen tests `compile-flags: -Copt-level` will avoid adding -O. Similarly for -g and -Cdebuglevel. --- src/librustc_codegen_llvm/attributes.rs | 6 +- src/librustc_codegen_llvm/declare.rs | 4 +- .../codegen/inline-always-works-always.rs | 21 ++++++ src/test/codegen/optimize-attr-1.rs | 59 +++++++-------- src/tools/compiletest/src/header.rs | 2 + src/tools/compiletest/src/runtest.rs | 72 +++++++++++++++---- 6 files changed, 115 insertions(+), 49 deletions(-) create mode 100644 src/test/codegen/inline-always-works-always.rs diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index 5a39e4f8b7f..a7d4f910f7a 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -155,8 +155,6 @@ pub fn from_fn_attrs( let codegen_fn_attrs = id.map(|id| cx.tcx.codegen_fn_attrs(id)) .unwrap_or_else(|| CodegenFnAttrs::new()); - inline(cx, llfn, codegen_fn_attrs.inline); - match codegen_fn_attrs.optimize { OptimizeAttr::None => { match cx.tcx.sess.opts.optimize { @@ -173,7 +171,7 @@ pub fn from_fn_attrs( OptLevel::No => { llvm::Attribute::MinSize.unapply_llfn(Function, llfn); llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn); - llvm::Attribute::OptimizeNone.apply_llfn(Function, llfn); + llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn); } _ => {} } @@ -190,6 +188,8 @@ pub fn from_fn_attrs( } } + inline(cx, llfn, codegen_fn_attrs.inline); + // 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/declare.rs b/src/librustc_codegen_llvm/declare.rs index 166c6ab9ae9..da45e0200d8 100644 --- a/src/librustc_codegen_llvm/declare.rs +++ b/src/librustc_codegen_llvm/declare.rs @@ -65,7 +65,7 @@ fn declare_raw_fn( } } - // FIXME(opt): this is kinda duplicated with similar code in attributes::from_fm_attrs… + // FIXME(opt): this is kinda duplicated with similar code in attributes::from_fn_attrs… match cx.tcx.sess.opts.optimize { OptLevel::Size => { llvm::Attribute::MinSize.unapply_llfn(Function, llfn); @@ -80,7 +80,7 @@ fn declare_raw_fn( OptLevel::No => { llvm::Attribute::MinSize.unapply_llfn(Function, llfn); llvm::Attribute::OptimizeForSize.unapply_llfn(Function, llfn); - llvm::Attribute::OptimizeNone.apply_llfn(Function, llfn); + llvm::Attribute::OptimizeNone.unapply_llfn(Function, llfn); } _ => {} } diff --git a/src/test/codegen/inline-always-works-always.rs b/src/test/codegen/inline-always-works-always.rs new file mode 100644 index 00000000000..912af782a8f --- /dev/null +++ b/src/test/codegen/inline-always-works-always.rs @@ -0,0 +1,21 @@ +// revisions: NO-OPT SIZE-OPT SPEED-OPT +//[NO-OPT] compile-flags: -Copt-level=0 +//[SIZE-OPT] compile-flags: -Copt-level=s +//[SPEED-OPT] compile-flags: -Copt-level=3 + +#![crate_type="rlib"] + +#[no_mangle] +#[inline(always)] +pub extern "C" fn callee() -> u32 { + 4 + 4 +} + +// CHECK-LABEL: caller +// SIZE-OPT: ret i32 8 +// SPEED-OPT: ret i32 8 +// NO-OPT: ret i32 8 +#[no_mangle] +pub extern "C" fn caller() -> u32 { + callee() +} diff --git a/src/test/codegen/optimize-attr-1.rs b/src/test/codegen/optimize-attr-1.rs index b742a361da9..376447e5b5d 100644 --- a/src/test/codegen/optimize-attr-1.rs +++ b/src/test/codegen/optimize-attr-1.rs @@ -1,49 +1,50 @@ // revisions: NO-OPT SIZE-OPT SPEED-OPT -// [NO-OPT]compile-flags: -Copt-level=0 -// [SIZE-OPT]compile-flags: -Copt-level=s -// [SPEED-OPT]compile-flags: -Copt-level=3 +//[NO-OPT] compile-flags: -Copt-level=0 -Ccodegen-units=1 +//[SIZE-OPT] compile-flags: -Copt-level=s -Ccodegen-units=1 +//[SPEED-OPT] compile-flags: -Copt-level=3 -Ccodegen-units=1 #![feature(optimize_attribute)] #![crate_type="rlib"] -// NO-OPT: Function Attrs:{{.*}}optnone -// NO-OPT-NOT: {{optsize|minsize}} -// NO-OPT-NEXT: @nothing +// CHECK-LABEL: define i32 @nothing +// CHECK-SAME: [[NOTHING_ATTRS:#[0-9]+]] // NO-OPT: ret i32 %1 -// -// SIZE-OPT: Function Attrs:{{.*}}optsize -// SIZE-OPT-NOT: {{minsize|optnone}} -// SIZE-OPT-NEXT: @nothing -// SIZE-OPT-NEXT: start -// SIZE-OPT-NEXT: ret i32 4 -// -// SPEED-OPT: Function Attrs: -// SPEED-OPT-NOT: {{minsize|optnone|optsize}} -// SPEED-OPT-NEXT: @nothing -// SPEED-OPT-NEXT: start -// SPEED-OPT-NEXT: ret i32 4 +// SIZE-OPT: ret i32 4 +// SPEEC-OPT: ret i32 4 #[no_mangle] pub fn nothing() -> i32 { 2 + 2 } -// CHECK: Function Attrs:{{.*}} minsize{{.*}}optsize -// CHECK-NEXT: @size -// CHECK-NEXT: start -// CHECK-NEXT: ret i32 4 +// CHECK-LABEL: define i32 @size +// CHECK-SAME: [[SIZE_ATTRS:#[0-9]+]] +// NO-OPT: ret i32 %1 +// SIZE-OPT: ret i32 6 +// SPEED-OPT: ret i32 6 #[optimize(size)] #[no_mangle] pub fn size() -> i32 { - 2 + 2 + 3 + 3 } -// CHECK: Function Attrs: -// CHECK-NOT: {{minsize|optsize|optnone}} -// CHECK-NEXT: @speed -// CHECK-NEXT: start -// CHECK-NEXT: ret i32 4 +// CHECK-LABEL: define i32 @speed +// NO-OPT-SAME: [[NOTHING_ATTRS]] +// SPEED-OPT-SAME: [[NOTHING_ATTRS]] +// SIZE-OPT-SAME: [[SPEED_ATTRS:#[0-9]+]] +// NO-OPT: ret i32 %1 +// SIZE-OPT: ret i32 8 +// SPEED-OPT: ret i32 8 #[optimize(speed)] #[no_mangle] pub fn speed() -> i32 { - 2 + 2 + 4 + 4 } + +// NO-OPT-DAG: attributes [[SIZE_ATTRS]] = {{.*}}minsize{{.*}}optsize{{.*}} +// SPEED-OPT-DAG: attributes [[SIZE_ATTRS]] = {{.*}}minsize{{.*}}optsize{{.*}} +// SIZE-OPT-DAG: attributes [[NOTHING_ATTRS]] = {{.*}}optsize{{.*}} +// SIZE-OPT-DAG: attributes [[SIZE_ATTRS]] = {{.*}}minsize{{.*}}optsize{{.*}} + +// SIZE-OPT: attributes [[SPEED_ATTRS]] +// SIZE-OPT-NOT: minsize +// SIZE-OPT-NOT: optsize diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 5eb1f5ec5ff..394e408e415 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -542,6 +542,8 @@ fn iter_header(testfile: &Path, cfg: Option<&str>, it: &mut dyn FnMut(&str)) { "#" }; + // FIXME: would be nice to allow some whitespace between comment and brace :) + // It took me like 2 days to debug why compile-flags weren’t taken into account for my test :) let comment_with_brace = comment.to_string() + "["; let rdr = BufReader::new(File::open(testfile).unwrap()); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 400c205d44b..375dc1c1283 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -552,9 +552,10 @@ impl<'test> TestCx<'test> { .args(&["--target", &self.config.target]) .arg("-L") .arg(&aux_dir) - .args(self.split_maybe_args(&self.config.target_rustcflags)) .args(&self.props.compile_flags) .envs(self.props.exec_env.clone()); + self.maybe_add_external_args(&mut rustc, + self.split_maybe_args(&self.config.target_rustcflags)); let src = match read_from { ReadFrom::Stdin(src) => Some(src), @@ -587,6 +588,15 @@ impl<'test> TestCx<'test> { } } + fn set_revision_flags(&self, cmd: &mut Command) { + if let Some(revision) = self.revision { + // Normalize revisions to be lowercase and replace `-`s with `_`s. + // Otherwise the `--cfg` flag is not valid. + let normalized_revision = revision.to_lowercase().replace("-", "_"); + cmd.args(&["--cfg", &normalized_revision]); + } + } + fn typecheck_source(&self, src: String) -> ProcRes { let mut rustc = Command::new(&self.config.rustc_path); @@ -612,12 +622,9 @@ impl<'test> TestCx<'test> { .arg(&self.config.build_base) .arg("-L") .arg(aux_dir); - - if let Some(revision) = self.revision { - rustc.args(&["--cfg", revision]); - } - - rustc.args(self.split_maybe_args(&self.config.target_rustcflags)); + self.set_revision_flags(&mut rustc); + self.maybe_add_external_args(&mut rustc, + self.split_maybe_args(&self.config.target_rustcflags)); rustc.args(&self.props.compile_flags); self.compose_and_run_compiler(rustc, Some(src)) @@ -1119,6 +1126,35 @@ impl<'test> TestCx<'test> { Some(new_options.join(" ")) } + fn maybe_add_external_args(&self, cmd: &mut Command, args: Vec) { + // Filter out the arguments that should not be added by runtest here. + // + // Notable use-cases are: do not add our optimisation flag if + // `compile-flags: -Copt-level=x` and similar for debug-info level as well. + const OPT_FLAGS: &[&str] = &["-O", "-Copt-level=", /*-C*/"opt-level="]; + const DEBUG_FLAGS: &[&str] = &["-g", "-Cdebuginfo=", /*-C*/"debuginfo="]; + + // FIXME: ideally we would "just" check the `cmd` itself, but it does not allow inspecting + // its arguments. They need to be collected separately. For now I cannot be bothered to + // implement this the "right" way. + let have_opt_flag = self.props.compile_flags.iter().any(|arg| { + OPT_FLAGS.iter().any(|f| arg.starts_with(f)) + }); + let have_debug_flag = self.props.compile_flags.iter().any(|arg| { + DEBUG_FLAGS.iter().any(|f| arg.starts_with(f)) + }); + + for arg in args { + if OPT_FLAGS.iter().any(|f| arg.starts_with(f)) && have_opt_flag { + continue; + } + if DEBUG_FLAGS.iter().any(|f| arg.starts_with(f)) && have_debug_flag { + continue; + } + cmd.arg(arg); + } + } + fn check_debugger_output(&self, debugger_run_result: &ProcRes, check_lines: &[String]) { let num_check_lines = check_lines.len(); @@ -1707,10 +1743,7 @@ impl<'test> TestCx<'test> { rustc.arg(&format!("--target={}", target)); } - - if let Some(revision) = self.revision { - rustc.args(&["--cfg", revision]); - } + self.set_revision_flags(&mut rustc); if !is_rustdoc { if let Some(ref incremental_dir) = self.props.incremental_dir { @@ -1810,9 +1843,11 @@ impl<'test> TestCx<'test> { } if self.props.force_host { - rustc.args(self.split_maybe_args(&self.config.host_rustcflags)); + self.maybe_add_external_args(&mut rustc, + self.split_maybe_args(&self.config.host_rustcflags)); } else { - rustc.args(self.split_maybe_args(&self.config.target_rustcflags)); + self.maybe_add_external_args(&mut rustc, + self.split_maybe_args(&self.config.target_rustcflags)); if !is_rustdoc { if let Some(ref linker) = self.config.linker { rustc.arg(format!("-Clinker={}", linker)); @@ -2065,12 +2100,19 @@ impl<'test> TestCx<'test> { .arg("--input-file") .arg(irfile) .arg(&self.testpaths.file); + // It would be more appropriate to make most of the arguments configurable through + // a comment-attribute similar to `compile-flags`. For example, --check-prefixes is a very + // useful flag. + // + // For now, though… + if let Some(rev) = self.revision { + let prefixes = format!("CHECK,{}", rev); + filecheck.args(&["--check-prefixes", &prefixes]); + } self.compose_and_run(filecheck, "", None, None) } fn run_codegen_test(&self) { - assert!(self.revision.is_none(), "revisions not relevant here"); - if self.config.llvm_filecheck.is_none() { self.fatal("missing --llvm-filecheck"); } From 89e34d3e32bcb8bc52f3b9deeae7034d7f045388 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 25 Nov 2018 19:56:10 +0200 Subject: [PATCH 4/5] Add a feature gate test for #[optimize] --- .../ui/feature-gate-optimize_attribute.rs | 25 +++++++++++ .../ui/feature-gate-optimize_attribute.stderr | 43 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/test/ui/feature-gate-optimize_attribute.rs create mode 100644 src/test/ui/feature-gate-optimize_attribute.stderr diff --git a/src/test/ui/feature-gate-optimize_attribute.rs b/src/test/ui/feature-gate-optimize_attribute.rs new file mode 100644 index 00000000000..4c3126f534e --- /dev/null +++ b/src/test/ui/feature-gate-optimize_attribute.rs @@ -0,0 +1,25 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![crate_type="rlib"] +#![optimize(speed)] //~ ERROR #54882 + +#[optimize(size)] //~ ERROR #54882 +mod module { + +#[optimize(size)] //~ ERROR #54882 +fn size() {} + +#[optimize(speed)] //~ ERROR #54882 +fn speed() {} + +#[optimize(banana)] //~ ERROR #54882 +fn not_known() {} + +} diff --git a/src/test/ui/feature-gate-optimize_attribute.stderr b/src/test/ui/feature-gate-optimize_attribute.stderr new file mode 100644 index 00000000000..cd790a0bb6a --- /dev/null +++ b/src/test/ui/feature-gate-optimize_attribute.stderr @@ -0,0 +1,43 @@ +error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) + --> $DIR/feature-gate-optimize_attribute.rs:16:1 + | +LL | #[optimize(size)] //~ ERROR #54882 + | ^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(optimize_attribute)] to the crate attributes to enable + +error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) + --> $DIR/feature-gate-optimize_attribute.rs:19:1 + | +LL | #[optimize(speed)] //~ ERROR #54882 + | ^^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(optimize_attribute)] to the crate attributes to enable + +error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) + --> $DIR/feature-gate-optimize_attribute.rs:22:1 + | +LL | #[optimize(banana)] //~ ERROR #54882 + | ^^^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(optimize_attribute)] to the crate attributes to enable + +error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) + --> $DIR/feature-gate-optimize_attribute.rs:13:1 + | +LL | #[optimize(size)] //~ ERROR #54882 + | ^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(optimize_attribute)] to the crate attributes to enable + +error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) + --> $DIR/feature-gate-optimize_attribute.rs:11:1 + | +LL | #![optimize(speed)] //~ ERROR #54882 + | ^^^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(optimize_attribute)] to the crate attributes to enable + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0658`. From ce289c6c9911c7ea55b7f30b125d3c38ed359da4 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 19 Jan 2019 00:37:52 +0200 Subject: [PATCH 5/5] Resolve breakage --- src/librustc_codegen_llvm/attributes.rs | 41 +++++++++++-------- src/librustc_codegen_llvm/declare.rs | 24 +---------- src/librustc_typeck/collect.rs | 2 +- src/librustc_typeck/diagnostics.rs | 2 +- src/libsyntax/feature_gate.rs | 2 +- .../ui/feature-gate-optimize_attribute.rs | 13 ++---- .../ui/feature-gate-optimize_attribute.stderr | 23 +++++++---- 7 files changed, 46 insertions(+), 61 deletions(-) diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index a7d4f910f7a..e6bc7bca46b 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -144,6 +144,28 @@ pub fn non_lazy_bind(sess: &Session, llfn: &'ll Value) { } } +pub(crate) fn default_optimisation_attrs(sess: &Session, llfn: &'ll Value) { + match 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.unapply_llfn(Function, llfn); + } + _ => {} + } +} + + /// Composite function which sets LLVM attributes for function depending on its AST (`#[attribute]`) /// attributes. pub fn from_fn_attrs( @@ -157,24 +179,7 @@ pub fn from_fn_attrs( 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.unapply_llfn(Function, llfn); - } - _ => {} - } + default_optimisation_attrs(cx.tcx.sess, llfn); } OptimizeAttr::Speed => { llvm::Attribute::MinSize.unapply_llfn(Function, llfn); diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs index da45e0200d8..6b7ee16cb71 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, OptLevel}; +use rustc::session::config::Sanitizer; use rustc_data_structures::small_c_str::SmallCStr; use abi::{FnType, FnTypeExt}; use attributes; @@ -65,28 +65,8 @@ fn declare_raw_fn( } } - // FIXME(opt): this is kinda duplicated with similar code in attributes::from_fn_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); - }, - 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.unapply_llfn(Function, llfn); - } - _ => {} - } - + attributes::default_optimisation_attrs(cx.tcx.sess, llfn); attributes::non_lazy_bind(cx.sess(), llfn); - llfn } diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 2256dbcec00..ade84faae8d 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -2379,7 +2379,7 @@ fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> Codegen if attr.path != "optimize" { return ia; } - let err = |sp, s| span_err!(tcx.sess.diagnostic(), sp, E0720, "{}", s); + let err = |sp, s| span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s); match attr.meta().map(|i| i.node) { Some(MetaItemKind::Word) => { err(attr.span, "expected one argument"); diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 21dcdaf4fa0..e02111bf2bf 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4719,5 +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 + E0722, // Malformed #[optimize] attribute } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index abbcf24fca5..adc5affedc8 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -1219,7 +1219,7 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, AttributeType, AttributeTemplate, Attribu cfg_fn!(alloc_error_handler))), // RFC 2412 - ("optimize", Whitelisted, Gated(Stability::Unstable, + ("optimize", Whitelisted, template!(List: "size|speed"), Gated(Stability::Unstable, "optimize_attribute", "#[optimize] attribute is an unstable feature", cfg_fn!(optimize_attribute))), diff --git a/src/test/ui/feature-gate-optimize_attribute.rs b/src/test/ui/feature-gate-optimize_attribute.rs index 4c3126f534e..c1f75100141 100644 --- a/src/test/ui/feature-gate-optimize_attribute.rs +++ b/src/test/ui/feature-gate-optimize_attribute.rs @@ -1,12 +1,3 @@ -// Copyright 2018 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. #![crate_type="rlib"] #![optimize(speed)] //~ ERROR #54882 @@ -19,7 +10,9 @@ fn size() {} #[optimize(speed)] //~ ERROR #54882 fn speed() {} -#[optimize(banana)] //~ ERROR #54882 +#[optimize(banana)] +//~^ ERROR #54882 +//~| ERROR E0722 fn not_known() {} } diff --git a/src/test/ui/feature-gate-optimize_attribute.stderr b/src/test/ui/feature-gate-optimize_attribute.stderr index cd790a0bb6a..ddd4c457d73 100644 --- a/src/test/ui/feature-gate-optimize_attribute.stderr +++ b/src/test/ui/feature-gate-optimize_attribute.stderr @@ -1,5 +1,5 @@ error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) - --> $DIR/feature-gate-optimize_attribute.rs:16:1 + --> $DIR/feature-gate-optimize_attribute.rs:7:1 | LL | #[optimize(size)] //~ ERROR #54882 | ^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[optimize(size)] //~ ERROR #54882 = help: add #![feature(optimize_attribute)] to the crate attributes to enable error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) - --> $DIR/feature-gate-optimize_attribute.rs:19:1 + --> $DIR/feature-gate-optimize_attribute.rs:10:1 | LL | #[optimize(speed)] //~ ERROR #54882 | ^^^^^^^^^^^^^^^^^^ @@ -15,15 +15,15 @@ LL | #[optimize(speed)] //~ ERROR #54882 = help: add #![feature(optimize_attribute)] to the crate attributes to enable error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) - --> $DIR/feature-gate-optimize_attribute.rs:22:1 + --> $DIR/feature-gate-optimize_attribute.rs:13:1 | -LL | #[optimize(banana)] //~ ERROR #54882 +LL | #[optimize(banana)] | ^^^^^^^^^^^^^^^^^^^ | = help: add #![feature(optimize_attribute)] to the crate attributes to enable error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) - --> $DIR/feature-gate-optimize_attribute.rs:13:1 + --> $DIR/feature-gate-optimize_attribute.rs:4:1 | LL | #[optimize(size)] //~ ERROR #54882 | ^^^^^^^^^^^^^^^^^ @@ -31,13 +31,20 @@ LL | #[optimize(size)] //~ ERROR #54882 = help: add #![feature(optimize_attribute)] to the crate attributes to enable error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) - --> $DIR/feature-gate-optimize_attribute.rs:11:1 + --> $DIR/feature-gate-optimize_attribute.rs:2:1 | LL | #![optimize(speed)] //~ ERROR #54882 | ^^^^^^^^^^^^^^^^^^^ | = help: add #![feature(optimize_attribute)] to the crate attributes to enable -error: aborting due to 5 previous errors +error[E0722]: invalid argument + --> $DIR/feature-gate-optimize_attribute.rs:13:12 + | +LL | #[optimize(banana)] + | ^^^^^^ -For more information about this error, try `rustc --explain E0658`. +error: aborting due to 6 previous errors + +Some errors occurred: E0658, E0722. +For more information about an error, try `rustc --explain E0658`.