diff options
| author | bors <bors@rust-lang.org> | 2019-01-26 07:08:18 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2019-01-26 07:08:18 +0000 |
| commit | 42eb5ff4042236ec3635035332e059afa7d63f9d (patch) | |
| tree | 2add28d90e48f64c036bf3dd0b37f454979e44e2 /src | |
| parent | 9df043b543bb9bc3e50bc243811c58d52a3aefea (diff) | |
| parent | ce289c6c9911c7ea55b7f30b125d3c38ed359da4 (diff) | |
| download | rust-42eb5ff4042236ec3635035332e059afa7d63f9d.tar.gz rust-42eb5ff4042236ec3635035332e059afa7d63f9d.zip | |
Auto merge of #55641 - nagisa:optimize-attr, r=pnkfelix
Implement optimize(size) and optimize(speed) attributes This PR implements both `optimize(size)` and `optimize(speed)` attributes. While the functionality itself works fine now, this PR is not yet complete: the code might be messy in places and, most importantly, the compiletest must be improved with functionality to run tests with custom optimization levels. Otherwise the new attribute cannot be tested properly. Oh, and not all of the RFC is implemented – attribute propagation is not implemented for example. # TODO * [x] Improve compiletest so that tests can be written; * [x] Assign a proper error number (E9999 currently, no idea how to allocate a number properly); * [ ] Perhaps reduce the duplication in LLVM attribute assignment code…
Diffstat (limited to 'src')
32 files changed, 448 insertions, 128 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..e6bc7bca46b 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) { @@ -151,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( @@ -162,6 +177,22 @@ pub fn from_fn_attrs( let codegen_fn_attrs = id.map(|id| cx.tcx.codegen_fn_attrs(id)) .unwrap_or_else(|| CodegenFnAttrs::new()); + match codegen_fn_attrs.optimize { + OptimizeAttr::None => { + default_optimisation_attrs(cx.tcx.sess, 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); + } + } + inline(cx, llfn, codegen_fn_attrs.inline); // The `uwtable` attribute according to LLVM is: 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..6b7ee16cb71 100644 --- a/src/librustc_codegen_llvm/declare.rs +++ b/src/librustc_codegen_llvm/declare.rs @@ -65,19 +65,8 @@ fn declare_raw_fn( } } - match cx.tcx.sess.opts.cg.opt_level.as_ref().map(String::as_ref) { - Some("s") => { - llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); - }, - Some("z") => { - llvm::Attribute::MinSize.apply_llfn(Function, llfn); - llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); - }, - _ => {}, - } - + attributes::default_optimisation_attrs(cx.tcx.sess, llfn); attributes::non_lazy_bind(cx.sess(), llfn); - llfn } diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index a88b1a8f52e..ab2fb67d549 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -72,7 +72,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; @@ -121,8 +121,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, @@ -144,10 +144,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) @@ -363,15 +364,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 199c1220a2c..58bdfc47fca 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..ade84faae8d 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, E0722, "{}", 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 1f260d317e4..3ed09dfe992 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 + E0722, // 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 85e80f7bdaf..d74a0d5623a 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -461,6 +461,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! ( @@ -1222,6 +1225,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, template!(List: "size|speed"), 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 e973318057c..b33165b8463 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; 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 new file mode 100644 index 00000000000..376447e5b5d --- /dev/null +++ b/src/test/codegen/optimize-attr-1.rs @@ -0,0 +1,50 @@ +// revisions: NO-OPT SIZE-OPT SPEED-OPT +//[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"] + +// CHECK-LABEL: define i32 @nothing +// CHECK-SAME: [[NOTHING_ATTRS:#[0-9]+]] +// NO-OPT: ret i32 %1 +// SIZE-OPT: ret i32 4 +// SPEEC-OPT: ret i32 4 +#[no_mangle] +pub fn nothing() -> i32 { + 2 + 2 +} + +// 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 { + 3 + 3 +} + +// 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 { + 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/test/ui/feature-gate-optimize_attribute.rs b/src/test/ui/feature-gate-optimize_attribute.rs new file mode 100644 index 00000000000..c1f75100141 --- /dev/null +++ b/src/test/ui/feature-gate-optimize_attribute.rs @@ -0,0 +1,18 @@ +#![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 +//~| 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 new file mode 100644 index 00000000000..ddd4c457d73 --- /dev/null +++ b/src/test/ui/feature-gate-optimize_attribute.stderr @@ -0,0 +1,50 @@ +error[E0658]: #[optimize] attribute is an unstable feature (see issue #54882) + --> $DIR/feature-gate-optimize_attribute.rs:7: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:10: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:13:1 + | +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:4: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:2:1 + | +LL | #![optimize(speed)] //~ ERROR #54882 + | ^^^^^^^^^^^^^^^^^^^ + | + = help: add #![feature(optimize_attribute)] to the crate attributes to enable + +error[E0722]: invalid argument + --> $DIR/feature-gate-optimize_attribute.rs:13:12 + | +LL | #[optimize(banana)] + | ^^^^^^ + +error: aborting due to 6 previous errors + +Some errors occurred: E0658, E0722. +For more information about an error, try `rustc --explain E0658`. 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<String>) { + // 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<space>*/"opt-level="]; + const DEBUG_FLAGS: &[&str] = &["-g", "-Cdebuginfo=", /*-C<space>*/"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"); } |
