Skip to content

Commit b762c2d

Browse files
committed
Auto merge of #47223 - alexcrichton:new-target-feature, r=eddyb
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
2 parents 80e2e67 + 0ecaa67 commit b762c2d

File tree

14 files changed

+262
-61
lines changed

14 files changed

+262
-61
lines changed

src/librustc/dep_graph/dep_node.rs

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

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

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

src/librustc/hir/check_attr.rs

+38-28
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@
1414
//! conflicts between multiple such attributes attached to the same
1515
//! item.
1616
17-
use session::Session;
17+
use ty::TyCtxt;
1818

19-
use syntax::ast;
20-
use syntax::visit;
21-
use syntax::visit::Visitor;
19+
use hir;
20+
use hir::intravisit::{self, Visitor, NestedVisitorMap};
2221

2322
#[derive(Copy, Clone, PartialEq)]
2423
enum Target {
@@ -30,45 +29,51 @@ enum Target {
3029
}
3130

3231
impl Target {
33-
fn from_item(item: &ast::Item) -> Target {
32+
fn from_item(item: &hir::Item) -> Target {
3433
match item.node {
35-
ast::ItemKind::Fn(..) => Target::Fn,
36-
ast::ItemKind::Struct(..) => Target::Struct,
37-
ast::ItemKind::Union(..) => Target::Union,
38-
ast::ItemKind::Enum(..) => Target::Enum,
34+
hir::ItemFn(..) => Target::Fn,
35+
hir::ItemStruct(..) => Target::Struct,
36+
hir::ItemUnion(..) => Target::Union,
37+
hir::ItemEnum(..) => Target::Enum,
3938
_ => Target::Other,
4039
}
4140
}
4241
}
4342

44-
struct CheckAttrVisitor<'a> {
45-
sess: &'a Session,
43+
struct CheckAttrVisitor<'a, 'tcx: 'a> {
44+
tcx: TyCtxt<'a, 'tcx, 'tcx>,
4645
}
4746

48-
impl<'a> CheckAttrVisitor<'a> {
47+
impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> {
4948
/// Check any attribute.
50-
fn check_attributes(&self, item: &ast::Item, target: Target) {
49+
fn check_attributes(&self, item: &hir::Item, target: Target) {
50+
self.tcx.target_features_enabled(self.tcx.hir.local_def_id(item.id));
51+
5152
for attr in &item.attrs {
5253
if let Some(name) = attr.name() {
5354
if name == "inline" {
5455
self.check_inline(attr, item, target)
5556
}
5657
}
5758
}
59+
5860
self.check_repr(item, target);
5961
}
6062

6163
/// Check if an `#[inline]` is applied to a function.
62-
fn check_inline(&self, attr: &ast::Attribute, item: &ast::Item, target: Target) {
64+
fn check_inline(&self, attr: &hir::Attribute, item: &hir::Item, target: Target) {
6365
if target != Target::Fn {
64-
struct_span_err!(self.sess, attr.span, E0518, "attribute should be applied to function")
66+
struct_span_err!(self.tcx.sess,
67+
attr.span,
68+
E0518,
69+
"attribute should be applied to function")
6570
.span_label(item.span, "not a function")
6671
.emit();
6772
}
6873
}
6974

7075
/// Check if the `#[repr]` attributes on `item` are valid.
71-
fn check_repr(&self, item: &ast::Item, target: Target) {
76+
fn check_repr(&self, item: &hir::Item, target: Target) {
7277
// Extract the names of all repr hints, e.g., [foo, bar, align] for:
7378
// ```
7479
// #[repr(foo)]
@@ -144,7 +149,7 @@ impl<'a> CheckAttrVisitor<'a> {
144149
}
145150
_ => continue,
146151
};
147-
struct_span_err!(self.sess, hint.span, E0517,
152+
struct_span_err!(self.tcx.sess, hint.span, E0517,
148153
"attribute should be applied to {}", allowed_targets)
149154
.span_label(item.span, format!("not {} {}", article, allowed_targets))
150155
.emit();
@@ -154,32 +159,37 @@ impl<'a> CheckAttrVisitor<'a> {
154159
if (int_reprs > 1)
155160
|| (is_simd && is_c)
156161
|| (int_reprs == 1 && is_c && is_c_like_enum(item)) {
157-
// Just point at all repr hints. This is not ideal, but tracking precisely which ones
158-
// are at fault is a huge hassle.
162+
// Just point at all repr hints. This is not ideal, but tracking
163+
// precisely which ones are at fault is a huge hassle.
159164
let spans: Vec<_> = hints.iter().map(|hint| hint.span).collect();
160-
span_warn!(self.sess, spans, E0566,
165+
span_warn!(self.tcx.sess, spans, E0566,
161166
"conflicting representation hints");
162167
}
163168
}
164169
}
165170

166-
impl<'a> Visitor<'a> for CheckAttrVisitor<'a> {
167-
fn visit_item(&mut self, item: &'a ast::Item) {
171+
impl<'a, 'tcx> Visitor<'tcx> for CheckAttrVisitor<'a, 'tcx> {
172+
fn nested_visit_map<'this>(&'this mut self) -> NestedVisitorMap<'this, 'tcx> {
173+
NestedVisitorMap::None
174+
}
175+
176+
fn visit_item(&mut self, item: &'tcx hir::Item) {
168177
let target = Target::from_item(item);
169178
self.check_attributes(item, target);
170-
visit::walk_item(self, item);
179+
intravisit::walk_item(self, item);
171180
}
172181
}
173182

174-
pub fn check_crate(sess: &Session, krate: &ast::Crate) {
175-
visit::walk_crate(&mut CheckAttrVisitor { sess: sess }, krate);
183+
pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
184+
let mut checker = CheckAttrVisitor { tcx };
185+
tcx.hir.krate().visit_all_item_likes(&mut checker.as_deep_visitor());
176186
}
177187

178-
fn is_c_like_enum(item: &ast::Item) -> bool {
179-
if let ast::ItemKind::Enum(ref def, _) = item.node {
188+
fn is_c_like_enum(item: &hir::Item) -> bool {
189+
if let hir::ItemEnum(ref def, _) = item.node {
180190
for variant in &def.variants {
181191
match variant.node.data {
182-
ast::VariantData::Unit(_) => { /* continue */ }
192+
hir::VariantData::Unit(_) => { /* continue */ }
183193
_ => { return false; }
184194
}
185195
}

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
@@ -360,6 +360,11 @@ define_maps! { <'tcx>
360360

361361
[] fn substitute_normalize_and_test_predicates:
362362
substitute_normalize_and_test_predicates_node((DefId, &'tcx Substs<'tcx>)) -> bool,
363+
364+
[] fn target_features_whitelist:
365+
target_features_whitelist_node(CrateNum) -> Rc<FxHashSet<String>>,
366+
[] fn target_features_enabled: TargetFeaturesEnabled(DefId) -> Rc<Vec<String>>,
367+
363368
}
364369

365370
//////////////////////////////////////////////////////////////////////
@@ -505,3 +510,7 @@ fn substitute_normalize_and_test_predicates_node<'tcx>(key: (DefId, &'tcx Substs
505510
-> DepConstructor<'tcx> {
506511
DepConstructor::SubstituteNormalizeAndTestPredicates { key }
507512
}
513+
514+
fn target_features_whitelist_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> {
515+
DepConstructor::TargetFeaturesWhitelist
516+
}

src/librustc/ty/maps/plumbing.rs

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

922925
true

src/librustc_driver/driver.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -210,10 +210,6 @@ pub fn compile_input(sess: &Session,
210210
Ok(()));
211211
}
212212

213-
time(sess.time_passes(), "attribute checking", || {
214-
hir::check_attr::check_crate(sess, &expanded_crate);
215-
});
216-
217213
let opt_crate = if control.keep_ast {
218214
Some(&expanded_crate)
219215
} else {
@@ -1038,6 +1034,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(control: &CompileController,
10381034
// tcx available.
10391035
rustc_incremental::dep_graph_tcx_init(tcx);
10401036

1037+
time(sess.time_passes(), "attribute checking", || {
1038+
hir::check_attr::check_crate(tcx)
1039+
});
1040+
10411041
time(time_passes,
10421042
"stability checking",
10431043
|| stability::check_unstable_api_usage(tcx));

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) {

0 commit comments

Comments
 (0)