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..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, // 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..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 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 { - 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..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) { + // 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"); }