Skip to content

Commit 0a8cfc2

Browse files
committed
adjust GCC backend
1 parent cfae43d commit 0a8cfc2

File tree

2 files changed

+116
-81
lines changed

2 files changed

+116
-81
lines changed

Diff for: compiler/rustc_codegen_gcc/src/errors.rs

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ pub(crate) struct UnstableCTargetFeature<'a> {
2828
#[diag(codegen_gcc_forbidden_ctarget_feature)]
2929
pub(crate) struct ForbiddenCTargetFeature<'a> {
3030
pub feature: &'a str,
31+
pub enabled: &'a str,
3132
pub reason: &'a str,
3233
}
3334

Diff for: compiler/rustc_codegen_gcc/src/gcc_util.rs

+115-81
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
use std::iter::FromIterator;
2+
13
#[cfg(feature = "master")]
24
use gccjit::Context;
35
use rustc_codegen_ssa::codegen_attrs::check_tied_features;
46
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;
79
use rustc_session::Session;
810
use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
911
use smallvec::{SmallVec, smallvec};
@@ -37,82 +39,128 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
3739
let mut features = vec![];
3840

3941
// 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));
4743

4844
// -Ctarget-features
4945
let known_features = sess.target.rust_target_features();
5046
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-
};
6747

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_enable, abi_disable) = sess.target.abi_required_features();
51+
let abi_enable_set = FxHashSet::from_iter(abi_enable.iter().copied());
52+
let abi_disable_set = FxHashSet::from_iter(abi_disable.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() {
7270
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 LLVM.
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)
10090
{
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
10794
}
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 });
108118
}
109119
}
120+
}
110121

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_disable_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+
if abi_enable_set.contains(feature) {
133+
sess.dcx().emit_warn(ForbiddenCTargetFeature {
134+
feature,
135+
enabled: "disabled",
136+
reason: "this feature is required by the target ABI",
137+
});
138+
}
113139
}
114140

115-
// ... otherwise though we run through `to_gcc_features` when
141+
// FIXME(nagisa): figure out how to not allocate a full hashset here.
142+
featsmap.insert(feature, enable);
143+
}
144+
}
145+
146+
// To be sure the ABI-relevant features are all in the right state, we explicitly
147+
// (un)set them here. This means if the target spec sets those features wrong,
148+
// we will silently correct them rather than silently producing wrong code.
149+
// (The target sanity check tries to catch this, but we can't know which features are
150+
// enabled in GCC by default so we can't be fully sure about that check.)
151+
for feature in abi_enable {
152+
all_rust_features.push((true, feature));
153+
}
154+
for feature in abi_disable {
155+
all_rust_features.push((false, feature));
156+
}
157+
158+
// Translate this into GCC features.
159+
let feats = all_rust_features
160+
.iter()
161+
.filter_map(|&(enable, feature)| {
162+
let enable_disable = if enable { '+' } else { '-' };
163+
// We run through `to_gcc_features` when
116164
// passing requests down to GCC. This means that all in-language
117165
// features also work on the command line instead of having two
118166
// different names when the GCC name and the Rust name differ.
@@ -146,26 +194,12 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
146194
features
147195
}
148196

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-
165197
// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
166198
pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
167199
let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
168200
match (arch, s) {
201+
// FIXME: seems like x87 does not exist?
202+
("x86", "x87") => smallvec![],
169203
("x86", "sse4.2") => smallvec!["sse4.2", "crc32"],
170204
("x86", "pclmulqdq") => smallvec!["pclmul"],
171205
("x86", "rdrand") => smallvec!["rdrnd"],

0 commit comments

Comments
 (0)