@@ -21,8 +21,8 @@ use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATU
21
21
22
22
use crate :: back:: write:: create_informational_target_machine;
23
23
use crate :: errors:: {
24
- FixedX18InvalidArch , ForbiddenCTargetFeature , InvalidTargetFeaturePrefix , PossibleFeature ,
25
- UnknownCTargetFeature , UnknownCTargetFeaturePrefix , UnstableCTargetFeature ,
24
+ FixedX18InvalidArch , ForbiddenCTargetFeature , PossibleFeature , UnknownCTargetFeature ,
25
+ UnknownCTargetFeaturePrefix , UnstableCTargetFeature ,
26
26
} ;
27
27
use crate :: llvm;
28
28
@@ -348,15 +348,28 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol>
348
348
{
349
349
if enabled {
350
350
// Also add all transitively implied features.
351
- features. extend ( sess. target . implied_target_features ( std:: iter:: once ( feature) ) ) ;
351
+
352
+ // We don't care about the order in `features` since the only thing we use it for is the
353
+ // `features.contains` below.
354
+ #[ allow( rustc:: potential_query_instability) ]
355
+ features. extend (
356
+ sess. target
357
+ . implied_target_features ( std:: iter:: once ( feature. as_str ( ) ) )
358
+ . iter ( )
359
+ . map ( |s| Symbol :: intern ( s) ) ,
360
+ ) ;
352
361
} else {
353
362
// Remove transitively reverse-implied features.
354
363
355
364
// We don't care about the order in `features` since the only thing we use it for is the
356
365
// `features.contains` below.
357
366
#[ allow( rustc:: potential_query_instability) ]
358
367
features. retain ( |f| {
359
- if sess. target . implied_target_features ( std:: iter:: once ( * f) ) . contains ( & feature) {
368
+ if sess
369
+ . target
370
+ . implied_target_features ( std:: iter:: once ( f. as_str ( ) ) )
371
+ . contains ( & feature. as_str ( ) )
372
+ {
360
373
// If `f` if implies `feature`, then `!feature` implies `!f`, so we have to
361
374
// remove `f`. (This is the standard logical contraposition principle.)
362
375
false
@@ -638,7 +651,7 @@ pub(crate) fn global_llvm_features(
638
651
sess. target
639
652
. features
640
653
. split ( ',' )
641
- . filter ( |v| !v. is_empty ( ) && backend_feature_name ( sess , v ) . is_some ( ) )
654
+ . filter ( |v| !v. is_empty ( ) )
642
655
// Drop +v8plus feature introduced in LLVM 20.
643
656
. filter ( |v| * v != "+v8plus" || get_version ( ) >= ( 20 , 0 , 0 ) )
644
657
. map ( String :: from) ,
@@ -651,89 +664,126 @@ pub(crate) fn global_llvm_features(
651
664
// -Ctarget-features
652
665
if !only_base_features {
653
666
let known_features = sess. target . rust_target_features ( ) ;
667
+ // Will only be filled when `diagnostics` is set!
654
668
let mut featsmap = FxHashMap :: default ( ) ;
655
669
656
- // insert implied features
670
+ // Ensure that all ABI-required features are enabled, and the ABI-forbidden ones
671
+ // are disabled.
672
+ let ( abi_enable, abi_disable) = sess. target . abi_required_features ( ) ;
673
+ let abi_enable_set = FxHashSet :: from_iter ( abi_enable. iter ( ) . copied ( ) ) ;
674
+ let abi_disable_set = FxHashSet :: from_iter ( abi_disable. iter ( ) . copied ( ) ) ;
675
+
676
+ // Compute implied features
657
677
let mut all_rust_features = vec ! [ ] ;
658
678
for feature in sess. opts . cg . target_feature . split ( ',' ) {
659
- match feature. strip_prefix ( '+' ) {
660
- Some ( feature) => all_rust_features. extend (
661
- UnordSet :: from (
662
- sess. target
663
- . implied_target_features ( std:: iter:: once ( Symbol :: intern ( feature) ) ) ,
664
- )
665
- . to_sorted_stable_ord ( )
666
- . iter ( )
667
- . map ( |s| format ! ( "+{}" , s. as_str( ) ) ) ,
668
- ) ,
669
- _ => all_rust_features. push ( feature. to_string ( ) ) ,
679
+ if let Some ( feature) = feature. strip_prefix ( '+' ) {
680
+ all_rust_features. extend (
681
+ UnordSet :: from ( sess. target . implied_target_features ( std:: iter:: once ( feature) ) )
682
+ . to_sorted_stable_ord ( )
683
+ . iter ( )
684
+ . map ( |& & s| ( true , s) ) ,
685
+ )
686
+ } else if let Some ( feature) = feature. strip_prefix ( '-' ) {
687
+ // FIXME: Why do we not remove implied features on "-" here?
688
+ // We do the equivalent above in `target_features_cfg`.
689
+ // See <https://github.com/rust-lang/rust/issues/134792>.
690
+ all_rust_features. push ( ( false , feature) ) ;
691
+ } else if !feature. is_empty ( ) {
692
+ if diagnostics {
693
+ sess. dcx ( ) . emit_warn ( UnknownCTargetFeaturePrefix { feature } ) ;
694
+ }
670
695
}
671
696
}
697
+ // Remove features that are meant for rustc, not LLVM.
698
+ all_rust_features. retain ( |( _, feature) | {
699
+ // Retain if it is not a rustc feature
700
+ !RUSTC_SPECIFIC_FEATURES . contains ( feature)
701
+ } ) ;
672
702
673
- let feats = all_rust_features
674
- . iter ( )
675
- . filter_map ( |s| {
676
- let enable_disable = match s. chars ( ) . next ( ) {
677
- None => return None ,
678
- Some ( c @ ( '+' | '-' ) ) => c,
679
- Some ( _) => {
680
- if diagnostics {
681
- sess. dcx ( ) . emit_warn ( UnknownCTargetFeaturePrefix { feature : s } ) ;
682
- }
683
- return None ;
684
- }
685
- } ;
686
-
687
- // Get the backend feature name, if any.
688
- // This excludes rustc-specific features, which do not get passed to LLVM.
689
- let feature = backend_feature_name ( sess, s) ?;
690
- // Warn against use of LLVM specific feature names and unstable features on the CLI.
691
- if diagnostics {
692
- let feature_state = known_features. iter ( ) . find ( |& & ( v, _, _) | v == feature) ;
693
- match feature_state {
694
- None => {
695
- let rust_feature =
696
- known_features. iter ( ) . find_map ( |& ( rust_feature, _, _) | {
697
- let llvm_features = to_llvm_features ( sess, rust_feature) ?;
698
- if llvm_features. contains ( feature)
699
- && !llvm_features. contains ( rust_feature)
700
- {
701
- Some ( rust_feature)
702
- } else {
703
- None
704
- }
705
- } ) ;
706
- let unknown_feature = if let Some ( rust_feature) = rust_feature {
707
- UnknownCTargetFeature {
708
- feature,
709
- rust_feature : PossibleFeature :: Some { rust_feature } ,
710
- }
711
- } else {
712
- UnknownCTargetFeature {
713
- feature,
714
- rust_feature : PossibleFeature :: None ,
703
+ // Check feature validity.
704
+ if diagnostics {
705
+ for & ( enable, feature) in & all_rust_features {
706
+ let feature_state = known_features. iter ( ) . find ( |& & ( v, _, _) | v == feature) ;
707
+ match feature_state {
708
+ None => {
709
+ let rust_feature =
710
+ known_features. iter ( ) . find_map ( |& ( rust_feature, _, _) | {
711
+ let llvm_features = to_llvm_features ( sess, rust_feature) ?;
712
+ if llvm_features. contains ( feature)
713
+ && !llvm_features. contains ( rust_feature)
714
+ {
715
+ Some ( rust_feature)
716
+ } else {
717
+ None
715
718
}
716
- } ;
717
- sess. dcx ( ) . emit_warn ( unknown_feature) ;
718
- }
719
- Some ( ( _, stability, _) ) => {
720
- if let Err ( reason) =
721
- stability. toggle_allowed ( & sess. target , enable_disable == '+' )
722
- {
723
- sess. dcx ( ) . emit_warn ( ForbiddenCTargetFeature { feature, reason } ) ;
724
- } else if stability. requires_nightly ( ) . is_some ( ) {
725
- // An unstable feature. Warn about using it. It makes little sense
726
- // to hard-error here since we just warn about fully unknown
727
- // features above.
728
- sess. dcx ( ) . emit_warn ( UnstableCTargetFeature { feature } ) ;
719
+ } ) ;
720
+ let unknown_feature = if let Some ( rust_feature) = rust_feature {
721
+ UnknownCTargetFeature {
722
+ feature,
723
+ rust_feature : PossibleFeature :: Some { rust_feature } ,
729
724
}
725
+ } else {
726
+ UnknownCTargetFeature { feature, rust_feature : PossibleFeature :: None }
727
+ } ;
728
+ sess. dcx ( ) . emit_warn ( unknown_feature) ;
729
+ }
730
+ Some ( ( _, stability, _) ) => {
731
+ if let Err ( reason) = stability. toggle_allowed ( & sess. target , enable) {
732
+ sess. dcx ( ) . emit_warn ( ForbiddenCTargetFeature {
733
+ feature,
734
+ enabled : if enable { "enabled" } else { "disabled" } ,
735
+ reason,
736
+ } ) ;
737
+ } else if stability. requires_nightly ( ) . is_some ( ) {
738
+ // An unstable feature. Warn about using it. It makes little sense
739
+ // to hard-error here since we just warn about fully unknown
740
+ // features above.
741
+ sess. dcx ( ) . emit_warn ( UnstableCTargetFeature { feature } ) ;
730
742
}
731
743
}
744
+ }
732
745
733
- // FIXME(nagisa): figure out how to not allocate a full hashset here.
734
- featsmap. insert ( feature, enable_disable == '+' ) ;
746
+ // Ensure that the features we enable/disable are compatible with the ABI.
747
+ if enable {
748
+ if abi_disable_set. contains ( feature) {
749
+ sess. dcx ( ) . emit_warn ( ForbiddenCTargetFeature {
750
+ feature,
751
+ enabled : "enabled" ,
752
+ reason : "this feature is incompatible with the target ABI" ,
753
+ } ) ;
754
+ }
755
+ } else {
756
+ if abi_enable_set. contains ( feature) {
757
+ sess. dcx ( ) . emit_warn ( ForbiddenCTargetFeature {
758
+ feature,
759
+ enabled : "disabled" ,
760
+ reason : "this feature is required by the target ABI" ,
761
+ } ) ;
762
+ }
735
763
}
736
764
765
+ // FIXME(nagisa): figure out how to not allocate a full hashset here.
766
+ featsmap. insert ( feature, enable) ;
767
+ }
768
+ }
769
+
770
+ // To be sure the ABI-relevant features are all in the right state, we explicitly
771
+ // (un)set them here. This means if the target spec sets those features wrong,
772
+ // we will silently correct them rather than silently producing wrong code.
773
+ // (The target sanity check tries to catch this, but we can't know which features are
774
+ // enabled in LLVM by default so we can't be fully sure about that check.)
775
+ for feature in abi_enable {
776
+ all_rust_features. push ( ( true , feature) ) ;
777
+ }
778
+ for feature in abi_disable {
779
+ all_rust_features. push ( ( false , feature) ) ;
780
+ }
781
+
782
+ // Translate this into LLVM features.
783
+ let feats = all_rust_features
784
+ . iter ( )
785
+ . filter_map ( |& ( enable, feature) | {
786
+ let enable_disable = if enable { '+' } else { '-' } ;
737
787
// We run through `to_llvm_features` when
738
788
// passing requests down to LLVM. This means that all in-language
739
789
// features also work on the command line instead of having two
@@ -746,9 +796,9 @@ pub(crate) fn global_llvm_features(
746
796
enable_disable, llvm_feature. llvm_feature_name
747
797
) )
748
798
. chain ( llvm_feature. dependency . into_iter ( ) . filter_map (
749
- move |feat| match ( enable_disable , feat) {
750
- ( '-' | '+' , TargetFeatureFoldStrength :: Both ( f) )
751
- | ( '+' , TargetFeatureFoldStrength :: EnableOnly ( f) ) => {
799
+ move |feat| match ( enable , feat) {
800
+ ( _ , TargetFeatureFoldStrength :: Both ( f) )
801
+ | ( true , TargetFeatureFoldStrength :: EnableOnly ( f) ) => {
752
802
Some ( format ! ( "{enable_disable}{f}" ) )
753
803
}
754
804
_ => None ,
@@ -780,22 +830,6 @@ pub(crate) fn global_llvm_features(
780
830
features
781
831
}
782
832
783
- /// Returns a feature name for the given `+feature` or `-feature` string.
784
- ///
785
- /// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].)
786
- fn backend_feature_name < ' a > ( sess : & Session , s : & ' a str ) -> Option < & ' a str > {
787
- // features must start with a `+` or `-`.
788
- let feature = s
789
- . strip_prefix ( & [ '+' , '-' ] [ ..] )
790
- . unwrap_or_else ( || sess. dcx ( ) . emit_fatal ( InvalidTargetFeaturePrefix { feature : s } ) ) ;
791
- // Rustc-specific feature requests like `+crt-static` or `-crt-static`
792
- // are not passed down to LLVM.
793
- if s. is_empty ( ) || RUSTC_SPECIFIC_FEATURES . contains ( & feature) {
794
- return None ;
795
- }
796
- Some ( feature)
797
- }
798
-
799
833
pub ( crate ) fn tune_cpu ( sess : & Session ) -> Option < & str > {
800
834
let name = sess. opts . unstable_opts . tune_cpu . as_ref ( ) ?;
801
835
Some ( handle_native ( name) )
0 commit comments