mono: require target feature for scalable vectors

Scalable vector types only work with the relevant target features
enabled, so require this for any function with the types in its
signature.
This commit is contained in:
David Wood 2025-07-10 08:46:19 +00:00
parent 4185e9f2ec
commit 5f27abdbc8
No known key found for this signature in database
6 changed files with 190 additions and 55 deletions

View file

@ -2,7 +2,10 @@ monomorphize_abi_error_disabled_vector_type =
this function {$is_call ->
[true] call
*[false] definition
} uses SIMD vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call ->
} uses {$is_scalable ->
[true] scalable
*[false] SIMD
} vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call ->
[true] {" "}in the caller
*[false] {""}
}

View file

@ -78,6 +78,8 @@ pub(crate) struct AbiErrorDisabledVectorType<'a> {
pub ty: Ty<'a>,
/// Whether this is a problem at a call site or at a declaration.
pub is_call: bool,
/// Whether this is a problem with a fixed length vector or a scalable vector
pub is_scalable: bool,
}
#[derive(Diagnostic)]

View file

@ -10,14 +10,37 @@ use rustc_target::callconv::{FnAbi, PassMode};
use crate::errors;
fn uses_vector_registers(mode: &PassMode, repr: &BackendRepr) -> bool {
/// Are vector registers used?
enum UsesVectorRegisters {
/// e.g. `neon`
FixedVector,
/// e.g. `sve`
ScalableVector,
No,
}
/// Determines whether the combination of `mode` and `repr` will use fixed vector registers,
/// scalable vector registers or no vector registers.
fn passes_vectors_by_value(mode: &PassMode, repr: &BackendRepr) -> UsesVectorRegisters {
match mode {
PassMode::Ignore | PassMode::Indirect { .. } => false,
PassMode::Cast { pad_i32: _, cast } => {
cast.prefix.iter().any(|r| r.is_some_and(|x| x.kind == RegKind::Vector))
|| cast.rest.unit.kind == RegKind::Vector
PassMode::Ignore | PassMode::Indirect { .. } => UsesVectorRegisters::No,
PassMode::Cast { pad_i32: _, cast }
if cast.prefix.iter().any(|r| r.is_some_and(|x| x.kind == RegKind::Vector))
|| cast.rest.unit.kind == RegKind::Vector =>
{
UsesVectorRegisters::FixedVector
}
PassMode::Direct(..) | PassMode::Pair(..) => matches!(repr, BackendRepr::SimdVector { .. }),
PassMode::Direct(..) | PassMode::Pair(..)
if matches!(repr, BackendRepr::SimdVector { .. }) =>
{
UsesVectorRegisters::FixedVector
}
PassMode::Direct(..) | PassMode::Pair(..)
if matches!(repr, BackendRepr::ScalableVector { .. }) =>
{
UsesVectorRegisters::ScalableVector
}
_ => UsesVectorRegisters::No,
}
}
@ -32,37 +55,60 @@ fn do_check_simd_vector_abi<'tcx>(
is_call: bool,
loc: impl Fn() -> (Span, HirId),
) {
let feature_def = tcx.sess.target.features_for_correct_vector_abi();
let codegen_attrs = tcx.codegen_fn_attrs(def_id);
let have_feature = |feat: Symbol| {
tcx.sess.unstable_target_features.contains(&feat)
|| codegen_attrs.target_features.iter().any(|x| x.name == feat)
let target_feats = tcx.sess.unstable_target_features.contains(&feat);
let fn_feats = codegen_attrs.target_features.iter().any(|x| x.name == feat);
target_feats || fn_feats
};
for arg_abi in abi.args.iter().chain(std::iter::once(&abi.ret)) {
let size = arg_abi.layout.size;
if uses_vector_registers(&arg_abi.mode, &arg_abi.layout.backend_repr) {
// Find the first feature that provides at least this vector size.
let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) {
Some((_, feature)) => feature,
None => {
match passes_vectors_by_value(&arg_abi.mode, &arg_abi.layout.backend_repr) {
UsesVectorRegisters::FixedVector => {
let feature_def = tcx.sess.target.features_for_correct_fixed_length_vector_abi();
// Find the first feature that provides at least this vector size.
let feature = match feature_def.iter().find(|(bits, _)| size.bits() <= *bits) {
Some((_, feature)) => feature,
None => {
let (span, _hir_id) = loc();
tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType {
span,
ty: arg_abi.layout.ty,
is_call,
});
continue;
}
};
if !feature.is_empty() && !have_feature(Symbol::intern(feature)) {
let (span, _hir_id) = loc();
tcx.dcx().emit_err(errors::AbiErrorUnsupportedVectorType {
tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
span,
required_feature: feature,
ty: arg_abi.layout.ty,
is_call,
is_scalable: false,
});
continue;
}
};
if !feature.is_empty() && !have_feature(Symbol::intern(feature)) {
// Emit error.
let (span, _hir_id) = loc();
tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
span,
required_feature: feature,
ty: arg_abi.layout.ty,
is_call,
});
}
UsesVectorRegisters::ScalableVector => {
let Some(required_feature) =
tcx.sess.target.features_for_correct_scalable_vector_abi()
else {
continue;
};
if !required_feature.is_empty() && !have_feature(Symbol::intern(required_feature)) {
let (span, _) = loc();
tcx.dcx().emit_err(errors::AbiErrorDisabledVectorType {
span,
required_feature,
ty: arg_abi.layout.ty,
is_call,
is_scalable: true,
});
}
}
UsesVectorRegisters::No => {
continue;
}
}
}

View file

@ -911,18 +911,24 @@ pub fn all_rust_features() -> impl Iterator<Item = (&'static str, Stability)> {
// These arrays represent the least-constraining feature that is required for vector types up to a
// certain size to have their "proper" ABI on each architecture.
// Note that they must be kept sorted by vector size.
const X86_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] =
const X86_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[(128, "sse"), (256, "avx"), (512, "avx512f")]; // FIXME: might need changes for AVX10.
const AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "neon")];
const AARCH64_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[(128, "neon")];
// We might want to add "helium" too.
const ARM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "neon")];
const ARM_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[(128, "neon")];
const AMDGPU_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(1024, "")];
const POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "altivec")];
const WASM_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "simd128")];
const S390X_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vector")];
const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[
const AMDGPU_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[(1024, "")];
const POWERPC_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[(128, "altivec")];
const WASM_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[(128, "simd128")];
const S390X_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[(128, "vector")];
const RISCV_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] = &[
(32, "zvl32b"),
(64, "zvl64b"),
(128, "zvl128b"),
@ -937,13 +943,16 @@ const RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[
(65536, "zvl65536b"),
];
// Always error on SPARC, as the necessary target features cannot be enabled in Rust at the moment.
const SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[/*(64, "vis")*/];
const SPARC_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[/*(64, "vis")*/];
const HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] =
const HEXAGON_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[/*(512, "hvx-length64b"),*/ (1024, "hvx-length128b")];
const MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "msa")];
const CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] = &[(128, "vdspv1")];
const LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI: &'static [(u64, &'static str)] =
const MIPS_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[(128, "msa")];
const CSKY_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[(128, "vdspv1")];
const LOONGARCH_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI: &'static [(u64, &'static str)] =
&[(128, "lsx"), (256, "lasx")];
#[derive(Copy, Clone, Debug)]
@ -982,24 +991,26 @@ impl Target {
}
}
pub fn features_for_correct_vector_abi(&self) -> &'static [(u64, &'static str)] {
pub fn features_for_correct_fixed_length_vector_abi(&self) -> &'static [(u64, &'static str)] {
match &self.arch {
Arch::X86 | Arch::X86_64 => X86_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::AArch64 | Arch::Arm64EC => AARCH64_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::Arm => ARM_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::PowerPC | Arch::PowerPC64 => POWERPC_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::LoongArch32 | Arch::LoongArch64 => LOONGARCH_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::RiscV32 | Arch::RiscV64 => RISCV_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::Wasm32 | Arch::Wasm64 => WASM_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::S390x => S390X_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::Sparc | Arch::Sparc64 => SPARC_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::Hexagon => HEXAGON_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => {
MIPS_FEATURES_FOR_CORRECT_VECTOR_ABI
Arch::X86 | Arch::X86_64 => X86_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
Arch::AArch64 | Arch::Arm64EC => AARCH64_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
Arch::Arm => ARM_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
Arch::PowerPC | Arch::PowerPC64 => POWERPC_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
Arch::LoongArch32 | Arch::LoongArch64 => {
LOONGARCH_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI
}
Arch::AmdGpu => AMDGPU_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::RiscV32 | Arch::RiscV64 => RISCV_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
Arch::Wasm32 | Arch::Wasm64 => WASM_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
Arch::S390x => S390X_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
Arch::Sparc | Arch::Sparc64 => SPARC_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
Arch::Hexagon => HEXAGON_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
Arch::Mips | Arch::Mips32r6 | Arch::Mips64 | Arch::Mips64r6 => {
MIPS_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI
}
Arch::AmdGpu => AMDGPU_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
Arch::Nvptx64 | Arch::Bpf | Arch::M68k => &[], // no vector ABI
Arch::CSky => CSKY_FEATURES_FOR_CORRECT_VECTOR_ABI,
Arch::CSky => CSKY_FEATURES_FOR_CORRECT_FIXED_LENGTH_VECTOR_ABI,
// FIXME: for some tier3 targets, we are overly cautious and always give warnings
// when passing args in vector registers.
Arch::Avr
@ -1011,6 +1022,14 @@ impl Target {
}
}
pub fn features_for_correct_scalable_vector_abi(&self) -> Option<&'static str> {
match &self.arch {
Arch::AArch64 | Arch::Arm64EC => Some("sve"),
// Other targets have no scalable vectors or they are unimplemented.
_ => None,
}
}
pub fn tied_target_features(&self) -> &'static [&'static [&'static str]] {
match &self.arch {
Arch::AArch64 | Arch::Arm64EC => AARCH64_TIED_FEATURES,

View file

@ -0,0 +1,40 @@
//@ build-fail
//@ compile-flags: --crate-type=lib
//@ only-aarch64
#![allow(incomplete_features, internal_features)]
#![feature(
simd_ffi,
rustc_attrs,
link_llvm_intrinsics
)]
#[derive(Copy, Clone)]
#[rustc_scalable_vector(4)]
#[allow(non_camel_case_types)]
pub struct svint32_t(i32);
#[inline(never)]
#[target_feature(enable = "sve")]
pub unsafe fn svdup_n_s32(op: i32) -> svint32_t {
extern "C" {
#[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.dup.x.nxv4i32")]
fn _svdup_n_s32(op: i32) -> svint32_t;
//~^ WARN: `extern` block uses type `svint32_t`, which is not FFI-safe
}
unsafe { _svdup_n_s32(op) }
}
pub fn non_annotated_callee(x: svint32_t) {}
//~^ ERROR: this function definition uses scalable vector type `svint32_t`
#[target_feature(enable = "sve")]
pub fn annotated_callee(x: svint32_t) {} // okay!
#[target_feature(enable = "sve")]
pub fn caller() {
unsafe {
let a = svdup_n_s32(42);
non_annotated_callee(a);
annotated_callee(a);
}
}

View file

@ -0,0 +1,25 @@
warning: `extern` block uses type `svint32_t`, which is not FFI-safe
--> $DIR/require-target-feature.rs:21:37
|
LL | fn _svdup_n_s32(op: i32) -> svint32_t;
| ^^^^^^^^^ not FFI-safe
|
= help: consider adding a `#[repr(C)]` or `#[repr(transparent)]` attribute to this struct
= note: this struct has unspecified layout
note: the type is defined here
--> $DIR/require-target-feature.rs:14:1
|
LL | pub struct svint32_t(i32);
| ^^^^^^^^^^^^^^^^^^^^
= note: `#[warn(improper_ctypes)]` on by default
error: this function definition uses scalable vector type `svint32_t` which (with the chosen ABI) requires the `sve` target feature, which is not enabled
--> $DIR/require-target-feature.rs:27:1
|
LL | pub fn non_annotated_callee(x: svint32_t) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
|
= help: consider enabling it globally (`-C target-feature=+sve`) or locally (`#[target_feature(enable="sve")]`)
error: aborting due to 1 previous error; 1 warning emitted