@@ -21,8 +21,8 @@ use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATU
2121
2222use crate :: back:: write:: create_informational_target_machine;
2323use crate :: errors:: {
24- FixedX18InvalidArch , ForbiddenCTargetFeature , InvalidTargetFeaturePrefix , PossibleFeature ,
25- UnknownCTargetFeature , UnknownCTargetFeaturePrefix , UnstableCTargetFeature ,
24+ FixedX18InvalidArch , ForbiddenCTargetFeature , PossibleFeature , UnknownCTargetFeature ,
25+ UnknownCTargetFeaturePrefix , UnstableCTargetFeature ,
2626} ;
2727use crate :: llvm;
2828
@@ -348,15 +348,28 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol>
348348 {
349349 if enabled {
350350 // 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+ ) ;
352361 } else {
353362 // Remove transitively reverse-implied features.
354363
355364 // We don't care about the order in `features` since the only thing we use it for is the
356365 // `features.contains` below.
357366 #[ allow( rustc:: potential_query_instability) ]
358367 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+ {
360373 // If `f` if implies `feature`, then `!feature` implies `!f`, so we have to
361374 // remove `f`. (This is the standard logical contraposition principle.)
362375 false
@@ -638,7 +651,7 @@ pub(crate) fn global_llvm_features(
638651 sess. target
639652 . features
640653 . split ( ',' )
641- . filter ( |v| !v. is_empty ( ) && backend_feature_name ( sess , v ) . is_some ( ) )
654+ . filter ( |v| !v. is_empty ( ) )
642655 // Drop +v8plus feature introduced in LLVM 20.
643656 . filter ( |v| * v != "+v8plus" || get_version ( ) >= ( 20 , 0 , 0 ) )
644657 . map ( String :: from) ,
@@ -651,89 +664,126 @@ pub(crate) fn global_llvm_features(
651664 // -Ctarget-features
652665 if !only_base_features {
653666 let known_features = sess. target . rust_target_features ( ) ;
667+ // Will only be filled when `diagnostics` is set!
654668 let mut featsmap = FxHashMap :: default ( ) ;
655669
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
657677 let mut all_rust_features = vec ! [ ] ;
658678 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+ }
670695 }
671696 }
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+ } ) ;
672702
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
715718 }
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 } ,
729724 }
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 } ) ;
730742 }
731743 }
744+ }
732745
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+ }
735763 }
736764
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 { '-' } ;
737787 // We run through `to_llvm_features` when
738788 // passing requests down to LLVM. This means that all in-language
739789 // features also work on the command line instead of having two
@@ -746,9 +796,9 @@ pub(crate) fn global_llvm_features(
746796 enable_disable, llvm_feature. llvm_feature_name
747797 ) )
748798 . 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) ) => {
752802 Some ( format ! ( "{enable_disable}{f}" ) )
753803 }
754804 _ => None ,
@@ -780,22 +830,6 @@ pub(crate) fn global_llvm_features(
780830 features
781831}
782832
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-
799833pub ( crate ) fn tune_cpu ( sess : & Session ) -> Option < & str > {
800834 let name = sess. opts . unstable_opts . tune_cpu . as_ref ( ) ?;
801835 Some ( handle_native ( name) )
0 commit comments