@@ -2,14 +2,18 @@ use crate::back::write::create_informational_target_machine;
22use crate :: { llvm, llvm_util} ;
33use libc:: c_int;
44use libloading:: Library ;
5- use rustc_codegen_ssa:: target_features:: { supported_target_features, tied_target_features} ;
5+ use rustc_codegen_ssa:: target_features:: {
6+ supported_target_features, tied_target_features, RUSTC_SPECIFIC_FEATURES ,
7+ } ;
68use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
9+ use rustc_data_structures:: small_c_str:: SmallCStr ;
710use rustc_fs_util:: path_to_c_string;
811use rustc_middle:: bug;
912use rustc_session:: config:: PrintRequest ;
1013use rustc_session:: Session ;
1114use rustc_span:: symbol:: Symbol ;
1215use rustc_target:: spec:: { MergeFunctions , PanicStrategy } ;
16+ use smallvec:: { smallvec, SmallVec } ;
1317use std:: ffi:: { CStr , CString } ;
1418use tracing:: debug;
1519
@@ -155,45 +159,46 @@ pub fn time_trace_profiler_finish(file_name: &Path) {
155159 }
156160}
157161
158- // WARNING: the features after applying `to_llvm_feature ` must be known
162+ // WARNING: the features after applying `to_llvm_features ` must be known
159163// to LLVM or the feature detection code will walk past the end of the feature
160164// array, leading to crashes.
165+ //
161166// To find a list of LLVM's names, check llvm-project/llvm/include/llvm/Support/*TargetParser.def
162167// where the * matches the architecture's name
163168// Beware to not use the llvm github project for this, but check the git submodule
164169// found in src/llvm-project
165170// Though note that Rust can also be build with an external precompiled version of LLVM
166171// which might lead to failures if the oldest tested / supported LLVM version
167172// doesn't yet support the relevant intrinsics
168- pub fn to_llvm_feature < ' a > ( sess : & Session , s : & ' a str ) -> Vec < & ' a str > {
173+ pub fn to_llvm_features < ' a > ( sess : & Session , s : & ' a str ) -> SmallVec < [ & ' a str ; 2 ] > {
169174 let arch = if sess. target . arch == "x86_64" { "x86" } else { & * sess. target . arch } ;
170175 match ( arch, s) {
171176 ( "x86" , "sse4.2" ) => {
172177 if get_version ( ) >= ( 14 , 0 , 0 ) {
173- vec ! [ "sse4.2" , "crc32" ]
178+ smallvec ! [ "sse4.2" , "crc32" ]
174179 } else {
175- vec ! [ "sse4.2" ]
180+ smallvec ! [ "sse4.2" ]
176181 }
177182 }
178- ( "x86" , "pclmulqdq" ) => vec ! [ "pclmul" ] ,
179- ( "x86" , "rdrand" ) => vec ! [ "rdrnd" ] ,
180- ( "x86" , "bmi1" ) => vec ! [ "bmi" ] ,
181- ( "x86" , "cmpxchg16b" ) => vec ! [ "cx16" ] ,
182- ( "x86" , "avx512vaes" ) => vec ! [ "vaes" ] ,
183- ( "x86" , "avx512gfni" ) => vec ! [ "gfni" ] ,
184- ( "x86" , "avx512vpclmulqdq" ) => vec ! [ "vpclmulqdq" ] ,
185- ( "aarch64" , "fp" ) => vec ! [ "fp-armv8" ] ,
186- ( "aarch64" , "fp16" ) => vec ! [ "fullfp16" ] ,
187- ( "aarch64" , "fhm" ) => vec ! [ "fp16fml" ] ,
188- ( "aarch64" , "rcpc2" ) => vec ! [ "rcpc-immo" ] ,
189- ( "aarch64" , "dpb" ) => vec ! [ "ccpp" ] ,
190- ( "aarch64" , "dpb2" ) => vec ! [ "ccdp" ] ,
191- ( "aarch64" , "frintts" ) => vec ! [ "fptoint" ] ,
192- ( "aarch64" , "fcma" ) => vec ! [ "complxnum" ] ,
193- ( "aarch64" , "pmuv3" ) => vec ! [ "perfmon" ] ,
194- ( "aarch64" , "paca" ) => vec ! [ "pauth" ] ,
195- ( "aarch64" , "pacg" ) => vec ! [ "pauth" ] ,
196- ( _, s) => vec ! [ s] ,
183+ ( "x86" , "pclmulqdq" ) => smallvec ! [ "pclmul" ] ,
184+ ( "x86" , "rdrand" ) => smallvec ! [ "rdrnd" ] ,
185+ ( "x86" , "bmi1" ) => smallvec ! [ "bmi" ] ,
186+ ( "x86" , "cmpxchg16b" ) => smallvec ! [ "cx16" ] ,
187+ ( "x86" , "avx512vaes" ) => smallvec ! [ "vaes" ] ,
188+ ( "x86" , "avx512gfni" ) => smallvec ! [ "gfni" ] ,
189+ ( "x86" , "avx512vpclmulqdq" ) => smallvec ! [ "vpclmulqdq" ] ,
190+ ( "aarch64" , "fp" ) => smallvec ! [ "fp-armv8" ] ,
191+ ( "aarch64" , "fp16" ) => smallvec ! [ "fullfp16" ] ,
192+ ( "aarch64" , "fhm" ) => smallvec ! [ "fp16fml" ] ,
193+ ( "aarch64" , "rcpc2" ) => smallvec ! [ "rcpc-immo" ] ,
194+ ( "aarch64" , "dpb" ) => smallvec ! [ "ccpp" ] ,
195+ ( "aarch64" , "dpb2" ) => smallvec ! [ "ccdp" ] ,
196+ ( "aarch64" , "frintts" ) => smallvec ! [ "fptoint" ] ,
197+ ( "aarch64" , "fcma" ) => smallvec ! [ "complxnum" ] ,
198+ ( "aarch64" , "pmuv3" ) => smallvec ! [ "perfmon" ] ,
199+ ( "aarch64" , "paca" ) => smallvec ! [ "pauth" ] ,
200+ ( "aarch64" , "pacg" ) => smallvec ! [ "pauth" ] ,
201+ ( _, s) => smallvec ! [ s] ,
197202 }
198203}
199204
@@ -207,7 +212,6 @@ pub fn check_tied_features(
207212 // Tied features must be set to the same value, or not set at all
208213 let mut tied_iter = tied. iter ( ) ;
209214 let enabled = features. get ( tied_iter. next ( ) . unwrap ( ) ) ;
210-
211215 if tied_iter. any ( |f| enabled != features. get ( f) ) {
212216 return Some ( tied) ;
213217 }
@@ -221,15 +225,11 @@ pub fn target_features(sess: &Session) -> Vec<Symbol> {
221225 supported_target_features ( sess)
222226 . iter ( )
223227 . filter_map ( |& ( feature, gate) | {
224- if sess. is_nightly_build ( ) || gate. is_none ( ) {
225- Some ( feature)
226- } else {
227- None
228- }
228+ if sess. is_nightly_build ( ) || gate. is_none ( ) { Some ( feature) } else { None }
229229 } )
230230 . filter ( |feature| {
231- for llvm_feature in to_llvm_feature ( sess, feature) {
232- let cstr = CString :: new ( llvm_feature) . unwrap ( ) ;
231+ for llvm_feature in to_llvm_features ( sess, feature) {
232+ let cstr = SmallCStr :: new ( llvm_feature) ;
233233 if unsafe { llvm:: LLVMRustHasFeature ( target_machine, cstr. as_ptr ( ) ) } {
234234 return true ;
235235 }
@@ -302,9 +302,9 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) {
302302 let mut rustc_target_features = supported_target_features ( sess)
303303 . iter ( )
304304 . filter_map ( |( feature, _gate) | {
305- for llvm_feature in to_llvm_feature ( sess, * feature) {
305+ for llvm_feature in to_llvm_features ( sess, * feature) {
306306 // LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings.
307- match target_features. binary_search_by_key ( & llvm_feature, |( f, _d) | ( * f ) ) . ok ( ) . map (
307+ match target_features. binary_search_by_key ( & llvm_feature, |( f, _d) | f ) . ok ( ) . map (
308308 |index| {
309309 let ( _f, desc) = target_features. remove ( index) ;
310310 ( * feature, desc)
@@ -374,14 +374,7 @@ pub fn target_cpu(sess: &Session) -> &str {
374374
375375/// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
376376/// `--target` and similar).
377- // FIXME(nagisa): Cache the output of this somehow? Maybe make this a query? We're calling this
378- // for every function that has `#[target_feature]` on it. The global features won't change between
379- // the functions; only crates, maybe…
380- pub fn llvm_global_features ( sess : & Session ) -> Vec < String > {
381- // FIXME(nagisa): this should definitely be available more centrally and to other codegen backends.
382- /// These features control behaviour of rustc rather than llvm.
383- const RUSTC_SPECIFIC_FEATURES : & [ & str ] = & [ "crt-static" ] ;
384-
377+ pub ( crate ) fn global_llvm_features ( sess : & Session , diagnostics : bool ) -> Vec < String > {
385378 // Features that come earlier are overriden by conflicting features later in the string.
386379 // Typically we'll want more explicit settings to override the implicit ones, so:
387380 //
@@ -427,42 +420,108 @@ pub fn llvm_global_features(sess: &Session) -> Vec<String> {
427420 Some ( _) | None => { }
428421 } ;
429422
430- fn strip ( s : & str ) -> & str {
431- s. strip_prefix ( & [ '+' , '-' ] ) . unwrap_or ( s)
423+ // Features implied by an implicit or explicit `--target`.
424+ features. extend (
425+ sess. target
426+ . features
427+ . split ( ',' )
428+ . filter ( |v| !v. is_empty ( ) && backend_feature_name ( v) . is_some ( ) )
429+ . map ( String :: from) ,
430+ ) ;
431+
432+ // -Ctarget-features
433+ let supported_features = supported_target_features ( sess) ;
434+ let feats = sess
435+ . opts
436+ . cg
437+ . target_feature
438+ . split ( ',' )
439+ . filter_map ( |s| {
440+ let enable_disable = match s. chars ( ) . next ( ) {
441+ None => return None ,
442+ Some ( c @ '+' | c @ '-' ) => c,
443+ Some ( _) => {
444+ if diagnostics {
445+ let mut diag = sess. struct_warn ( & format ! (
446+ "unknown feature specified for `-Ctarget-feature`: `{}`" ,
447+ s
448+ ) ) ;
449+ diag. note ( "features must begin with a `+` to enable or `-` to disable it" ) ;
450+ diag. emit ( ) ;
451+ }
452+ return None ;
453+ }
454+ } ;
455+
456+ let feature = backend_feature_name ( s) ?;
457+ // Warn against use of LLVM specific feature names on the CLI.
458+ if diagnostics && !supported_features. iter ( ) . any ( |& ( v, _) | v == feature) {
459+ let rust_feature = supported_features. iter ( ) . find_map ( |& ( rust_feature, _) | {
460+ let llvm_features = to_llvm_features ( sess, rust_feature) ;
461+ if llvm_features. contains ( & feature) && !llvm_features. contains ( & rust_feature) {
462+ Some ( rust_feature)
463+ } else {
464+ None
465+ }
466+ } ) ;
467+ let mut diag = sess. struct_warn ( & format ! (
468+ "unknown feature specified for `-Ctarget-feature`: `{}`" ,
469+ feature
470+ ) ) ;
471+ diag. note ( "it is still passed through to the codegen backend" ) ;
472+ if let Some ( rust_feature) = rust_feature {
473+ diag. help ( & format ! ( "you might have meant: `{}`" , rust_feature) ) ;
474+ } else {
475+ diag. note ( "consider filing a feature request" ) ;
476+ }
477+ diag. emit ( ) ;
478+ }
479+ Some ( ( enable_disable, feature) )
480+ } )
481+ . collect :: < SmallVec < [ ( char , & str ) ; 8 ] > > ( ) ;
482+
483+ if diagnostics {
484+ // FIXME(nagisa): figure out how to not allocate a full hashset here.
485+ let featmap = feats. iter ( ) . map ( |& ( flag, feat) | ( feat, flag == '+' ) ) . collect ( ) ;
486+ if let Some ( f) = check_tied_features ( sess, & featmap) {
487+ sess. err ( & format ! (
488+ "target features {} must all be enabled or disabled together" ,
489+ f. join( ", " )
490+ ) ) ;
491+ }
432492 }
433493
434- let filter = |s : & str | {
435- // features must start with a `+` or `-`.
436- let feature = match s. strip_prefix ( & [ '+' , '-' ] [ ..] ) {
437- None => return vec ! [ ] ,
438- // Rustc-specific feature requests like `+crt-static` or `-crt-static`
439- // are not passed down to LLVM.
440- Some ( feature) if RUSTC_SPECIFIC_FEATURES . contains ( & feature) => return vec ! [ ] ,
441- Some ( feature) => feature,
442- } ;
443- // ... otherwise though we run through `to_llvm_feature` when
494+ features. extend ( feats. into_iter ( ) . flat_map ( |( enable_disable, feature) | {
495+ // rustc-specific features do not get passed down to LLVM…
496+ if RUSTC_SPECIFIC_FEATURES . contains ( & feature) {
497+ return SmallVec :: < [ _ ; 2 ] > :: new ( ) ;
498+ }
499+ // ... otherwise though we run through `to_llvm_feature when
444500 // passing requests down to LLVM. This means that all in-language
445501 // features also work on the command line instead of having two
446502 // different names when the LLVM name and the Rust name differ.
447- to_llvm_feature ( sess, feature) . iter ( ) . map ( |f| format ! ( "{}{}" , & s[ ..1 ] , f) ) . collect ( )
448- } ;
449-
450- // Features implied by an implicit or explicit `--target`.
451- features. extend ( sess. target . features . split ( ',' ) . flat_map ( & filter) ) ;
503+ to_llvm_features ( sess, feature)
504+ . into_iter ( )
505+ . map ( |f| format ! ( "{}{}" , enable_disable, f) )
506+ . collect ( )
507+ } ) ) ;
508+ features
509+ }
452510
453- // -Ctarget-features
454- let feats: Vec < & str > = sess. opts . cg . target_feature . split ( ',' ) . collect ( ) ;
455- // LLVM enables based on the last occurence of a feature
456- if let Some ( f) =
457- check_tied_features ( sess, & feats. iter ( ) . map ( |f| ( strip ( f) , !f. starts_with ( "-" ) ) ) . collect ( ) )
458- {
459- sess. err ( & format ! (
460- "target features {} must all be enabled or disabled together" ,
461- f. join( ", " )
462- ) ) ;
511+ /// Returns a feature name for the given `+feature` or `-feature` string.
512+ ///
513+ /// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].)
514+ fn backend_feature_name ( s : & str ) -> Option < & str > {
515+ // features must start with a `+` or `-`.
516+ let feature = s. strip_prefix ( & [ '+' , '-' ] [ ..] ) . unwrap_or_else ( || {
517+ bug ! ( "target feature `{}` must begin with a `+` or `-`" , s) ;
518+ } ) ;
519+ // Rustc-specific feature requests like `+crt-static` or `-crt-static`
520+ // are not passed down to LLVM.
521+ if RUSTC_SPECIFIC_FEATURES . contains ( & feature) {
522+ return None ;
463523 }
464- features. extend ( feats. iter ( ) . flat_map ( & filter) ) ;
465- features
524+ Some ( feature)
466525}
467526
468527pub fn tune_cpu ( sess : & Session ) -> Option < & str > {
0 commit comments