-
Notifications
You must be signed in to change notification settings - Fork 13k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ABI-required target features: warn when they are missing in base CPU #136147
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -319,7 +319,6 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol> | |
sess.target | ||
.rust_target_features() | ||
.iter() | ||
.filter(|(_, gate, _)| gate.in_cfg()) | ||
.filter(|(feature, _, _)| { | ||
// skip checking special features, as LLVM may not understand them | ||
if RUSTC_SPECIAL_FEATURES.contains(feature) { | ||
|
@@ -388,9 +387,13 @@ pub fn target_features_cfg(sess: &Session, allow_unstable: bool) -> Vec<Symbol> | |
sess.target | ||
.rust_target_features() | ||
.iter() | ||
.filter(|(_, gate, _)| gate.in_cfg()) | ||
.filter_map(|(feature, gate, _)| { | ||
if sess.is_nightly_build() || allow_unstable || gate.requires_nightly().is_none() { | ||
// The `allow_unstable` set is used by rustc internally to determined which target | ||
// features are truly available, so we want to return even perma-unstable "forbidden" | ||
// features. | ||
if allow_unstable | ||
|| (gate.in_cfg() && (sess.is_nightly_build() || gate.requires_nightly().is_none())) | ||
{ | ||
Some(*feature) | ||
} else { | ||
None | ||
|
@@ -670,12 +673,6 @@ pub(crate) fn global_llvm_features( | |
// Will only be filled when `diagnostics` is set! | ||
let mut featsmap = FxHashMap::default(); | ||
|
||
// Ensure that all ABI-required features are enabled, and the ABI-forbidden ones | ||
// are disabled. | ||
let abi_feature_constraints = sess.target.abi_required_features(); | ||
let abi_incompatible_set = | ||
FxHashSet::from_iter(abi_feature_constraints.incompatible.iter().copied()); | ||
|
||
// Compute implied features | ||
let mut all_rust_features = vec![]; | ||
for feature in sess.opts.cg.target_feature.split(',') { | ||
|
@@ -746,52 +743,11 @@ pub(crate) fn global_llvm_features( | |
} | ||
} | ||
|
||
// Ensure that the features we enable/disable are compatible with the ABI. | ||
if enable { | ||
if abi_incompatible_set.contains(feature) { | ||
sess.dcx().emit_warn(ForbiddenCTargetFeature { | ||
feature, | ||
enabled: "enabled", | ||
reason: "this feature is incompatible with the target ABI", | ||
}); | ||
} | ||
} else { | ||
// FIXME: we have to request implied features here since | ||
// negative features do not handle implied features above. | ||
for &required in abi_feature_constraints.required.iter() { | ||
let implied = | ||
sess.target.implied_target_features(std::iter::once(required)); | ||
if implied.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 LLVM by default so we can't be fully sure about that check.) | ||
// We add these at the beginning of the list so that `-Ctarget-features` can | ||
// still override it... that's unsound, but more compatible with past behavior. | ||
all_rust_features.splice( | ||
0..0, | ||
abi_feature_constraints | ||
.required | ||
.iter() | ||
.map(|&f| (true, f)) | ||
.chain(abi_feature_constraints.incompatible.iter().map(|&f| (false, f))), | ||
); | ||
|
||
Comment on lines
-779
to
-794
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. love to see it |
||
// Translate this into LLVM features. | ||
let feats = all_rust_features | ||
.iter() | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,21 +18,25 @@ use rustc_session::{EarlyDiagCtxt, Session, filesearch}; | |
use rustc_span::edit_distance::find_best_match_for_name; | ||
use rustc_span::edition::Edition; | ||
use rustc_span::source_map::SourceMapInputs; | ||
use rustc_span::sym; | ||
use rustc_span::{Symbol, sym}; | ||
use rustc_target::spec::Target; | ||
use tracing::info; | ||
|
||
use crate::errors; | ||
|
||
/// Function pointer type that constructs a new CodegenBackend. | ||
pub type MakeBackendFn = fn() -> Box<dyn CodegenBackend>; | ||
type MakeBackendFn = fn() -> Box<dyn CodegenBackend>; | ||
|
||
/// Adds `target_feature = "..."` cfgs for a variety of platform | ||
/// specific features (SSE, NEON etc.). | ||
/// | ||
/// This is performed by checking whether a set of permitted features | ||
/// is available on the target machine, by querying the codegen backend. | ||
pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dyn CodegenBackend) { | ||
pub(crate) fn add_configuration( | ||
cfg: &mut Cfg, | ||
sess: &mut Session, | ||
codegen_backend: &dyn CodegenBackend, | ||
) { | ||
let tf = sym::target_feature; | ||
|
||
let unstable_target_features = codegen_backend.target_features_cfg(sess, true); | ||
|
@@ -48,6 +52,34 @@ pub fn add_configuration(cfg: &mut Cfg, sess: &mut Session, codegen_backend: &dy | |
} | ||
} | ||
|
||
/// Ensures that all target features required by the ABI are present. | ||
/// Must be called after `unstable_target_features` has been populated! | ||
Comment on lines
+55
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do not love ordering sensitivity, but it is tolerable in small amounts, and it seems we are localizing all our various similarly-shaped checks to this one spot, right? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is now part of early session bringup, which is quite order-dependent. I didn't have any better idea for where to put it... I could put it somewhere in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Then I think we should worry about fixing this after we fix the ordering dependency of session bringup (even if just to make it more state-machine-y) |
||
pub(crate) fn check_abi_required_features(sess: &Session) { | ||
let abi_feature_constraints = sess.target.abi_required_features(); | ||
// We check this against `unstable_target_features` as that is conveniently already | ||
// back-translated to rustc feature names, taking into account `-Ctarget-cpu` and `-Ctarget-feature`. | ||
// Just double-check that the features we care about are actually on our list. | ||
for feature in | ||
abi_feature_constraints.required.iter().chain(abi_feature_constraints.incompatible.iter()) | ||
{ | ||
assert!( | ||
sess.target.rust_target_features().iter().any(|(name, ..)| feature == name), | ||
"target feature {feature} is required/incompatible for the current ABI but not a recognized feature for this target" | ||
); | ||
} | ||
|
||
for feature in abi_feature_constraints.required { | ||
if !sess.unstable_target_features.contains(&Symbol::intern(feature)) { | ||
sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "enabled" }); | ||
} | ||
} | ||
for feature in abi_feature_constraints.incompatible { | ||
if sess.unstable_target_features.contains(&Symbol::intern(feature)) { | ||
sess.dcx().emit_warn(errors::AbiRequiredTargetFeature { feature, enabled: "disabled" }); | ||
} | ||
} | ||
} | ||
|
||
pub static STACK_SIZE: OnceLock<usize> = OnceLock::new(); | ||
pub const DEFAULT_STACK_SIZE: usize = 8 * 1024 * 1024; | ||
|
||
|
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
warning: unstable feature specified for `-Ctarget-feature`: `x87` | ||
| | ||
= note: this feature is not stably supported; its behavior can change in the future | ||
|
||
warning: target feature `x87` cannot be disabled with `-Ctarget-feature`: this feature is required by the target ABI | ||
warning: target feature `x87` must be enabled to ensure that the ABI of the current target can be implemented correctly | ||
| | ||
= note: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! | ||
= note: for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344> | ||
|
||
warning: unstable feature specified for `-Ctarget-feature`: `x87` | ||
| | ||
= note: this feature is not stably supported; its behavior can change in the future | ||
|
||
warning: 2 warnings emitted | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yay