adjust GCC backend
This commit is contained in:
parent
cfae43d638
commit
0a8cfc2f8f
2 changed files with 117 additions and 82 deletions
|
@ -28,6 +28,7 @@ pub(crate) struct UnstableCTargetFeature<'a> {
|
||||||
#[diag(codegen_gcc_forbidden_ctarget_feature)]
|
#[diag(codegen_gcc_forbidden_ctarget_feature)]
|
||||||
pub(crate) struct ForbiddenCTargetFeature<'a> {
|
pub(crate) struct ForbiddenCTargetFeature<'a> {
|
||||||
pub feature: &'a str,
|
pub feature: &'a str,
|
||||||
|
pub enabled: &'a str,
|
||||||
pub reason: &'a str,
|
pub reason: &'a str,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
#[cfg(feature = "master")]
|
#[cfg(feature = "master")]
|
||||||
use gccjit::Context;
|
use gccjit::Context;
|
||||||
use rustc_codegen_ssa::codegen_attrs::check_tied_features;
|
use rustc_codegen_ssa::codegen_attrs::check_tied_features;
|
||||||
use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
|
use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_middle::bug;
|
use rustc_data_structures::unord::UnordSet;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
|
use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
|
||||||
use smallvec::{SmallVec, smallvec};
|
use smallvec::{SmallVec, smallvec};
|
||||||
|
@ -37,82 +39,128 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
|
||||||
let mut features = vec![];
|
let mut features = vec![];
|
||||||
|
|
||||||
// Features implied by an implicit or explicit `--target`.
|
// Features implied by an implicit or explicit `--target`.
|
||||||
features.extend(
|
features.extend(sess.target.features.split(',').filter(|v| !v.is_empty()).map(String::from));
|
||||||
sess.target
|
|
||||||
.features
|
|
||||||
.split(',')
|
|
||||||
.filter(|v| !v.is_empty() && backend_feature_name(v).is_some())
|
|
||||||
.map(String::from),
|
|
||||||
);
|
|
||||||
|
|
||||||
// -Ctarget-features
|
// -Ctarget-features
|
||||||
let known_features = sess.target.rust_target_features();
|
let known_features = sess.target.rust_target_features();
|
||||||
let mut featsmap = FxHashMap::default();
|
let mut featsmap = FxHashMap::default();
|
||||||
let feats = sess
|
|
||||||
.opts
|
|
||||||
.cg
|
|
||||||
.target_feature
|
|
||||||
.split(',')
|
|
||||||
.filter_map(|s| {
|
|
||||||
let enable_disable = match s.chars().next() {
|
|
||||||
None => return None,
|
|
||||||
Some(c @ ('+' | '-')) => c,
|
|
||||||
Some(_) => {
|
|
||||||
if diagnostics {
|
|
||||||
sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature: s });
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get the backend feature name, if any.
|
// Ensure that all ABI-required features are enabled, and the ABI-forbidden ones
|
||||||
// This excludes rustc-specific features, that do not get passed down to GCC.
|
// are disabled.
|
||||||
let feature = backend_feature_name(s)?;
|
let (abi_enable, abi_disable) = sess.target.abi_required_features();
|
||||||
// Warn against use of GCC specific feature names on the CLI.
|
let abi_enable_set = FxHashSet::from_iter(abi_enable.iter().copied());
|
||||||
|
let abi_disable_set = FxHashSet::from_iter(abi_disable.iter().copied());
|
||||||
|
|
||||||
|
// Compute implied features
|
||||||
|
let mut all_rust_features = vec![];
|
||||||
|
for feature in sess.opts.cg.target_feature.split(',') {
|
||||||
|
if let Some(feature) = feature.strip_prefix('+') {
|
||||||
|
all_rust_features.extend(
|
||||||
|
UnordSet::from(sess.target.implied_target_features(std::iter::once(feature)))
|
||||||
|
.to_sorted_stable_ord()
|
||||||
|
.iter()
|
||||||
|
.map(|&&s| (true, s)),
|
||||||
|
)
|
||||||
|
} else if let Some(feature) = feature.strip_prefix('-') {
|
||||||
|
// FIXME: Why do we not remove implied features on "-" here?
|
||||||
|
// We do the equivalent above in `target_features_cfg`.
|
||||||
|
// See <https://github.com/rust-lang/rust/issues/134792>.
|
||||||
|
all_rust_features.push((false, feature));
|
||||||
|
} else if !feature.is_empty() {
|
||||||
if diagnostics {
|
if diagnostics {
|
||||||
let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
|
sess.dcx().emit_warn(UnknownCTargetFeaturePrefix { feature });
|
||||||
match feature_state {
|
}
|
||||||
None => {
|
}
|
||||||
let rust_feature =
|
}
|
||||||
known_features.iter().find_map(|&(rust_feature, _, _)| {
|
// Remove features that are meant for rustc, not LLVM.
|
||||||
let gcc_features = to_gcc_features(sess, rust_feature);
|
all_rust_features.retain(|(_, feature)| {
|
||||||
if gcc_features.contains(&feature)
|
// Retain if it is not a rustc feature
|
||||||
&& !gcc_features.contains(&rust_feature)
|
!RUSTC_SPECIFIC_FEATURES.contains(feature)
|
||||||
{
|
});
|
||||||
Some(rust_feature)
|
|
||||||
} else {
|
// Check feature validity.
|
||||||
None
|
if diagnostics {
|
||||||
}
|
for &(enable, feature) in &all_rust_features {
|
||||||
});
|
let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
|
||||||
let unknown_feature = if let Some(rust_feature) = rust_feature {
|
match feature_state {
|
||||||
UnknownCTargetFeature {
|
None => {
|
||||||
feature,
|
let rust_feature = known_features.iter().find_map(|&(rust_feature, _, _)| {
|
||||||
rust_feature: PossibleFeature::Some { rust_feature },
|
let gcc_features = to_gcc_features(sess, rust_feature);
|
||||||
}
|
if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature)
|
||||||
} else {
|
|
||||||
UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
|
|
||||||
};
|
|
||||||
sess.dcx().emit_warn(unknown_feature);
|
|
||||||
}
|
|
||||||
Some((_, stability, _)) => {
|
|
||||||
if let Err(reason) =
|
|
||||||
stability.toggle_allowed(&sess.target, enable_disable == '+')
|
|
||||||
{
|
{
|
||||||
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
|
Some(rust_feature)
|
||||||
} else if stability.requires_nightly().is_some() {
|
} else {
|
||||||
// An unstable feature. Warn about using it. (It makes little sense
|
None
|
||||||
// to hard-error here since we just warn about fully unknown
|
|
||||||
// features above).
|
|
||||||
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
let unknown_feature = if let Some(rust_feature) = rust_feature {
|
||||||
|
UnknownCTargetFeature {
|
||||||
|
feature,
|
||||||
|
rust_feature: PossibleFeature::Some { rust_feature },
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
|
||||||
|
};
|
||||||
|
sess.dcx().emit_warn(unknown_feature);
|
||||||
|
}
|
||||||
|
Some((_, stability, _)) => {
|
||||||
|
if let Err(reason) = stability.toggle_allowed() {
|
||||||
|
sess.dcx().emit_warn(ForbiddenCTargetFeature {
|
||||||
|
feature,
|
||||||
|
enabled: if enable { "enabled" } else { "disabled" },
|
||||||
|
reason,
|
||||||
|
});
|
||||||
|
} else if stability.requires_nightly().is_some() {
|
||||||
|
// An unstable feature. Warn about using it. (It makes little sense
|
||||||
|
// to hard-error here since we just warn about fully unknown
|
||||||
|
// features above).
|
||||||
|
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME(nagisa): figure out how to not allocate a full hashset here.
|
|
||||||
featsmap.insert(feature, enable_disable == '+');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ... otherwise though we run through `to_gcc_features` when
|
// Ensure that the features we enable/disable are compatible with the ABI.
|
||||||
|
if enable {
|
||||||
|
if abi_disable_set.contains(feature) {
|
||||||
|
sess.dcx().emit_warn(ForbiddenCTargetFeature {
|
||||||
|
feature,
|
||||||
|
enabled: "enabled",
|
||||||
|
reason: "this feature is incompatible with the target ABI",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if abi_enable_set.contains(feature) {
|
||||||
|
sess.dcx().emit_warn(ForbiddenCTargetFeature {
|
||||||
|
feature,
|
||||||
|
enabled: "disabled",
|
||||||
|
reason: "this feature is required by the target ABI",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(nagisa): figure out how to not allocate a full hashset here.
|
||||||
|
featsmap.insert(feature, enable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be sure the ABI-relevant features are all in the right state, we explicitly
|
||||||
|
// (un)set them here. This means if the target spec sets those features wrong,
|
||||||
|
// we will silently correct them rather than silently producing wrong code.
|
||||||
|
// (The target sanity check tries to catch this, but we can't know which features are
|
||||||
|
// enabled in GCC by default so we can't be fully sure about that check.)
|
||||||
|
for feature in abi_enable {
|
||||||
|
all_rust_features.push((true, feature));
|
||||||
|
}
|
||||||
|
for feature in abi_disable {
|
||||||
|
all_rust_features.push((false, feature));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Translate this into GCC features.
|
||||||
|
let feats = all_rust_features
|
||||||
|
.iter()
|
||||||
|
.filter_map(|&(enable, feature)| {
|
||||||
|
let enable_disable = if enable { '+' } else { '-' };
|
||||||
|
// We run through `to_gcc_features` when
|
||||||
// passing requests down to GCC. This means that all in-language
|
// passing requests down to GCC. This means that all in-language
|
||||||
// features also work on the command line instead of having two
|
// features also work on the command line instead of having two
|
||||||
// different names when the GCC name and the Rust name differ.
|
// 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
|
||||||
features
|
features
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a feature name for the given `+feature` or `-feature` string.
|
|
||||||
///
|
|
||||||
/// Only allows features that are backend specific (i.e. not [`RUSTC_SPECIFIC_FEATURES`].)
|
|
||||||
fn backend_feature_name(s: &str) -> Option<&str> {
|
|
||||||
// features must start with a `+` or `-`.
|
|
||||||
let feature = s.strip_prefix(&['+', '-'][..]).unwrap_or_else(|| {
|
|
||||||
bug!("target feature `{}` must begin with a `+` or `-`", s);
|
|
||||||
});
|
|
||||||
// Rustc-specific feature requests like `+crt-static` or `-crt-static`
|
|
||||||
// are not passed down to GCC.
|
|
||||||
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(feature)
|
|
||||||
}
|
|
||||||
|
|
||||||
// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
|
// To find a list of GCC's names, check https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
|
||||||
pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
|
pub fn to_gcc_features<'a>(sess: &Session, s: &'a str) -> SmallVec<[&'a str; 2]> {
|
||||||
let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
|
let arch = if sess.target.arch == "x86_64" { "x86" } else { &*sess.target.arch };
|
||||||
match (arch, s) {
|
match (arch, s) {
|
||||||
|
// FIXME: seems like x87 does not exist?
|
||||||
|
("x86", "x87") => smallvec![],
|
||||||
("x86", "sse4.2") => smallvec!["sse4.2", "crc32"],
|
("x86", "sse4.2") => smallvec!["sse4.2", "crc32"],
|
||||||
("x86", "pclmulqdq") => smallvec!["pclmul"],
|
("x86", "pclmulqdq") => smallvec!["pclmul"],
|
||||||
("x86", "rdrand") => smallvec!["rdrnd"],
|
("x86", "rdrand") => smallvec!["rdrnd"],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue