@@ -2,14 +2,18 @@ use crate::back::write::create_informational_target_machine;
2
2
use crate :: { llvm, llvm_util} ;
3
3
use libc:: c_int;
4
4
use 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
+ } ;
6
8
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
9
+ use rustc_data_structures:: small_c_str:: SmallCStr ;
7
10
use rustc_fs_util:: path_to_c_string;
8
11
use rustc_middle:: bug;
9
12
use rustc_session:: config:: PrintRequest ;
10
13
use rustc_session:: Session ;
11
14
use rustc_span:: symbol:: Symbol ;
12
15
use rustc_target:: spec:: { MergeFunctions , PanicStrategy } ;
16
+ use smallvec:: { smallvec, SmallVec } ;
13
17
use std:: ffi:: { CStr , CString } ;
14
18
use tracing:: debug;
15
19
@@ -155,45 +159,46 @@ pub fn time_trace_profiler_finish(file_name: &Path) {
155
159
}
156
160
}
157
161
158
- // WARNING: the features after applying `to_llvm_feature ` must be known
162
+ // WARNING: the features after applying `to_llvm_features ` must be known
159
163
// to LLVM or the feature detection code will walk past the end of the feature
160
164
// array, leading to crashes.
165
+ //
161
166
// To find a list of LLVM's names, check llvm-project/llvm/include/llvm/Support/*TargetParser.def
162
167
// where the * matches the architecture's name
163
168
// Beware to not use the llvm github project for this, but check the git submodule
164
169
// found in src/llvm-project
165
170
// Though note that Rust can also be build with an external precompiled version of LLVM
166
171
// which might lead to failures if the oldest tested / supported LLVM version
167
172
// 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 ] > {
169
174
let arch = if sess. target . arch == "x86_64" { "x86" } else { & * sess. target . arch } ;
170
175
match ( arch, s) {
171
176
( "x86" , "sse4.2" ) => {
172
177
if get_version ( ) >= ( 14 , 0 , 0 ) {
173
- vec ! [ "sse4.2" , "crc32" ]
178
+ smallvec ! [ "sse4.2" , "crc32" ]
174
179
} else {
175
- vec ! [ "sse4.2" ]
180
+ smallvec ! [ "sse4.2" ]
176
181
}
177
182
}
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] ,
197
202
}
198
203
}
199
204
@@ -207,7 +212,6 @@ pub fn check_tied_features(
207
212
// Tied features must be set to the same value, or not set at all
208
213
let mut tied_iter = tied. iter ( ) ;
209
214
let enabled = features. get ( tied_iter. next ( ) . unwrap ( ) ) ;
210
-
211
215
if tied_iter. any ( |f| enabled != features. get ( f) ) {
212
216
return Some ( tied) ;
213
217
}
@@ -221,15 +225,11 @@ pub fn target_features(sess: &Session) -> Vec<Symbol> {
221
225
supported_target_features ( sess)
222
226
. iter ( )
223
227
. 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 }
229
229
} )
230
230
. 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) ;
233
233
if unsafe { llvm:: LLVMRustHasFeature ( target_machine, cstr. as_ptr ( ) ) } {
234
234
return true ;
235
235
}
@@ -302,9 +302,9 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine) {
302
302
let mut rustc_target_features = supported_target_features ( sess)
303
303
. iter ( )
304
304
. filter_map ( |( feature, _gate) | {
305
- for llvm_feature in to_llvm_feature ( sess, * feature) {
305
+ for llvm_feature in to_llvm_features ( sess, * feature) {
306
306
// 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 (
308
308
|index| {
309
309
let ( _f, desc) = target_features. remove ( index) ;
310
310
( * feature, desc)
@@ -374,14 +374,7 @@ pub fn target_cpu(sess: &Session) -> &str {
374
374
375
375
/// The list of LLVM features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
376
376
/// `--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 > {
385
378
// Features that come earlier are overriden by conflicting features later in the string.
386
379
// Typically we'll want more explicit settings to override the implicit ones, so:
387
380
//
@@ -427,42 +420,108 @@ pub fn llvm_global_features(sess: &Session) -> Vec<String> {
427
420
Some ( _) | None => { }
428
421
} ;
429
422
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
+ }
432
492
}
433
493
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
444
500
// passing requests down to LLVM. This means that all in-language
445
501
// features also work on the command line instead of having two
446
502
// 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
+ }
452
510
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 ;
463
523
}
464
- features. extend ( feats. iter ( ) . flat_map ( & filter) ) ;
465
- features
524
+ Some ( feature)
466
525
}
467
526
468
527
pub fn tune_cpu ( sess : & Session ) -> Option < & str > {
0 commit comments