1010//! Set and unset common attributes on LLVM values.
1111
1212use std:: ffi:: { CStr , CString } ;
13+ use std:: rc:: Rc ;
1314
15+ use rustc:: hir:: Unsafety ;
16+ use rustc:: hir:: def_id:: { DefId , LOCAL_CRATE } ;
1417use rustc:: session:: config:: Sanitizer ;
18+ use rustc:: ty:: TyCtxt ;
19+ use rustc:: ty:: maps:: Providers ;
20+ use rustc_data_structures:: fx:: FxHashSet ;
1521
1622use llvm:: { self , Attribute , ValueRef } ;
1723use llvm:: AttributePlace :: Function ;
24+ use llvm_util;
1825pub use syntax:: attr:: { self , InlineAttr } ;
1926use syntax:: ast;
2027use 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
134136fn 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+ }
0 commit comments