Skip to content

Commit

Permalink
mark some target features as 'forbidden' so they cannot be (un)set
Browse files Browse the repository at this point in the history
  • Loading branch information
RalfJung committed Sep 2, 2024
1 parent 9b82580 commit 648e5e1
Show file tree
Hide file tree
Showing 17 changed files with 279 additions and 128 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_llvm/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ codegen_llvm_dynamic_linking_with_lto =
codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture
codegen_llvm_forbidden_ctarget_feature =
target feature `{$feature}` cannot be toggled with `-Ctarget-feature`
codegen_llvm_from_llvm_diag = {$message}
codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_codegen_llvm/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ pub(crate) struct UnstableCTargetFeature<'a> {
pub feature: &'a str,
}

#[derive(Diagnostic)]
#[diag(codegen_llvm_forbidden_ctarget_feature)]
pub(crate) struct ForbiddenCTargetFeature<'a> {
pub feature: &'a str,
}

#[derive(Subdiagnostic)]
pub(crate) enum PossibleFeature<'a> {
#[help(codegen_llvm_possible_feature)]
Expand Down
91 changes: 54 additions & 37 deletions compiler/rustc_codegen_llvm/src/llvm_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ use rustc_session::config::{PrintKind, PrintRequest};
use rustc_session::Session;
use rustc_span::symbol::Symbol;
use rustc_target::spec::{MergeFunctions, PanicStrategy};
use rustc_target::target_features::{RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};
use rustc_target::target_features::{Stability, RUSTC_SPECIAL_FEATURES, RUSTC_SPECIFIC_FEATURES};

use crate::back::write::create_informational_target_machine;
use crate::errors::{
FixedX18InvalidArch, InvalidTargetFeaturePrefix, PossibleFeature, TargetFeatureDisableOrEnable,
UnknownCTargetFeature, UnknownCTargetFeaturePrefix, UnstableCTargetFeature,
FixedX18InvalidArch, ForbiddenCTargetFeature, InvalidTargetFeaturePrefix, PossibleFeature,
TargetFeatureDisableOrEnable, UnknownCTargetFeature, UnknownCTargetFeaturePrefix,
UnstableCTargetFeature,
};
use crate::llvm;

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

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

// Filter enabled features based on feature gates
sess.target
.supported_target_features()
.known_target_features()
.iter()
.filter_map(|&(feature, gate, _)| {
if sess.is_nightly_build() || allow_unstable || gate.is_stable() {
Expand Down Expand Up @@ -407,9 +414,13 @@ fn print_target_features(out: &mut String, sess: &Session, tm: &llvm::TargetMach
let mut known_llvm_target_features = FxHashSet::<&'static str>::default();
let mut rustc_target_features = sess
.target
.supported_target_features()
.known_target_features()
.iter()
.filter_map(|(feature, _gate, _implied)| {
.filter_map(|(feature, gate, _implied)| {
if matches!(gate, Stability::Forbidden) {
// Do not list forbidden features.
return None;
}
// LLVM asserts that these are sorted. LLVM and Rust both use byte comparison for these strings.
let llvm_feature = to_llvm_features(sess, *feature)?.llvm_feature_name;
let desc =
Expand Down Expand Up @@ -576,7 +587,7 @@ pub(crate) fn global_llvm_features(

// -Ctarget-features
if !only_base_features {
let supported_features = sess.target.supported_target_features();
let known_features = sess.target.known_target_features();
let (llvm_major, _, _) = get_version();
let mut featsmap = FxHashMap::default();

Expand Down Expand Up @@ -614,37 +625,43 @@ pub(crate) fn global_llvm_features(
let feature = backend_feature_name(sess, s)?;
// Warn against use of LLVM specific feature names and unstable features on the CLI.
if diagnostics {
let feature_state = supported_features.iter().find(|&&(v, _, _)| v == feature);
if feature_state.is_none() {
let rust_feature =
supported_features.iter().find_map(|&(rust_feature, _, _)| {
let llvm_features = to_llvm_features(sess, rust_feature)?;
if llvm_features.contains(feature)
&& !llvm_features.contains(rust_feature)
{
Some(rust_feature)
} else {
None
let feature_state = known_features.iter().find(|&&(v, _, _)| v == feature);
match feature_state {
None => {
let rust_feature =
known_features.iter().find_map(|&(rust_feature, _, _)| {
let llvm_features = to_llvm_features(sess, rust_feature)?;
if llvm_features.contains(feature)
&& !llvm_features.contains(rust_feature)
{
Some(rust_feature)
} else {
None
}
});
let unknown_feature = if let Some(rust_feature) = rust_feature {
UnknownCTargetFeature {
feature,
rust_feature: PossibleFeature::Some { rust_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);
} else if feature_state
.is_some_and(|(_name, feature_gate, _implied)| !feature_gate.is_stable())
{
// An unstable feature. Warn about using it.
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
} else {
UnknownCTargetFeature {
feature,
rust_feature: PossibleFeature::None,
}
};
sess.dcx().emit_warn(unknown_feature);
}
Some((_, Stability::Stable, _)) => {}
Some((_, Stability::Unstable(_), _)) => {
// An unstable feature. Warn about using it.
sess.dcx().emit_warn(UnstableCTargetFeature { feature });
}
Some((_, Stability::Forbidden, _)) => {
sess.dcx().emit_err(ForbiddenCTargetFeature { feature });
}
}
}

if diagnostics {
// FIXME(nagisa): figure out how to not allocate a full hashset here.
featsmap.insert(feature, enable_disable == '+');
}
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_codegen_ssa/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ codegen_ssa_failed_to_write = failed to write {$path}: {$error}
codegen_ssa_field_associated_value_expected = associated value expected for `{$name}`
codegen_ssa_forbidden_target_feature =
target feature `{$feature}` cannot be toggled with `#[target_feature]`
codegen_ssa_ignoring_emit_path = ignoring emit path because multiple .{$extension} files were produced
codegen_ssa_ignoring_output = ignoring -o because multiple .{$extension} files were produced
Expand Down
8 changes: 4 additions & 4 deletions compiler/rustc_codegen_ssa/src/codegen_attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use rustc_span::{sym, Span};
use rustc_target::spec::{abi, SanitizerSet};

use crate::errors;
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature};
use crate::target_features::{check_target_feature_trait_unsafe, from_target_feature_attr};

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

let supported_target_features = tcx.supported_target_features(LOCAL_CRATE);
let known_target_features = tcx.known_target_features(LOCAL_CRATE);

let mut inline_span = None;
let mut link_ordinal_span = None;
Expand Down Expand Up @@ -298,10 +298,10 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
check_target_feature_trait_unsafe(tcx, did, attr.span);
}
}
from_target_feature(
from_target_feature_attr(
tcx,
attr,
supported_target_features,
known_target_features,
&mut codegen_fn_attrs.target_features,
);
}
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_codegen_ssa/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1017,6 +1017,14 @@ pub struct TargetFeatureSafeTrait {
pub def: Span,
}

#[derive(Diagnostic)]
#[diag(codegen_ssa_forbidden_target_feature)]
pub struct ForbiddenTargetFeature<'a> {
#[primary_span]
pub span: Span,
pub feature: &'a str,
}

#[derive(Diagnostic)]
#[diag(codegen_ssa_failed_to_get_layout)]
pub struct FailedToGetLayout<'tcx> {
Expand Down
115 changes: 76 additions & 39 deletions compiler/rustc_codegen_ssa/src/target_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ use rustc_middle::ty::TyCtxt;
use rustc_session::parse::feature_err;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span;
use rustc_target::target_features::{self, Stability};

use crate::errors;

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

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

// Only allow features whose feature gates have been enabled.
let allowed = match feature_gate.as_ref().copied() {
Some(sym::arm_target_feature) => rust_features.arm_target_feature,
Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature,
Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature,
Some(sym::mips_target_feature) => rust_features.mips_target_feature,
Some(sym::riscv_target_feature) => rust_features.riscv_target_feature,
Some(sym::avx512_target_feature) => rust_features.avx512_target_feature,
Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature,
Some(sym::tbm_target_feature) => rust_features.tbm_target_feature,
Some(sym::wasm_target_feature) => rust_features.wasm_target_feature,
Some(sym::rtm_target_feature) => rust_features.rtm_target_feature,
Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature,
Some(sym::bpf_target_feature) => rust_features.bpf_target_feature,
Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature,
Some(sym::csky_target_feature) => rust_features.csky_target_feature,
Some(sym::loongarch_target_feature) => rust_features.loongarch_target_feature,
Some(sym::lahfsahf_target_feature) => rust_features.lahfsahf_target_feature,
Some(sym::prfchw_target_feature) => rust_features.prfchw_target_feature,
Some(sym::sha512_sm_x86) => rust_features.sha512_sm_x86,
Some(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics,
Some(sym::xop_target_feature) => rust_features.xop_target_feature,
Some(sym::s390x_target_feature) => rust_features.s390x_target_feature,
Some(name) => bug!("unknown target feature gate {}", name),
None => true,
let allowed = match stability {
Stability::Forbidden => false,
Stability::Stable => true,
Stability::Unstable(sym::arm_target_feature) => rust_features.arm_target_feature,
Stability::Unstable(sym::hexagon_target_feature) => {
rust_features.hexagon_target_feature
}
Stability::Unstable(sym::powerpc_target_feature) => {
rust_features.powerpc_target_feature
}
Stability::Unstable(sym::mips_target_feature) => rust_features.mips_target_feature,
Stability::Unstable(sym::riscv_target_feature) => {
rust_features.riscv_target_feature
}
Stability::Unstable(sym::avx512_target_feature) => {
rust_features.avx512_target_feature
}
Stability::Unstable(sym::sse4a_target_feature) => {
rust_features.sse4a_target_feature
}
Stability::Unstable(sym::tbm_target_feature) => rust_features.tbm_target_feature,
Stability::Unstable(sym::wasm_target_feature) => rust_features.wasm_target_feature,
Stability::Unstable(sym::rtm_target_feature) => rust_features.rtm_target_feature,
Stability::Unstable(sym::ermsb_target_feature) => {
rust_features.ermsb_target_feature
}
Stability::Unstable(sym::bpf_target_feature) => rust_features.bpf_target_feature,
Stability::Unstable(sym::aarch64_ver_target_feature) => {
rust_features.aarch64_ver_target_feature
}
Stability::Unstable(sym::csky_target_feature) => rust_features.csky_target_feature,
Stability::Unstable(sym::loongarch_target_feature) => {
rust_features.loongarch_target_feature
}
Stability::Unstable(sym::lahfsahf_target_feature) => {
rust_features.lahfsahf_target_feature
}
Stability::Unstable(sym::prfchw_target_feature) => {
rust_features.prfchw_target_feature
}
Stability::Unstable(sym::sha512_sm_x86) => rust_features.sha512_sm_x86,
Stability::Unstable(sym::x86_amx_intrinsics) => rust_features.x86_amx_intrinsics,
Stability::Unstable(sym::xop_target_feature) => rust_features.xop_target_feature,
Stability::Unstable(sym::s390x_target_feature) => {
rust_features.s390x_target_feature
}
Stability::Unstable(name) => bug!("unknown target feature gate {}", name),
};
if !allowed {
feature_err(
&tcx.sess,
feature_gate.unwrap(),
item.span(),
format!("the target feature `{feature}` is currently unstable"),
)
.emit();
match stability {
Stability::Stable => unreachable!(),
&Stability::Unstable(lang_feature_name) => {
feature_err(
&tcx.sess,
lang_feature_name,
item.span(),
format!("the target feature `{feature}` is currently unstable"),
)
.emit();
}
Stability::Forbidden => {
tcx.dcx().emit_err(errors::ForbiddenTargetFeature {
span: item.span(),
feature,
});
}
}
}
Some(Symbol::intern(feature))
}));
Expand Down Expand Up @@ -160,20 +197,20 @@ pub fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_s

pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers {
supported_target_features: |tcx, cnum| {
known_target_features: |tcx, cnum| {
assert_eq!(cnum, LOCAL_CRATE);
if tcx.sess.opts.actually_rustdoc {
// rustdoc needs to be able to document functions that use all the features, so
// whitelist them all
rustc_target::target_features::all_known_features()
.map(|(a, b)| (a.to_string(), b.as_feature_name()))
.map(|(a, b)| (a.to_string(), b))
.collect()
} else {
tcx.sess
.target
.supported_target_features()
.known_target_features()
.iter()
.map(|&(a, b, _)| (a.to_string(), b.as_feature_name()))
.map(|&(a, b, _)| (a.to_string(), b))
.collect()
}
},
Expand Down
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/query/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2160,10 +2160,10 @@ rustc_queries! {
desc { "computing autoderef types for `{}`", goal.value.value }
}

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

query implied_target_features(feature: Symbol) -> &'tcx Vec<Symbol> {
Expand Down
Loading

0 comments on commit 648e5e1

Please sign in to comment.