diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index ec52c6cf57b..eda7ddb161f 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -635,6 +635,10 @@ define_dep_nodes!( <'tcx> [] Null, [] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) }, + + [input] TargetFeaturesWhitelist, + [] TargetFeaturesEnabled(DefId), + ); trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug { diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index 881c59e05aa..8dedcb24c2f 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -631,6 +631,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::substitute_normalize_and_test_pre } } +impl<'tcx> QueryDescription<'tcx> for queries::target_features_whitelist<'tcx> { + fn describe(_tcx: TyCtxt, _: CrateNum) -> String { + format!("looking up the whitelist of target features") + } +} + 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/maps/mod.rs b/src/librustc/ty/maps/mod.rs index bd8c22aab93..51cae9e13f8 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -363,6 +363,11 @@ define_maps! { <'tcx> [] fn substitute_normalize_and_test_predicates: substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool, + + [] fn target_features_whitelist: + target_features_whitelist_node(CrateNum) -> Rc>, + [] fn target_features_enabled: TargetFeaturesEnabled(DefId) -> Rc>, + } ////////////////////////////////////////////////////////////////////// @@ -508,3 +513,7 @@ fn substitute_normalize_and_test_predicates_node<'tcx>(key: (DefId, &'tcx Substs -> DepConstructor<'tcx> { DepConstructor::SubstituteNormalizeAndTestPredicates { key } } + +fn target_features_whitelist_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { + DepConstructor::TargetFeaturesWhitelist +} diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index dd8b8a2e5da..4f6f1ab0ee2 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -918,6 +918,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, } DepKind::IsTranslatedFunction => { force!(is_translated_function, def_id!()); } DepKind::OutputFilenames => { force!(output_filenames, LOCAL_CRATE); } + + DepKind::TargetFeaturesWhitelist => { force!(target_features_whitelist, LOCAL_CRATE); } + DepKind::TargetFeaturesEnabled => { force!(target_features_enabled, def_id!()); } } true diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs index 745aa0da829..f3105e03523 100644 --- a/src/librustc_trans/attributes.rs +++ b/src/librustc_trans/attributes.rs @@ -10,11 +10,18 @@ //! Set and unset common attributes on LLVM values. use std::ffi::{CStr, CString}; +use std::rc::Rc; +use rustc::hir::Unsafety; +use rustc::hir::def_id::{DefId, LOCAL_CRATE}; use rustc::session::config::Sanitizer; +use rustc::ty::TyCtxt; +use rustc::ty::maps::Providers; +use rustc_data_structures::fx::FxHashSet; use llvm::{self, Attribute, ValueRef}; use llvm::AttributePlace::Function; +use llvm_util; pub use syntax::attr::{self, InlineAttr}; use syntax::ast; use context::CrateContext; @@ -94,23 +101,16 @@ pub fn set_probestack(ccx: &CrateContext, llfn: ValueRef) { /// Composite function which sets LLVM attributes for function depending on its AST (#[attribute]) /// attributes. -pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) { +pub fn from_fn_attrs(ccx: &CrateContext, llfn: ValueRef, id: DefId) { use syntax::attr::*; - inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), attrs)); + let attrs = ccx.tcx().get_attrs(id); + inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), &attrs)); set_frame_pointer_elimination(ccx, llfn); set_probestack(ccx, llfn); - let mut target_features = vec![]; - for attr in attrs { - if attr.check_name("target_feature") { - if let Some(val) = attr.value_str() { - for feat in val.as_str().split(",").map(|f| f.trim()) { - if !feat.is_empty() && !feat.contains('\0') { - target_features.push(feat.to_string()); - } - } - } - } else if attr.check_name("cold") { + + for attr in attrs.iter() { + if attr.check_name("cold") { Attribute::Cold.apply_llfn(Function, llfn); } else if attr.check_name("naked") { naked(llfn, true); @@ -123,6 +123,8 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe unwind(llfn, false); } } + + let target_features = ccx.tcx().target_features_enabled(id); if !target_features.is_empty() { let val = CString::new(target_features.join(",")).unwrap(); llvm::AddFunctionAttrStringValue( @@ -134,3 +136,97 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe fn cstr(s: &'static str) -> &CStr { CStr::from_bytes_with_nul(s.as_bytes()).expect("null-terminated string") } + +pub fn provide(providers: &mut Providers) { + providers.target_features_whitelist = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + Rc::new(llvm_util::target_feature_whitelist(tcx.sess) + .iter() + .map(|c| c.to_str().unwrap().to_string()) + .collect()) + }; + + providers.target_features_enabled = |tcx, id| { + let whitelist = tcx.target_features_whitelist(LOCAL_CRATE); + let mut target_features = Vec::new(); + for attr in tcx.get_attrs(id).iter() { + if !attr.check_name("target_feature") { + continue + } + if let Some(val) = attr.value_str() { + for feat in val.as_str().split(",").map(|f| f.trim()) { + if !feat.is_empty() && !feat.contains('\0') { + target_features.push(feat.to_string()); + } + } + let msg = "#[target_feature = \"..\"] is deprecated and will \ + eventually be removed, use \ + #[target_feature(enable = \"..\")] instead"; + tcx.sess.span_warn(attr.span, &msg); + continue + } + + if tcx.fn_sig(id).unsafety() == Unsafety::Normal { + let msg = "#[target_feature(..)] can only be applied to \ + `unsafe` function"; + tcx.sess.span_err(attr.span, msg); + } + from_target_feature(tcx, attr, &whitelist, &mut target_features); + } + Rc::new(target_features) + }; +} + +fn from_target_feature( + tcx: TyCtxt, + attr: &ast::Attribute, + whitelist: &FxHashSet, + target_features: &mut Vec, +) { + let list = match attr.meta_item_list() { + Some(list) => list, + None => { + let msg = "#[target_feature] attribute must be of the form \ + #[target_feature(..)]"; + tcx.sess.span_err(attr.span, &msg); + return + } + }; + + for item in list { + if !item.check_name("enable") { + let msg = "#[target_feature(..)] only accepts sub-keys of `enable` \ + currently"; + tcx.sess.span_err(item.span, &msg); + continue + } + let value = match item.value_str() { + Some(list) => list, + None => { + let msg = "#[target_feature] attribute must be of the form \ + #[target_feature(enable = \"..\")]"; + tcx.sess.span_err(item.span, &msg); + continue + } + }; + let value = value.as_str(); + for feature in value.split(',') { + if whitelist.contains(feature) { + target_features.push(format!("+{}", feature)); + continue + } + + let msg = format!("the feature named `{}` is not valid for \ + this target", feature); + let mut err = tcx.sess.struct_span_err(item.span, &msg); + + if feature.starts_with("+") { + let valid = whitelist.contains(&feature[1..]); + if valid { + err.help("consider removing the leading `+` in the feature name"); + } + } + err.emit(); + } + } +} diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs index 0a0f2615a1b..ccbc6620ffe 100644 --- a/src/librustc_trans/callee.rs +++ b/src/librustc_trans/callee.rs @@ -99,8 +99,7 @@ pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, if instance.def.is_inline(tcx) { attributes::inline(llfn, attributes::InlineAttr::Hint); } - let attrs = instance.def.attrs(ccx.tcx()); - attributes::from_fn_attrs(ccx, &attrs, llfn); + attributes::from_fn_attrs(ccx, llfn, instance.def.def_id()); let instance_def_id = instance.def_id(); diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index ee08a7f1ec4..974c268749b 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -167,6 +167,7 @@ impl rustc_trans_utils::trans_crate::TransCrate for LlvmTransCrate { back::symbol_names::provide(providers); back::symbol_export::provide(providers); base::provide(providers); + attributes::provide(providers); } fn provide_extern(providers: &mut ty::maps::Providers) { diff --git a/src/librustc_trans/llvm_util.rs b/src/librustc_trans/llvm_util.rs index b3d0b574d1d..8112a9eeab1 100644 --- a/src/librustc_trans/llvm_util.rs +++ b/src/librustc_trans/llvm_util.rs @@ -13,8 +13,8 @@ use back::write::create_target_machine; use llvm; use rustc::session::Session; use rustc::session::config::PrintRequest; -use libc::{c_int, c_char}; -use std::ffi::CString; +use libc::c_int; +use std::ffi::{CStr, CString}; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Once; @@ -97,8 +97,18 @@ const POWERPC_WHITELIST: &'static [&'static str] = &["altivec\0", const MIPS_WHITELIST: &'static [&'static str] = &["msa\0"]; pub fn target_features(sess: &Session) -> Vec { + let whitelist = target_feature_whitelist(sess); let target_machine = create_target_machine(sess); + let mut features = Vec::new(); + for feat in whitelist { + if unsafe { llvm::LLVMRustHasFeature(target_machine, feat.as_ptr()) } { + features.push(Symbol::intern(feat.to_str().unwrap())); + } + } + features +} +pub fn target_feature_whitelist(sess: &Session) -> Vec<&CStr> { let whitelist = match &*sess.target.target.arch { "arm" => ARM_WHITELIST, "aarch64" => AARCH64_WHITELIST, @@ -108,15 +118,9 @@ pub fn target_features(sess: &Session) -> Vec { "powerpc" | "powerpc64" => POWERPC_WHITELIST, _ => &[], }; - - let mut features = Vec::new(); - for feat in whitelist { - assert_eq!(feat.chars().last(), Some('\0')); - if unsafe { llvm::LLVMRustHasFeature(target_machine, feat.as_ptr() as *const c_char) } { - features.push(Symbol::intern(&feat[..feat.len() - 1])); - } - } - features + whitelist.iter().map(|m| { + CStr::from_bytes_with_nul(m.as_bytes()).unwrap() + }).collect() } pub fn print_version() { diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs index 31d8e092c4a..fa6a42e062d 100644 --- a/src/librustc_trans/trans_item.rs +++ b/src/librustc_trans/trans_item.rs @@ -199,7 +199,7 @@ fn predefine_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, if instance.def.is_inline(ccx.tcx()) { attributes::inline(lldecl, attributes::InlineAttr::Hint); } - attributes::from_fn_attrs(ccx, &attrs, lldecl); + attributes::from_fn_attrs(ccx, lldecl, instance.def.def_id()); ccx.instances().borrow_mut().insert(instance, lldecl); } diff --git a/src/test/ui/target-feature-wrong.rs b/src/test/ui/target-feature-wrong.rs new file mode 100644 index 00000000000..df24035e10b --- /dev/null +++ b/src/test/ui/target-feature-wrong.rs @@ -0,0 +1,35 @@ +// Copyright 2015 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. + +// ignore-arm +// ignore-aarch64 + +#![feature(target_feature)] + +#[target_feature = "+sse2"] +//~^ WARN: deprecated +#[target_feature(enable = "foo")] +//~^ ERROR: not valid for this target +#[target_feature(bar)] +//~^ ERROR: only accepts sub-keys +#[target_feature(disable = "baz")] +//~^ ERROR: only accepts sub-keys +unsafe fn foo() {} + +#[target_feature(enable = "sse2")] +//~^ ERROR: can only be applied to `unsafe` function +fn bar() {} + +fn main() { + unsafe { + foo(); + bar(); + } +} diff --git a/src/test/ui/target-feature-wrong.stderr b/src/test/ui/target-feature-wrong.stderr new file mode 100644 index 00000000000..0cbfeb3a7b7 --- /dev/null +++ b/src/test/ui/target-feature-wrong.stderr @@ -0,0 +1,32 @@ +warning: #[target_feature = ".."] is deprecated and will eventually be removed, use #[target_feature(enable = "..")] instead + --> $DIR/target-feature-wrong.rs:16:1 + | +16 | #[target_feature = "+sse2"] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: the feature named `foo` is not valid for this target + --> $DIR/target-feature-wrong.rs:18:18 + | +18 | #[target_feature(enable = "foo")] + | ^^^^^^^^^^^^^^ + +error: #[target_feature(..)] only accepts sub-keys of `enable` currently + --> $DIR/target-feature-wrong.rs:20:18 + | +20 | #[target_feature(bar)] + | ^^^ + +error: #[target_feature(..)] only accepts sub-keys of `enable` currently + --> $DIR/target-feature-wrong.rs:22:18 + | +22 | #[target_feature(disable = "baz")] + | ^^^^^^^^^^^^^^^ + +error: #[target_feature(..)] can only be applied to `unsafe` function + --> $DIR/target-feature-wrong.rs:26:1 + | +26 | #[target_feature(enable = "sse2")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 4 previous errors +