|  | 
|  | 1 | +use std::iter::FromIterator; | 
|  | 2 | + | 
| 1 | 3 | #[cfg(feature = "master")] | 
| 2 | 4 | use gccjit::Context; | 
| 3 | 5 | use rustc_codegen_ssa::codegen_attrs::check_tied_features; | 
| 4 | 6 | use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable; | 
| 5 |  | -use rustc_data_structures::fx::FxHashMap; | 
| 6 |  | -use rustc_middle::bug; | 
|  | 7 | +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; | 
|  | 8 | +use rustc_data_structures::unord::UnordSet; | 
| 7 | 9 | use rustc_session::Session; | 
| 8 | 10 | use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES; | 
| 9 | 11 | use smallvec::{SmallVec, smallvec}; | 
| @@ -37,82 +39,137 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri | 
| 37 | 39 |     let mut features = vec![]; | 
| 38 | 40 | 
 | 
| 39 | 41 |     // Features implied by an implicit or explicit `--target`. | 
| 40 |  | -    features.extend( | 
| 41 |  | -        sess.target | 
| 42 |  | -            .features | 
| 43 |  | -            .split(',') | 
| 44 |  | -            .filter(|v| !v.is_empty() && backend_feature_name(v).is_some()) | 
| 45 |  | -            .map(String::from), | 
| 46 |  | -    ); | 
|  | 42 | +    features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from)); | 
| 47 | 43 | 
 | 
| 48 | 44 |     // -Ctarget-features | 
| 49 | 45 |     let known_features = sess.target.rust_target_features(); | 
| 50 | 46 |     let mut featsmap = FxHashMap::default(); | 
| 51 |  | -    let feats = sess | 
| 52 |  | -        .opts | 
| 53 |  | -        .cg | 
| 54 |  | -        .target_feature | 
| 55 |  | -        .split(',') | 
| 56 |  | -        .filter_map(|s| { | 
| 57 |  | -            let enable_disable = match s.chars().next() { | 
| 58 |  | -                None => return None, | 
| 59 |  | -                Some(c @ ('+' | '-')) => c, | 
| 60 |  | -                Some(_) => { | 
| 61 |  | -                    if diagnostics { | 
| 62 |  | -                        sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s }); | 
| 63 |  | -                    } | 
| 64 |  | -                    return None; | 
| 65 |  | -                } | 
| 66 |  | -            }; | 
| 67 | 47 | 
 | 
| 68 |  | -            // Get the backend feature name, if any. | 
| 69 |  | -            // This excludes rustc-specific features, that do not get passed down to GCC. | 
| 70 |  | -            let feature = backend_feature_name(s)?; | 
| 71 |  | -            // Warn against use of GCC specific feature names on the CLI. | 
|  | 48 | +    // Ensure that all ABI-required features are enabled, and the ABI-forbidden ones | 
|  | 49 | +    // are disabled. | 
|  | 50 | +    let abi_feature_constraints = sess.target.abi_required_features(); | 
|  | 51 | +    let abi_incompatible_set = | 
|  | 52 | +        FxHashSet::from_iter(abi_feature_constraints.incompatible.iter().copied()); | 
|  | 53 | + | 
|  | 54 | +    // Compute implied features | 
|  | 55 | +    let mut all_rust_features = vec![]; | 
|  | 56 | +    for feature in sess.opts.cg.target_feature.split(',') { | 
|  | 57 | +        if let Some(feature) = feature.strip_prefix('+') { | 
|  | 58 | +            all_rust_features.extend( | 
|  | 59 | +                UnordSet::from(sess.target.implied_target_features(std::iter::once(feature))) | 
|  | 60 | +                    .to_sorted_stable_ord() | 
|  | 61 | +                    .iter() | 
|  | 62 | +                    .map(|&&s| (true, s)), | 
|  | 63 | +            ) | 
|  | 64 | +        } else if let Some(feature) = feature.strip_prefix('-') { | 
|  | 65 | +            // FIXME: Why do we not remove implied features on "-" here? | 
|  | 66 | +            // We do the equivalent above in `target_features_cfg`. | 
|  | 67 | +            // See <https://github.com/rust-lang/rust/issues/134792>. | 
|  | 68 | +            all_rust_features.push((false, feature)); | 
|  | 69 | +        } else if !feature.is_empty() { | 
| 72 | 70 |             if diagnostics { | 
| 73 |  | -                let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); | 
| 74 |  | -                match feature_state { | 
| 75 |  | -                    None => { | 
| 76 |  | -                        let rust_feature = | 
| 77 |  | -                            known_features.iter().find_map(|&(rust_feature, _, _)| { | 
| 78 |  | -                                let gcc_features = to_gcc_features(sess, rust_feature); | 
| 79 |  | -                                if gcc_features.contains(&feature) | 
| 80 |  | -                                    && !gcc_features.contains(&rust_feature) | 
| 81 |  | -                                { | 
| 82 |  | -                                    Some(rust_feature) | 
| 83 |  | -                                } else { | 
| 84 |  | -                                    None | 
| 85 |  | -                                } | 
| 86 |  | -                            }); | 
| 87 |  | -                        let unknown_feature = if let Some(rust_feature) = rust_feature { | 
| 88 |  | -                            UnknownCTargetFeature { | 
| 89 |  | -                                feature, | 
| 90 |  | -                                rust_feature: PossibleFeature::Some { rust_feature }, | 
| 91 |  | -                            } | 
| 92 |  | -                        } else { | 
| 93 |  | -                            UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } | 
| 94 |  | -                        }; | 
| 95 |  | -                        sess.dcx().emit_warn(unknown_feature); | 
| 96 |  | -                    } | 
| 97 |  | -                    Some((_, stability, _)) => { | 
| 98 |  | -                        if let Err(reason) = | 
| 99 |  | -                            stability.toggle_allowed(&sess.target, enable_disable == '+') | 
|  | 71 | +                sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature }); | 
|  | 72 | +            } | 
|  | 73 | +        } | 
|  | 74 | +    } | 
|  | 75 | +    // Remove features that are meant for rustc, not codegen. | 
|  | 76 | +    all_rust_features.retain(|(_, feature)| { | 
|  | 77 | +        // Retain if it is not a rustc feature | 
|  | 78 | +        !RUSTC_SPECIFIC_FEATURES.contains(feature) | 
|  | 79 | +    }); | 
|  | 80 | + | 
|  | 81 | +    // Check feature validity. | 
|  | 82 | +    if diagnostics { | 
|  | 83 | +        for &(enable, feature) in &all_rust_features { | 
|  | 84 | +            let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature); | 
|  | 85 | +            match feature_state { | 
|  | 86 | +                None => { | 
|  | 87 | +                    let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| { | 
|  | 88 | +                        let gcc_features = to_gcc_features(sess, rust_feature); | 
|  | 89 | +                        if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature) | 
| 100 | 90 |                         { | 
| 101 |  | -                            sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason }); | 
| 102 |  | -                        } else if stability.requires_nightly().is_some() { | 
| 103 |  | -                            // An unstable feature. Warn about using it. (It makes little sense | 
| 104 |  | -                            // to hard-error here since we just warn about fully unknown | 
| 105 |  | -                            // features above). | 
| 106 |  | -                            sess.dcx().emit_warn(UnstableCTargetFeature { feature }); | 
|  | 91 | +                            Some(rust_feature) | 
|  | 92 | +                        } else { | 
|  | 93 | +                            None | 
| 107 | 94 |                         } | 
|  | 95 | +                    }); | 
|  | 96 | +                    let unknown_feature = if let Some(rust_feature) = rust_feature { | 
|  | 97 | +                        UnknownCTargetFeature { | 
|  | 98 | +                            feature, | 
|  | 99 | +                            rust_feature: PossibleFeature::Some { rust_feature }, | 
|  | 100 | +                        } | 
|  | 101 | +                    } else { | 
|  | 102 | +                        UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None } | 
|  | 103 | +                    }; | 
|  | 104 | +                    sess.dcx().emit_warn(unknown_feature); | 
|  | 105 | +                } | 
|  | 106 | +                Some((_, stability, _)) => { | 
|  | 107 | +                    if let Err(reason) = stability.toggle_allowed() { | 
|  | 108 | +                        sess.dcx().emit_warn(ForbiddenCTargetFeature { | 
|  | 109 | +                            feature, | 
|  | 110 | +                            enabled: if enable { "enabled" } else { "disabled" }, | 
|  | 111 | +                            reason, | 
|  | 112 | +                        }); | 
|  | 113 | +                    } else if stability.requires_nightly().is_some() { | 
|  | 114 | +                        // An unstable feature. Warn about using it. (It makes little sense | 
|  | 115 | +                        // to hard-error here since we just warn about fully unknown | 
|  | 116 | +                        // features above). | 
|  | 117 | +                        sess.dcx().emit_warn(UnstableCTargetFeature { feature }); | 
| 108 | 118 |                     } | 
| 109 | 119 |                 } | 
|  | 120 | +            } | 
| 110 | 121 | 
 | 
| 111 |  | -                // FIXME(nagisa): figure out how to not allocate a full hashset here. | 
| 112 |  | -                featsmap.insert(feature, enable_disable == '+'); | 
|  | 122 | +            // Ensure that the features we enable/disable are compatible with the ABI. | 
|  | 123 | +            if enable { | 
|  | 124 | +                if abi_incompatible_set.contains(feature) { | 
|  | 125 | +                    sess.dcx().emit_warn(ForbiddenCTargetFeature { | 
|  | 126 | +                        feature, | 
|  | 127 | +                        enabled: "enabled", | 
|  | 128 | +                        reason: "this feature is incompatible with the target ABI", | 
|  | 129 | +                    }); | 
|  | 130 | +                } | 
|  | 131 | +            } else { | 
|  | 132 | +                // FIXME: we have to request implied features here since | 
|  | 133 | +                // negative features do not handle implied features above. | 
|  | 134 | +                for &required in abi_feature_constraints.required.iter() { | 
|  | 135 | +                    let implied = sess.target.implied_target_features(std::iter::once(required)); | 
|  | 136 | +                    if implied.contains(feature) { | 
|  | 137 | +                        sess.dcx().emit_warn(ForbiddenCTargetFeature { | 
|  | 138 | +                            feature, | 
|  | 139 | +                            enabled: "disabled", | 
|  | 140 | +                            reason: "this feature is required by the target ABI", | 
|  | 141 | +                        }); | 
|  | 142 | +                    } | 
|  | 143 | +                } | 
| 113 | 144 |             } | 
| 114 | 145 | 
 | 
| 115 |  | -            // ... otherwise though we run through `to_gcc_features` when | 
|  | 146 | +            // FIXME(nagisa): figure out how to not allocate a full hashset here. | 
|  | 147 | +            featsmap.insert(feature, enable); | 
|  | 148 | +        } | 
|  | 149 | +    } | 
|  | 150 | + | 
|  | 151 | +    // To be sure the ABI-relevant features are all in the right state, we explicitly | 
|  | 152 | +    // (un)set them here. This means if the target spec sets those features wrong, | 
|  | 153 | +    // we will silently correct them rather than silently producing wrong code. | 
|  | 154 | +    // (The target sanity check tries to catch this, but we can't know which features are | 
|  | 155 | +    // enabled in GCC by default so we can't be fully sure about that check.) | 
|  | 156 | +    // We add these at the beginning of the list so that `-Ctarget-features` can | 
|  | 157 | +    // still override it... that's unsound, but more compatible with past behavior. | 
|  | 158 | +    all_rust_features.splice( | 
|  | 159 | +        0..0, | 
|  | 160 | +        abi_feature_constraints | 
|  | 161 | +            .required | 
|  | 162 | +            .iter() | 
|  | 163 | +            .map(|&f| (true, f)) | 
|  | 164 | +            .chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))), | 
|  | 165 | +    ); | 
|  | 166 | + | 
|  | 167 | +    // Translate this into GCC features. | 
|  | 168 | +    let feats = all_rust_features | 
|  | 169 | +        .iter() | 
|  | 170 | +        .filter_map(|&(enable, feature)| { | 
|  | 171 | +            let enable_disable = if enable { '+' } else { '-' }; | 
|  | 172 | +            // We run through `to_gcc_features` when | 
| 116 | 173 |             // passing requests down to GCC. This means that all in-language | 
| 117 | 174 |             // features also work on the command line instead of having two | 
| 118 | 175 |             // different names when the GCC name and the Rust name differ. | 
| @@ -146,26 +203,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri | 
| 146 | 203 |     features | 
| 147 | 204 | } | 
| 148 | 205 | 
 | 
| 149 |  | -/// Returns a feature name for the given `+feature` or `-feature` string. | 
| 150 |  | -/// | 
| 151 |  | -/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].) | 
| 152 |  | -fn backend_feature_name(s: &str) -> Option<&str> { | 
| 153 |  | -    // features must start with a `+` or `-`. | 
| 154 |  | -    let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| { | 
| 155 |  | -        bug!("target feature `{}` must begin with a `+` or `-`", s); | 
| 156 |  | -    }); | 
| 157 |  | -    // Rustc-specific feature requests like `+crt-static` or `-crt-static` | 
| 158 |  | -    // are not passed down to GCC. | 
| 159 |  | -    if RUSTC_SPECIFIC_FEATURES.contains(&feature) { | 
| 160 |  | -        return None; | 
| 161 |  | -    } | 
| 162 |  | -    Some(feature) | 
| 163 |  | -} | 
| 164 |  | - | 
| 165 | 206 | // To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html | 
| 166 | 207 | pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> { | 
| 167 | 208 |     let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch }; | 
| 168 | 209 |     match (arch, s) { | 
|  | 210 | +        // FIXME: seems like x87 does not exist? | 
|  | 211 | +        ("x86", "x87") => smallvec![], | 
| 169 | 212 |         ("x86", "sse4.2") => smallvec!["sse4.2", "crc32"], | 
| 170 | 213 |         ("x86", "pclmulqdq") => smallvec!["pclmul"], | 
| 171 | 214 |         ("x86", "rdrand") => smallvec!["rdrnd"], | 
|  | 
0 commit comments