Skip to content

Commit 648e5e1

Browse files
committed
mark some target features as 'forbidden' so they cannot be (un)set
1 parent 9b82580 commit 648e5e1

17 files changed

+279
-128
lines changed

compiler/rustc_codegen_llvm/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ 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`
12+
1013
codegen_llvm_from_llvm_diag = {$message}
1114
1215
codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}

compiler/rustc_codegen_llvm/src/errors.rs

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

34+
#[derive(Diagnostic)]
35+
#[diag(codegen_llvm_forbidden_ctarget_feature)]
36+
pub(crate) struct ForbiddenCTargetFeature<'a> {
37+
pub feature: &'a str,
38+
}
39+
3440
#[derive(Subdiagnostic)]
3541
pub(crate) enum PossibleFeature<'a> {
3642
#[help(codegen_llvm_possible_feature)]

compiler/rustc_codegen_llvm/src/llvm_util.rs

+54-37
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,13 @@ use rustc_session::config::{PrintKind, PrintRequest};
1515
use rustc_session::Session;
1616
use rustc_span::symbol::Symbol;
1717
use rustc_target::spec::{MergeFunctions, PanicStrategy};
18-
use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
18+
use rustc_target::target_features::{Stability, RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
1919

2020
use crate::back::write::create_informational_target_machine;
2121
use crate::errors::{
22-
FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, TargetFeatureDisableOrEnable,
23-
UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
22+
FixedX18InvalidArch, ForbiddenCTargetFeature, InvalidTargetFeaturePrefix, PossibleFeature,
23+
TargetFeatureDisableOrEnable, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
24+
UnstableCTargetFeature,
2425
};
2526
use crate::llvm;
2627

@@ -298,11 +299,17 @@ pub(crate) fn check_tied_features(
298299
pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
299300
let mut features = vec![];
300301

301-
// Add base features for the target
302+
// Add base features for the target.
303+
// We do *not* add the -Ctarget-features there, and instead duplicate the logic for that below.
304+
// The reaosn is that if LLVM considers a feature implied but we do not, we don't want that to
305+
// show up in `cfg`. That way, `cfg` is entirely under our control -- except for the handling of
306+
// the target CPU, that is still expanded to target features (with all their implied features) by
307+
// LLVM.
302308
let target_machine = create_informational_target_machine(sess, true);
309+
// Compute which of the known target features are enables in the 'base' target machine.
303310
features.extend(
304311
sess.target
305-
.supported_target_features()
312+
.known_target_features()
306313
.iter()
307314
.filter(|(feature, _, _)| {
308315
// skip checking special features, as LLVM may not understands them
@@ -344,7 +351,7 @@ pub fn target_features(sess: &Session, allow_unstable: bool) -> Vec<Symbol> {
344351

345352
// Filter enabled features based on feature gates
346353
sess.target
347-
.supported_target_features()
354+
.known_target_features()
348355
.iter()
349356
.filter_map(|&(feature, gate, _)| {
350357
if sess.is_nightly_build() || allow_unstable || gate.is_stable() {
@@ -407,9 +414,13 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach
407414
let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
408415
let mut rustc_target_features = sess
409416
.target
410-
.supported_target_features()
417+
.known_target_features()
411418
.iter()
412-
.filter_map(|(feature, _gate, _implied)| {
419+
.filter_map(|(feature, gate, _implied)| {
420+
if matches!(gate, Stability::Forbidden) {
421+
// Do not list forbidden features.
422+
return None;
423+
}
413424
// LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings.
414425
let llvm_feature = to_llvm_features(sess, *feature)?.llvm_feature_name;
415426
let desc =
@@ -576,7 +587,7 @@ pub(crate) fn global_llvm_features(
576587

577588
// -Ctarget-features
578589
if !only_base_features {
579-
let supported_features = sess.target.supported_target_features();
590+
let known_features = sess.target.known_target_features();
580591
let (llvm_major, _, _) = get_version();
581592
let mut featsmap = FxHashMap::default();
582593

@@ -614,37 +625,43 @@ pub(crate) fn global_llvm_features(
614625
let feature = backend_feature_name(sess, s)?;
615626
// Warn against use of LLVM specific feature names and unstable features on the CLI.
616627
if diagnostics {
617-
let feature_state = supported_features.iter().find(|&&(v, _, _)| v == feature);
618-
if feature_state.is_none() {
619-
let rust_feature =
620-
supported_features.iter().find_map(|&(rust_feature, _, _)| {
621-
let llvm_features = to_llvm_features(sess, rust_feature)?;
622-
if llvm_features.contains(feature)
623-
&& !llvm_features.contains(rust_feature)
624-
{
625-
Some(rust_feature)
626-
} else {
627-
None
628+
let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
629+
match feature_state {
630+
None => {
631+
let rust_feature =
632+
known_features.iter().find_map(|&(rust_feature, _, _)| {
633+
let llvm_features = to_llvm_features(sess, rust_feature)?;
634+
if llvm_features.contains(feature)
635+
&& !llvm_features.contains(rust_feature)
636+
{
637+
Some(rust_feature)
638+
} else {
639+
None
640+
}
641+
});
642+
let unknown_feature = if let Some(rust_feature) = rust_feature {
643+
UnknownCTargetFeature {
644+
feature,
645+
rust_feature: PossibleFeature::Some { rust_feature },
628646
}
629-
});
630-
let unknown_feature = if let Some(rust_feature) = rust_feature {
631-
UnknownCTargetFeature {
632-
feature,
633-
rust_feature: PossibleFeature::Some { rust_feature },
634-
}
635-
} else {
636-
UnknownCTargetFeature { feature, rust_feature: PossibleFeature::None }
637-
};
638-
sess.dcx().emit_warn(unknown_feature);
639-
} else if feature_state
640-
.is_some_and(|(_name, feature_gate, _implied)| !feature_gate.is_stable())
641-
{
642-
// An unstable feature. Warn about using it.
643-
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
647+
} else {
648+
UnknownCTargetFeature {
649+
feature,
650+
rust_feature: PossibleFeature::None,
651+
}
652+
};
653+
sess.dcx().emit_warn(unknown_feature);
654+
}
655+
Some((_, Stability::Stable, _)) => {}
656+
Some((_, Stability::Unstable(_), _)) => {
657+
// An unstable feature. Warn about using it.
658+
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
659+
}
660+
Some((_, Stability::Forbidden, _)) => {
661+
sess.dcx().emit_err(ForbiddenCTargetFeature { feature });
662+
}
644663
}
645-
}
646664

647-
if diagnostics {
648665
// FIXME(nagisa): figure out how to not allocate a full hashset here.
649666
featsmap.insert(feature, enable_disable == '+');
650667
}

compiler/rustc_codegen_ssa/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error}
5858
5959
codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
6060
61+
codegen_ssa_forbidden_target_feature =
62+
target feature `{$feature}` cannot be toggled with `#[target_feature]`
63+
6164
codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced
6265
6366
codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced

compiler/rustc_codegen_ssa/src/codegen_attrs.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use rustc_span::{sym, Span};
2020
use rustc_target::spec::{abi, SanitizerSet};
2121

2222
use crate::errors;
23-
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature};
23+
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};
2424

2525
fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
2626
use rustc_middle::mir::mono::Linkage::*;
@@ -72,7 +72,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
7272
codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_BUILTINS;
7373
}
7474

75-
let supported_target_features = tcx.supported_target_features(LOCAL_CRATE);
75+
let known_target_features = tcx.known_target_features(LOCAL_CRATE);
7676

7777
let mut inline_span = None;
7878
let mut link_ordinal_span = None;
@@ -298,10 +298,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
298298
check_target_feature_trait_unsafe(tcx, did, attr.span);
299299
}
300300
}
301-
from_target_feature(
301+
from_target_feature_attr(
302302
tcx,
303303
attr,
304-
supported_target_features,
304+
known_target_features,
305305
&mut codegen_fn_attrs.target_features,
306306
);
307307
}

compiler/rustc_codegen_ssa/src/errors.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1017,6 +1017,14 @@ pub struct TargetFeatureSafeTrait {
10171017
pub def: Span,
10181018
}
10191019

1020+
#[derive(Diagnostic)]
1021+
#[diag(codegen_ssa_forbidden_target_feature)]
1022+
pub struct ForbiddenTargetFeature<'a> {
1023+
#[primary_span]
1024+
pub span: Span,
1025+
pub feature: &'a str,
1026+
}
1027+
10201028
#[derive(Diagnostic)]
10211029
#[diag(codegen_ssa_failed_to_get_layout)]
10221030
pub struct FailedToGetLayout<'tcx> {

compiler/rustc_codegen_ssa/src/target_features.rs

+76-39
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@ use rustc_middle::ty::TyCtxt;
1212
use rustc_session::parse::feature_err;
1313
use rustc_span::symbol::{sym, Symbol};
1414
use rustc_span::Span;
15+
use rustc_target::target_features::{self, Stability};
1516

1617
use crate::errors;
1718

18-
pub fn from_target_feature(
19+
/// Compute the enabled target features from the `#[target_feature]` function attribute.
20+
/// Enabled target features are added to `target_features`.
21+
pub fn from_target_feature_attr(
1922
tcx: TyCtxt<'_>,
2023
attr: &ast::Attribute,
21-
supported_target_features: &UnordMap<String, Option<Symbol>>,
24+
known_target_features: &UnordMap<String, target_features::Stability>,
2225
target_features: &mut Vec<TargetFeature>,
2326
) {
2427
let Some(list) = attr.meta_item_list() else { return };
@@ -47,12 +50,12 @@ pub fn from_target_feature(
4750

4851
// We allow comma separation to enable multiple features.
4952
added_target_features.extend(value.as_str().split(',').filter_map(|feature| {
50-
let Some(feature_gate) = supported_target_features.get(feature) else {
53+
let Some(stability) = known_target_features.get(feature) else {
5154
let msg = format!("the feature named `{feature}` is not valid for this target");
5255
let mut err = tcx.dcx().struct_span_err(item.span(), msg);
5356
err.span_label(item.span(), format!("`{feature}` is not valid for this target"));
5457
if let Some(stripped) = feature.strip_prefix('+') {
55-
let valid = supported_target_features.contains_key(stripped);
58+
let valid = known_target_features.contains_key(stripped);
5659
if valid {
5760
err.help("consider removing the leading `+` in the feature name");
5861
}
@@ -62,39 +65,73 @@ pub fn from_target_feature(
6265
};
6366

6467
// Only allow features whose feature gates have been enabled.
65-
let allowed = match feature_gate.as_ref().copied() {
66-
Some(sym::arm_target_feature) => rust_features.arm_target_feature,
67-
Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature,
68-
Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature,
69-
Some(sym::mips_target_feature) => rust_features.mips_target_feature,
70-
Some(sym::riscv_target_feature) => rust_features.riscv_target_feature,
71-
Some(sym::avx512_target_feature) => rust_features.avx512_target_feature,
72-
Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature,
73-
Some(sym::tbm_target_feature) => rust_features.tbm_target_feature,
74-
Some(sym::wasm_target_feature) => rust_features.wasm_target_feature,
75-
Some(sym::rtm_target_feature) => rust_features.rtm_target_feature,
76-
Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature,
77-
Some(sym::bpf_target_feature) => rust_features.bpf_target_feature,
78-
Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature,
79-
Some(sym::csky_target_feature) => rust_features.csky_target_feature,
80-
Some(sym::loongarch_target_feature) => rust_features.loongarch_target_feature,
81-
Some(sym::lahfsahf_target_feature) => rust_features.lahfsahf_target_feature,
82-
Some(sym::prfchw_target_feature) => rust_features.prfchw_target_feature,
83-
Some(sym::sha512_sm_x86) => rust_features.sha512_sm_x86,
84-
Some(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics,
85-
Some(sym::xop_target_feature) => rust_features.xop_target_feature,
86-
Some(sym::s390x_target_feature) => rust_features.s390x_target_feature,
87-
Some(name) => bug!("unknown target feature gate {}", name),
88-
None => true,
68+
let allowed = match stability {
69+
Stability::Forbidden => false,
70+
Stability::Stable => true,
71+
Stability::Unstable(sym::arm_target_feature) => rust_features.arm_target_feature,
72+
Stability::Unstable(sym::hexagon_target_feature) => {
73+
rust_features.hexagon_target_feature
74+
}
75+
Stability::Unstable(sym::powerpc_target_feature) => {
76+
rust_features.powerpc_target_feature
77+
}
78+
Stability::Unstable(sym::mips_target_feature) => rust_features.mips_target_feature,
79+
Stability::Unstable(sym::riscv_target_feature) => {
80+
rust_features.riscv_target_feature
81+
}
82+
Stability::Unstable(sym::avx512_target_feature) => {
83+
rust_features.avx512_target_feature
84+
}
85+
Stability::Unstable(sym::sse4a_target_feature) => {
86+
rust_features.sse4a_target_feature
87+
}
88+
Stability::Unstable(sym::tbm_target_feature) => rust_features.tbm_target_feature,
89+
Stability::Unstable(sym::wasm_target_feature) => rust_features.wasm_target_feature,
90+
Stability::Unstable(sym::rtm_target_feature) => rust_features.rtm_target_feature,
91+
Stability::Unstable(sym::ermsb_target_feature) => {
92+
rust_features.ermsb_target_feature
93+
}
94+
Stability::Unstable(sym::bpf_target_feature) => rust_features.bpf_target_feature,
95+
Stability::Unstable(sym::aarch64_ver_target_feature) => {
96+
rust_features.aarch64_ver_target_feature
97+
}
98+
Stability::Unstable(sym::csky_target_feature) => rust_features.csky_target_feature,
99+
Stability::Unstable(sym::loongarch_target_feature) => {
100+
rust_features.loongarch_target_feature
101+
}
102+
Stability::Unstable(sym::lahfsahf_target_feature) => {
103+
rust_features.lahfsahf_target_feature
104+
}
105+
Stability::Unstable(sym::prfchw_target_feature) => {
106+
rust_features.prfchw_target_feature
107+
}
108+
Stability::Unstable(sym::sha512_sm_x86) => rust_features.sha512_sm_x86,
109+
Stability::Unstable(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics,
110+
Stability::Unstable(sym::xop_target_feature) => rust_features.xop_target_feature,
111+
Stability::Unstable(sym::s390x_target_feature) => {
112+
rust_features.s390x_target_feature
113+
}
114+
Stability::Unstable(name) => bug!("unknown target feature gate {}", name),
89115
};
90116
if !allowed {
91-
feature_err(
92-
&tcx.sess,
93-
feature_gate.unwrap(),
94-
item.span(),
95-
format!("the target feature `{feature}` is currently unstable"),
96-
)
97-
.emit();
117+
match stability {
118+
Stability::Stable => unreachable!(),
119+
&Stability::Unstable(lang_feature_name) => {
120+
feature_err(
121+
&tcx.sess,
122+
lang_feature_name,
123+
item.span(),
124+
format!("the target feature `{feature}` is currently unstable"),
125+
)
126+
.emit();
127+
}
128+
Stability::Forbidden => {
129+
tcx.dcx().emit_err(errors::ForbiddenTargetFeature {
130+
span: item.span(),
131+
feature,
132+
});
133+
}
134+
}
98135
}
99136
Some(Symbol::intern(feature))
100137
}));
@@ -160,20 +197,20 @@ pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_s
160197

161198
pub(crate) fn provide(providers: &mut Providers) {
162199
*providers = Providers {
163-
supported_target_features: |tcx, cnum| {
200+
known_target_features: |tcx, cnum| {
164201
assert_eq!(cnum, LOCAL_CRATE);
165202
if tcx.sess.opts.actually_rustdoc {
166203
// rustdoc needs to be able to document functions that use all the features, so
167204
// whitelist them all
168205
rustc_target::target_features::all_known_features()
169-
.map(|(a, b)| (a.to_string(), b.as_feature_name()))
206+
.map(|(a, b)| (a.to_string(), b))
170207
.collect()
171208
} else {
172209
tcx.sess
173210
.target
174-
.supported_target_features()
211+
.known_target_features()
175212
.iter()
176-
.map(|&(a, b, _)| (a.to_string(), b.as_feature_name()))
213+
.map(|&(a, b, _)| (a.to_string(), b))
177214
.collect()
178215
}
179216
},

compiler/rustc_middle/src/query/mod.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -2160,10 +2160,10 @@ rustc_queries! {
21602160
desc { "computing autoderef types for `{}`", goal.value.value }
21612161
}
21622162

2163-
query supported_target_features(_: CrateNum) -> &'tcx UnordMap<String, Option<Symbol>> {
2163+
query known_target_features(_: CrateNum) -> &'tcx UnordMap<String, rustc_target::target_features::Stability> {
21642164
arena_cache
21652165
eval_always
2166-
desc { "looking up supported target features" }
2166+
desc { "looking up known target features" }
21672167
}
21682168

21692169
query implied_target_features(feature: Symbol) -> &'tcx Vec<Symbol> {

0 commit comments

Comments
 (0)