|
| 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