Skip to content

Commit e8c698b

Browse files
committed
Auto merge of #129884 - RalfJung:forbidden-target-features, r=workingjubilee
mark some target features as 'forbidden' so they cannot be (un)set with -Ctarget-feature The context for this is #116344: some target features change the way floats are passed between functions. Changing those target features is unsound as code compiled for the same target may now use different ABIs. So this introduces a new concept of "forbidden" target features (on top of the existing "stable " and "unstable" categories), and makes it a hard error to (un)set such a target feature. For now, the x86 and ARM feature `soft-float` is on that list. We'll have to make some effort to collect more relevant features, and similar features from other targets, but that can happen after the basic infrastructure for this landed. (These features are being collected in #131799.) I've made this a warning for now to give people some time to speak up if this would break something. MCP: rust-lang/compiler-team#780
2 parents 096277e + ffad9aa commit e8c698b

23 files changed

+371
-157
lines changed

compiler/rustc_codegen_gcc/messages.ftl

+9-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ codegen_gcc_invalid_minimum_alignment =
88
codegen_gcc_lto_not_supported =
99
LTO is not supported. You may get a linker error.
1010
11+
codegen_gcc_forbidden_ctarget_feature =
12+
target feature `{$feature}` cannot be toggled with `-Ctarget-feature`
13+
1114
codegen_gcc_unwinding_inline_asm =
1215
GCC backend does not support unwinding from inline asm
1316
@@ -24,11 +27,15 @@ codegen_gcc_lto_dylib = lto cannot be used for `dylib` crate type without `-Zdyl
2427
codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err})
2528
2629
codegen_gcc_unknown_ctarget_feature =
27-
unknown feature specified for `-Ctarget-feature`: `{$feature}`
28-
.note = it is still passed through to the codegen backend
30+
unknown and unstable feature specified for `-Ctarget-feature`: `{$feature}`
31+
.note = it is still passed through to the codegen backend, but use of this feature might be unsound and the behavior of this feature can change in the future
2932
.possible_feature = you might have meant: `{$rust_feature}`
3033
.consider_filing_feature_request = consider filing a feature request
3134
35+
codegen_gcc_unstable_ctarget_feature =
36+
unstable feature specified for `-Ctarget-feature`: `{$feature}`
37+
.note = this feature is not stably supported; its behavior can change in the future
38+
3239
codegen_gcc_missing_features =
3340
add the missing features in a `target_feature` attribute
3441

compiler/rustc_codegen_gcc/src/errors.rs

+13
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,19 @@ pub(crate) struct UnknownCTargetFeature<'a> {
1717
pub rust_feature: PossibleFeature<'a>,
1818
}
1919

20+
#[derive(Diagnostic)]
21+
#[diag(codegen_gcc_unstable_ctarget_feature)]
22+
#[note]
23+
pub(crate) struct UnstableCTargetFeature<'a> {
24+
pub feature: &'a str,
25+
}
26+
27+
#[derive(Diagnostic)]
28+
#[diag(codegen_gcc_forbidden_ctarget_feature)]
29+
pub(crate) struct ForbiddenCTargetFeature<'a> {
30+
pub feature: &'a str,
31+
}
32+
2033
#[derive(Subdiagnostic)]
2134
pub(crate) enum PossibleFeature<'a> {
2235
#[help(codegen_gcc_possible_feature)]

compiler/rustc_codegen_gcc/src/gcc_util.rs

+40-25
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ use rustc_codegen_ssa::errors::TargetFeatureDisableOrEnable;
55
use rustc_data_structures::fx::FxHashMap;
66
use rustc_middle::bug;
77
use rustc_session::Session;
8-
use rustc_target::target_features::RUSTC_SPECIFIC_FEATURES;
8+
use rustc_target::target_features::{RUSTC_SPECIFIC_FEATURES, Stability};
99
use smallvec::{SmallVec, smallvec};
1010

11-
use crate::errors::{PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix};
11+
use crate::errors::{
12+
ForbiddenCTargetFeature, PossibleFeature, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
13+
UnstableCTargetFeature,
14+
};
1215

1316
/// The list of GCC features computed from CLI flags (`-Ctarget-cpu`, `-Ctarget-feature`,
1417
/// `--target` and similar).
@@ -43,7 +46,7 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
4346
);
4447

4548
// -Ctarget-features
46-
let supported_features = sess.target.supported_target_features();
49+
let known_features = sess.target.rust_target_features();
4750
let mut featsmap = FxHashMap::default();
4851
let feats = sess
4952
.opts
@@ -62,37 +65,49 @@ pub(crate) fn global_gcc_features(sess: &Session, diagnostics: bool) -> Vec<Stri
6265
}
6366
};
6467

68+
// Get the backend feature name, if any.
69+
// This excludes rustc-specific features, that do not get passed down to GCC.
6570
let feature = backend_feature_name(s)?;
6671
// Warn against use of GCC specific feature names on the CLI.
67-
if diagnostics && !supported_features.iter().any(|&(v, _, _)| v == feature) {
68-
let rust_feature = supported_features.iter().find_map(|&(rust_feature, _, _)| {
69-
let gcc_features = to_gcc_features(sess, rust_feature);
70-
if gcc_features.contains(&feature) && !gcc_features.contains(&rust_feature) {
71-
Some(rust_feature)
72-
} else {
73-
None
72+
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);
7496
}
75-
});
76-
let unknown_feature = if let Some(rust_feature) = rust_feature {
77-
UnknownCTargetFeature {
78-
feature,
79-
rust_feature: PossibleFeature::Some { rust_feature },
97+
Some((_, Stability::Stable, _)) => {}
98+
Some((_, Stability::Unstable(_), _)) => {
99+
// An unstable feature. Warn about using it.
100+
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
80101
}
81-
} else {
82-
UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
83-
};
84-
sess.dcx().emit_warn(unknown_feature);
85-
}
102+
Some((_, Stability::Forbidden { .. }, _)) => {
103+
sess.dcx().emit_err(ForbiddenCTargetFeature { feature });
104+
}
105+
}
86106

87-
if diagnostics {
88107
// FIXME(nagisa): figure out how to not allocate a full hashset here.
89108
featsmap.insert(feature, enable_disable == '+');
90109
}
91110

92-
// rustc-specific features do not get passed down to GCC…
93-
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
94-
return None;
95-
}
96111
// ... otherwise though we run through `to_gcc_features` when
97112
// passing requests down to GCC. This means that all in-language
98113
// features also work on the command line instead of having two

compiler/rustc_codegen_gcc/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -491,8 +491,9 @@ pub fn target_features(
491491
) -> Vec<Symbol> {
492492
// TODO(antoyo): use global_gcc_features.
493493
sess.target
494-
.supported_target_features()
494+
.rust_target_features()
495495
.iter()
496+
.filter(|(_, gate, _)| gate.is_supported())
496497
.filter_map(|&(feature, gate, _)| {
497498
if sess.is_nightly_build() || allow_unstable || gate.is_stable() {
498499
Some(feature)

compiler/rustc_codegen_llvm/messages.ftl

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ codegen_llvm_dynamic_linking_with_lto =
77
88
codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
99
10+
codegen_llvm_forbidden_ctarget_feature =
11+
target feature `{$feature}` cannot be toggled with `-Ctarget-feature`: {$reason}
12+
.note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
13+
codegen_llvm_forbidden_ctarget_feature_issue = for more information, see issue #116344 <https://github.com/rust-lang/rust/issues/116344>
14+
1015
codegen_llvm_from_llvm_diag = {$message}
1116
1217
codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}

compiler/rustc_codegen_llvm/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ pub(crate) struct UnstableCTargetFeature<'a> {
3131
pub feature: &'a str,
3232
}
3333

34+
#[derive(Diagnostic)]
35+
#[diag(codegen_llvm_forbidden_ctarget_feature)]
36+
#[note]
37+
#[note(codegen_llvm_forbidden_ctarget_feature_issue)]
38+
pub(crate) struct ForbiddenCTargetFeature<'a> {
39+
pub feature: &'a str,
40+
pub reason: &'a str,
41+
}
42+
3443
#[derive(Subdiagnostic)]
3544
pub(crate) enum PossibleFeature<'a> {
3645
#[help(codegen_llvm_possible_feature)]

compiler/rustc_codegen_llvm/src/llvm_util.rs

+71-48
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ use rustc_session::Session;
1717
use rustc_session::config::{PrintKind, PrintRequest};
1818
use rustc_span::symbol::Symbol;
1919
use rustc_target::spec::{MergeFunctions, PanicStrategy, SmallDataThresholdSupport};
20-
use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
20+
use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES, Stability};
2121

2222
use crate::back::write::create_informational_target_machine;
2323
use crate::errors::{
24-
FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, UnknownCTargetFeature,
25-
UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
24+
FixedX18InvalidArch, ForbiddenCTargetFeature, InvalidTargetFeaturePrefix, PossibleFeature,
25+
UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
2626
};
2727
use crate::llvm;
2828

@@ -281,19 +281,29 @@ pub(crate) fn to_llvm_features<'a>(sess: &Session, s: &'a str) -> Option<LLVMFea
281281
}
282282
}
283283

284-
/// Used to generate cfg variables and apply features
285-
/// Must express features in the way Rust understands them
284+
/// Used to generate cfg variables and apply features.
285+
/// Must express features in the way Rust understands them.
286+
///
287+
/// We do not have to worry about RUSTC_SPECIFIC_FEATURES here, those are handled outside codegen.
286288
pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
287-
let mut features = vec![];
288-
289-
// Add base features for the target
289+
let mut features: FxHashSet<Symbol> = Default::default();
290+
291+
// Add base features for the target.
292+
// We do *not* add the -Ctarget-features there, and instead duplicate the logic for that below.
293+
// The reason is that if LLVM considers a feature implied but we do not, we don't want that to
294+
// show up in `cfg`. That way, `cfg` is entirely under our control -- except for the handling of
295+
// the target CPU, that is still expanded to target features (with all their implied features) by
296+
// LLVM.
290297
let target_machine = create_informational_target_machine(sess, true);
298+
// Compute which of the known target features are enabled in the 'base' target machine.
299+
// We only consider "supported" features; "forbidden" features are not reflected in `cfg` as of now.
291300
features.extend(
292301
sess.target
293-
.supported_target_features()
302+
.rust_target_features()
294303
.iter()
304+
.filter(|(_, gate, _)| gate.is_supported())
295305
.filter(|(feature, _, _)| {
296-
// skip checking special features, as LLVM may not understands them
306+
// skip checking special features, as LLVM may not understand them
297307
if RUSTC_SPECIAL_FEATURES.contains(feature) {
298308
return true;
299309
}
@@ -324,16 +334,22 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
324334
if enabled {
325335
features.extend(sess.target.implied_target_features(std::iter::once(feature)));
326336
} else {
337+
// We don't care about the order in `features` since the only thing we use it for is the
338+
// `features.contains` below.
339+
#[allow(rustc::potential_query_instability)]
327340
features.retain(|f| {
341+
// Keep a feature if it does not imply `feature`. Or, equivalently,
342+
// remove the reverse-dependencies of `feature`.
328343
!sess.target.implied_target_features(std::iter::once(*f)).contains(&feature)
329344
});
330345
}
331346
}
332347

333348
// Filter enabled features based on feature gates
334349
sess.target
335-
.supported_target_features()
350+
.rust_target_features()
336351
.iter()
352+
.filter(|(_, gate, _)| gate.is_supported())
337353
.filter_map(|&(feature, gate, _)| {
338354
if sess.is_nightly_build() || allow_unstable || gate.is_stable() {
339355
Some(feature)
@@ -451,9 +467,13 @@ fn print_target_features(sess: &Session, tm: &llvm::TargetMachine, out: &mut Str
451467
let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
452468
let mut rustc_target_features = sess
453469
.target
454-
.supported_target_features()
470+
.rust_target_features()
455471
.iter()
456-
.filter_map(|(feature, _gate, _implied)| {
472+
.filter_map(|(feature, gate, _implied)| {
473+
if !gate.is_supported() {
474+
// Only list (experimentally) supported features.
475+
return None;
476+
}
457477
// LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these
458478
// strings.
459479
let llvm_feature = to_llvm_features(sess, *feature)?.llvm_feature_name;
@@ -605,7 +625,7 @@ pub(crate) fn global_llvm_features(
605625

606626
// -Ctarget-features
607627
if !only_base_features {
608-
let supported_features = sess.target.supported_target_features();
628+
let known_features = sess.target.rust_target_features();
609629
let mut featsmap = FxHashMap::default();
610630

611631
// insert implied features
@@ -639,50 +659,53 @@ pub(crate) fn global_llvm_features(
639659
}
640660
};
641661

662+
// Get the backend feature name, if any.
663+
// This excludes rustc-specific features, which do not get passed to LLVM.
642664
let feature = backend_feature_name(sess, s)?;
643665
// Warn against use of LLVM specific feature names and unstable features on the CLI.
644666
if diagnostics {
645-
let feature_state = supported_features.iter().find(|&&(v, _, _)| v == feature);
646-
if feature_state.is_none() {
647-
let rust_feature =
648-
supported_features.iter().find_map(|&(rust_feature, _, _)| {
649-
let llvm_features = to_llvm_features(sess, rust_feature)?;
650-
if llvm_features.contains(feature)
651-
&& !llvm_features.contains(rust_feature)
652-
{
653-
Some(rust_feature)
654-
} else {
655-
None
667+
let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
668+
match feature_state {
669+
None => {
670+
let rust_feature =
671+
known_features.iter().find_map(|&(rust_feature, _, _)| {
672+
let llvm_features = to_llvm_features(sess, rust_feature)?;
673+
if llvm_features.contains(feature)
674+
&& !llvm_features.contains(rust_feature)
675+
{
676+
Some(rust_feature)
677+
} else {
678+
None
679+
}
680+
});
681+
let unknown_feature = if let Some(rust_feature) = rust_feature {
682+
UnknownCTargetFeature {
683+
feature,
684+
rust_feature: PossibleFeature::Some { rust_feature },
656685
}
657-
});
658-
let unknown_feature = if let Some(rust_feature) = rust_feature {
659-
UnknownCTargetFeature {
660-
feature,
661-
rust_feature: PossibleFeature::Some { rust_feature },
662-
}
663-
} else {
664-
UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
665-
};
666-
sess.dcx().emit_warn(unknown_feature);
667-
} else if feature_state
668-
.is_some_and(|(_name, feature_gate, _implied)| !feature_gate.is_stable())
669-
{
670-
// An unstable feature. Warn about using it.
671-
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
686+
} else {
687+
UnknownCTargetFeature {
688+
feature,
689+
rust_feature: PossibleFeature::None,
690+
}
691+
};
692+
sess.dcx().emit_warn(unknown_feature);
693+
}
694+
Some((_, Stability::Stable, _)) => {}
695+
Some((_, Stability::Unstable(_), _)) => {
696+
// An unstable feature. Warn about using it.
697+
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
698+
}
699+
Some((_, Stability::Forbidden { reason }, _)) => {
700+
sess.dcx().emit_warn(ForbiddenCTargetFeature { feature, reason });
701+
}
672702
}
673-
}
674703

675-
if diagnostics {
676704
// FIXME(nagisa): figure out how to not allocate a full hashset here.
677705
featsmap.insert(feature, enable_disable == '+');
678706
}
679707

680-
// rustc-specific features do not get passed down to LLVM…
681-
if RUSTC_SPECIFIC_FEATURES.contains(&feature) {
682-
return None;
683-
}
684-
685-
// ... otherwise though we run through `to_llvm_features` when
708+
// We run through `to_llvm_features` when
686709
// passing requests down to LLVM. This means that all in-language
687710
// features also work on the command line instead of having two
688711
// different names when the LLVM name and the Rust name differ.

compiler/rustc_codegen_ssa/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error}
6464
6565
codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
6666
67+
codegen_ssa_forbidden_target_feature_attr =
68+
target feature `{$feature}` cannot be toggled with `#[target_feature]`: {$reason}
69+
6770
codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced
6871
6972
codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced

0 commit comments

Comments
 (0)