Skip to content

Commit 5f006ce

Browse files
committed
rustc: Tweak #[target_feature] syntax
This is an implementation of the `#[target_feature]` syntax-related changes of [RFC 2045][rfc]. Notably two changes have been implemented: * The new syntax is `#[target_feature(enable = "..")]` instead of `#[target_feature = "+.."]`. The `enable` key is necessary instead of the `+` to indicate that a feature is being enabled, and a sub-list is used for possible expansion in the future. Additionally within this syntax the feature names being enabled are now whitelisted against a known set of target feature names that we know about. * The `#[target_feature]` attribute can only be applied to unsafe functions. It was decided in the RFC that invoking an instruction possibly not defined for the current processor is undefined behavior, so to enable this feature for now it requires an `unsafe` intervention. [rfc]: https://github.com/rust-lang/rfcs/blob/master/text/2045-target-feature.md
1 parent e6072a7 commit 5f006ce

File tree

11 files changed

+216
-27
lines changed

11 files changed

+216
-27
lines changed

src/librustc/dep_graph/dep_node.rs

+4
Original file line numberDiff line numberDiff line change
@@ -635,6 +635,10 @@ define_dep_nodes!( <'tcx>
635635
[] Null,
636636

637637
[] SubstituteNormalizeAndTestPredicates { key: (DefId, &'tcx Substs<'tcx>) },
638+
639+
[input] TargetFeaturesWhitelist,
640+
[] TargetFeaturesEnabled(DefId),
641+
638642
);
639643

640644
trait DepNodeParams<'a, 'gcx: 'tcx + 'a, 'tcx: 'a> : fmt::Debug {

src/librustc/ty/maps/config.rs

+6
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,12 @@ impl<'tcx> QueryDescription<'tcx> for queries::substitute_normalize_and_test_pre
631631
}
632632
}
633633

634+
impl<'tcx> QueryDescription<'tcx> for queries::target_features_whitelist<'tcx> {
635+
fn describe(_tcx: TyCtxt, _: CrateNum) -> String {
636+
format!("looking up the whitelist of target features")
637+
}
638+
}
639+
634640
macro_rules! impl_disk_cacheable_query(
635641
($query_name:ident, |$key:tt| $cond:expr) => {
636642
impl<'tcx> QueryDescription<'tcx> for queries::$query_name<'tcx> {

src/librustc/ty/maps/mod.rs

+9
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,11 @@ define_maps! { <'tcx>
363363

364364
[] fn substitute_normalize_and_test_predicates:
365365
substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool,
366+
367+
[] fn target_features_whitelist:
368+
target_features_whitelist_node(CrateNum) -> Rc<FxHashSet<String>>,
369+
[] fn target_features_enabled: TargetFeaturesEnabled(DefId) -> Rc<Vec<String>>,
370+
366371
}
367372

368373
//////////////////////////////////////////////////////////////////////
@@ -508,3 +513,7 @@ fn substitute_normalize_and_test_predicates_node<'tcx>(key: (DefId, &'tcx Substs
508513
-> DepConstructor<'tcx> {
509514
DepConstructor::SubstituteNormalizeAndTestPredicates { key }
510515
}
516+
517+
fn target_features_whitelist_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> {
518+
DepConstructor::TargetFeaturesWhitelist
519+
}

src/librustc/ty/maps/plumbing.rs

+3
Original file line numberDiff line numberDiff line change
@@ -918,6 +918,9 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>,
918918
}
919919
DepKind::IsTranslatedFunction => { force!(is_translated_function, def_id!()); }
920920
DepKind::OutputFilenames => { force!(output_filenames, LOCAL_CRATE); }
921+
922+
DepKind::TargetFeaturesWhitelist => { force!(target_features_whitelist, LOCAL_CRATE); }
923+
DepKind::TargetFeaturesEnabled => { force!(target_features_enabled, def_id!()); }
921924
}
922925

923926
true

src/librustc_trans/attributes.rs

+109-13
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,18 @@
1010
//! Set and unset common attributes on LLVM values.
1111
1212
use std::ffi::{CStr, CString};
13+
use std::rc::Rc;
1314

15+
use rustc::hir::Unsafety;
16+
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
1417
use rustc::session::config::Sanitizer;
18+
use rustc::ty::TyCtxt;
19+
use rustc::ty::maps::Providers;
20+
use rustc_data_structures::fx::FxHashSet;
1521

1622
use llvm::{self, Attribute, ValueRef};
1723
use llvm::AttributePlace::Function;
24+
use llvm_util;
1825
pub use syntax::attr::{self, InlineAttr};
1926
use syntax::ast;
2027
use context::CrateContext;
@@ -94,23 +101,16 @@ pub fn set_probestack(ccx: &CrateContext, llfn: ValueRef) {
94101

95102
/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute])
96103
/// attributes.
97-
pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRef) {
104+
pub fn from_fn_attrs(ccx: &CrateContext, llfn: ValueRef, id: DefId) {
98105
use syntax::attr::*;
99-
inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), attrs));
106+
let attrs = ccx.tcx().get_attrs(id);
107+
inline(llfn, find_inline_attr(Some(ccx.sess().diagnostic()), &attrs));
100108

101109
set_frame_pointer_elimination(ccx, llfn);
102110
set_probestack(ccx, llfn);
103-
let mut target_features = vec![];
104-
for attr in attrs {
105-
if attr.check_name("target_feature") {
106-
if let Some(val) = attr.value_str() {
107-
for feat in val.as_str().split(",").map(|f| f.trim()) {
108-
if !feat.is_empty() && !feat.contains('\0') {
109-
target_features.push(feat.to_string());
110-
}
111-
}
112-
}
113-
} else if attr.check_name("cold") {
111+
112+
for attr in attrs.iter() {
113+
if attr.check_name("cold") {
114114
Attribute::Cold.apply_llfn(Function, llfn);
115115
} else if attr.check_name("naked") {
116116
naked(llfn, true);
@@ -123,6 +123,8 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe
123123
unwind(llfn, false);
124124
}
125125
}
126+
127+
let target_features = ccx.tcx().target_features_enabled(id);
126128
if !target_features.is_empty() {
127129
let val = CString::new(target_features.join(",")).unwrap();
128130
llvm::AddFunctionAttrStringValue(
@@ -134,3 +136,97 @@ pub fn from_fn_attrs(ccx: &CrateContext, attrs: &[ast::Attribute], llfn: ValueRe
134136
fn cstr(s: &'static str) -> &CStr {
135137
CStr::from_bytes_with_nul(s.as_bytes()).expect("null-terminated string")
136138
}
139+
140+
pub fn provide(providers: &mut Providers) {
141+
providers.target_features_whitelist = |tcx, cnum| {
142+
assert_eq!(cnum, LOCAL_CRATE);
143+
Rc::new(llvm_util::target_feature_whitelist(tcx.sess)
144+
.iter()
145+
.map(|c| c.to_str().unwrap().to_string())
146+
.collect())
147+
};
148+
149+
providers.target_features_enabled = |tcx, id| {
150+
let whitelist = tcx.target_features_whitelist(LOCAL_CRATE);
151+
let mut target_features = Vec::new();
152+
for attr in tcx.get_attrs(id).iter() {
153+
if !attr.check_name("target_feature") {
154+
continue
155+
}
156+
if let Some(val) = attr.value_str() {
157+
for feat in val.as_str().split(",").map(|f| f.trim()) {
158+
if !feat.is_empty() && !feat.contains('\0') {
159+
target_features.push(feat.to_string());
160+
}
161+
}
162+
let msg = "#[target_feature = \"..\"] is deprecated and will \
163+
eventually be removed, use \
164+
#[target_feature(enable = \"..\")] instead";
165+
tcx.sess.span_warn(attr.span, &msg);
166+
continue
167+
}
168+
169+
if tcx.fn_sig(id).unsafety() == Unsafety::Normal {
170+
let msg = "#[target_feature(..)] can only be applied to \
171+
`unsafe` function";
172+
tcx.sess.span_err(attr.span, msg);
173+
}
174+
from_target_feature(tcx, attr, &whitelist, &mut target_features);
175+
}
176+
Rc::new(target_features)
177+
};
178+
}
179+
180+
fn from_target_feature(
181+
tcx: TyCtxt,
182+
attr: &ast::Attribute,
183+
whitelist: &FxHashSet<String>,
184+
target_features: &mut Vec<String>,
185+
) {
186+
let list = match attr.meta_item_list() {
187+
Some(list) => list,
188+
None => {
189+
let msg = "#[target_feature] attribute must be of the form \
190+
#[target_feature(..)]";
191+
tcx.sess.span_err(attr.span, &msg);
192+
return
193+
}
194+
};
195+
196+
for item in list {
197+
if !item.check_name("enable") {
198+
let msg = "#[target_feature(..)] only accepts sub-keys of `enable` \
199+
currently";
200+
tcx.sess.span_err(item.span, &msg);
201+
continue
202+
}
203+
let value = match item.value_str() {
204+
Some(list) => list,
205+
None => {
206+
let msg = "#[target_feature] attribute must be of the form \
207+
#[target_feature(enable = \"..\")]";
208+
tcx.sess.span_err(item.span, &msg);
209+
continue
210+
}
211+
};
212+
let value = value.as_str();
213+
for feature in value.split(',') {
214+
if whitelist.contains(feature) {
215+
target_features.push(format!("+{}", feature));
216+
continue
217+
}
218+
219+
let msg = format!("the feature named `{}` is not valid for \
220+
this target", feature);
221+
let mut err = tcx.sess.struct_span_err(item.span, &msg);
222+
223+
if feature.starts_with("+") {
224+
let valid = whitelist.contains(&feature[1..]);
225+
if valid {
226+
err.help("consider removing the leading `+` in the feature name");
227+
}
228+
}
229+
err.emit();
230+
}
231+
}
232+
}

src/librustc_trans/callee.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,7 @@ pub fn get_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
9999
if instance.def.is_inline(tcx) {
100100
attributes::inline(llfn, attributes::InlineAttr::Hint);
101101
}
102-
let attrs = instance.def.attrs(ccx.tcx());
103-
attributes::from_fn_attrs(ccx, &attrs, llfn);
102+
attributes::from_fn_attrs(ccx, llfn, instance.def.def_id());
104103

105104
let instance_def_id = instance.def_id();
106105

src/librustc_trans/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ impl rustc_trans_utils::trans_crate::TransCrate for LlvmTransCrate {
167167
back::symbol_names::provide(providers);
168168
back::symbol_export::provide(providers);
169169
base::provide(providers);
170+
attributes::provide(providers);
170171
}
171172

172173
fn provide_extern(providers: &mut ty::maps::Providers) {

src/librustc_trans/llvm_util.rs

+15-11
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ use back::write::create_target_machine;
1313
use llvm;
1414
use rustc::session::Session;
1515
use rustc::session::config::PrintRequest;
16-
use libc::{c_int, c_char};
17-
use std::ffi::CString;
16+
use libc::c_int;
17+
use std::ffi::{CStr, CString};
1818

1919
use std::sync::atomic::{AtomicBool, Ordering};
2020
use std::sync::Once;
@@ -97,8 +97,18 @@ const POWERPC_WHITELIST: &'static [&'static str] = &["altivec\0",
9797
const MIPS_WHITELIST: &'static [&'static str] = &["msa\0"];
9898

9999
pub fn target_features(sess: &Session) -> Vec<Symbol> {
100+
let whitelist = target_feature_whitelist(sess);
100101
let target_machine = create_target_machine(sess);
102+
let mut features = Vec::new();
103+
for feat in whitelist {
104+
if unsafe { llvm::LLVMRustHasFeature(target_machine, feat.as_ptr()) } {
105+
features.push(Symbol::intern(feat.to_str().unwrap()));
106+
}
107+
}
108+
features
109+
}
101110

111+
pub fn target_feature_whitelist(sess: &Session) -> Vec<&CStr> {
102112
let whitelist = match &*sess.target.target.arch {
103113
"arm" => ARM_WHITELIST,
104114
"aarch64" => AARCH64_WHITELIST,
@@ -108,15 +118,9 @@ pub fn target_features(sess: &Session) -> Vec<Symbol> {
108118
"powerpc" | "powerpc64" => POWERPC_WHITELIST,
109119
_ => &[],
110120
};
111-
112-
let mut features = Vec::new();
113-
for feat in whitelist {
114-
assert_eq!(feat.chars().last(), Some('\0'));
115-
if unsafe { llvm::LLVMRustHasFeature(target_machine, feat.as_ptr() as *const c_char) } {
116-
features.push(Symbol::intern(&feat[..feat.len() - 1]));
117-
}
118-
}
119-
features
121+
whitelist.iter().map(|m| {
122+
CStr::from_bytes_with_nul(m.as_bytes()).unwrap()
123+
}).collect()
120124
}
121125

122126
pub fn print_version() {

src/librustc_trans/trans_item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ fn predefine_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
199199
if instance.def.is_inline(ccx.tcx()) {
200200
attributes::inline(lldecl, attributes::InlineAttr::Hint);
201201
}
202-
attributes::from_fn_attrs(ccx, &attrs, lldecl);
202+
attributes::from_fn_attrs(ccx, lldecl, instance.def.def_id());
203203

204204
ccx.instances().borrow_mut().insert(instance, lldecl);
205205
}

src/test/ui/target-feature-wrong.rs

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// ignore-arm
12+
// ignore-aarch64
13+
14+
#![feature(target_feature)]
15+
16+
#[target_feature = "+sse2"]
17+
//~^ WARN: deprecated
18+
#[target_feature(enable = "foo")]
19+
//~^ ERROR: not valid for this target
20+
#[target_feature(bar)]
21+
//~^ ERROR: only accepts sub-keys
22+
#[target_feature(disable = "baz")]
23+
//~^ ERROR: only accepts sub-keys
24+
unsafe fn foo() {}
25+
26+
#[target_feature(enable = "sse2")]
27+
//~^ ERROR: can only be applied to `unsafe` function
28+
fn bar() {}
29+
30+
fn main() {
31+
unsafe {
32+
foo();
33+
bar();
34+
}
35+
}
+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
warning: #[target_feature = ".."] is deprecated and will eventually be removed, use #[target_feature(enable = "..")] instead
2+
--> $DIR/target-feature-wrong.rs:16:1
3+
|
4+
16 | #[target_feature = "+sse2"]
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: the feature named `foo` is not valid for this target
8+
--> $DIR/target-feature-wrong.rs:18:18
9+
|
10+
18 | #[target_feature(enable = "foo")]
11+
| ^^^^^^^^^^^^^^
12+
13+
error: #[target_feature(..)] only accepts sub-keys of `enable` currently
14+
--> $DIR/target-feature-wrong.rs:20:18
15+
|
16+
20 | #[target_feature(bar)]
17+
| ^^^
18+
19+
error: #[target_feature(..)] only accepts sub-keys of `enable` currently
20+
--> $DIR/target-feature-wrong.rs:22:18
21+
|
22+
22 | #[target_feature(disable = "baz")]
23+
| ^^^^^^^^^^^^^^^
24+
25+
error: #[target_feature(..)] can only be applied to `unsafe` function
26+
--> $DIR/target-feature-wrong.rs:26:1
27+
|
28+
26 | #[target_feature(enable = "sse2")]
29+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30+
31+
error: aborting due to 4 previous errors
32+

0 commit comments

Comments
 (0)