Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 0 additions & 160 deletions crates/core_arch/src/hexagon/scalar.rs

Large diffs are not rendered by default.

140 changes: 0 additions & 140 deletions crates/core_arch/src/mips/msa.rs

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion crates/core_arch/src/powerpc/vsx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,6 @@ mod sealed {
/// Vector permute.
#[inline]
#[target_feature(enable = "vsx")]
//#[rustc_legacy_const_generics(2)]
#[unstable(feature = "stdarch_powerpc", issue = "111145")]
pub unsafe fn vec_xxpermdi<T, const DM: i32>(a: T, b: T) -> T
where
Expand Down
4 changes: 0 additions & 4 deletions crates/core_arch/src/riscv32/zk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ unsafe extern "llvm-intrinsic" {
/// The `BS` parameter is expected to be a constant value and only the bottom 2 bits of `bs` are
/// used.
#[target_feature(enable = "zkne")]
#[rustc_legacy_const_generics(2)]
// See #1464
// #[cfg_attr(test, assert_instr(aes32esi, BS = 0))]
#[inline]
Expand Down Expand Up @@ -80,7 +79,6 @@ pub fn aes32esi<const BS: u8>(rs1: u32, rs2: u32) -> u32 {
/// The `bs` parameter is expected to be a constant value and only the bottom 2 bits of `bs` are
/// used.
#[target_feature(enable = "zkne")]
#[rustc_legacy_const_generics(2)]
// See #1464
// #[cfg_attr(test, assert_instr(aes32esmi, BS = 0))]
#[inline]
Expand Down Expand Up @@ -108,7 +106,6 @@ pub fn aes32esmi<const BS: u8>(rs1: u32, rs2: u32) -> u32 {
/// The `BS` parameter is expected to be a constant value and only the bottom 2 bits of `bs` are
/// used.
#[target_feature(enable = "zknd")]
#[rustc_legacy_const_generics(2)]
// See #1464
// #[cfg_attr(test, assert_instr(aes32dsi, BS = 0))]
#[inline]
Expand Down Expand Up @@ -137,7 +134,6 @@ pub fn aes32dsi<const BS: u8>(rs1: u32, rs2: u32) -> u32 {
/// The `BS` parameter is expected to be a constant value and only the bottom 2 bits of `bs` are
/// used.
#[target_feature(enable = "zknd")]
#[rustc_legacy_const_generics(2)]
// See #1464
// #[cfg_attr(test, assert_instr(aes32dsmi, BS = 0))]
#[inline]
Expand Down
1 change: 0 additions & 1 deletion crates/core_arch/src/riscv64/zk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,6 @@ pub fn aes64dsm(rs1: u64, rs2: u64) -> u64 {
///
/// The `RNUM` parameter is expected to be a constant value inside the range of `0..=10`.
#[target_feature(enable = "zkne_or_zknd")]
#[rustc_legacy_const_generics(1)]
#[inline]
#[unstable(feature = "riscv_ext_intrinsics", issue = "114544")]
pub fn aes64ks1i<const RNUM: u8>(rs1: u64) -> u64 {
Expand Down
20 changes: 9 additions & 11 deletions crates/core_arch/src/riscv_shared/zk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,24 +169,23 @@ pub fn sha256sum1(rs1: u32) -> u32 {
/// C = L(B) = B ⊕ (B ≪ 2) ⊕ (B ≪ 10) ⊕ (B ≪ 18) ⊕ (B ≪ 24)
/// ```
///
/// It can be implemented by `sm4ed` instruction like:
/// It can be implemented by the `sm4ed` instruction like this:
///
/// ```no_run
/// # #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))]
/// # fn round_function(x0: u32, x1: u32, x2: u32, x3: u32, rk: u32) -> u32 {
/// # #[cfg(target_arch = "riscv32")] use core::arch::riscv32::sm4ed;
/// # #[cfg(target_arch = "riscv64")] use core::arch::riscv64::sm4ed;
/// let a = x1 ^ x2 ^ x3 ^ rk;
/// let c0 = sm4ed(x0, a, 0);
/// let c1 = sm4ed(c0, a, 1); // c1 represents c[0..=1], etc.
/// let c2 = sm4ed(c1, a, 2);
/// let c3 = sm4ed(c2, a, 3);
/// let c0 = sm4ed::<0>(x0, a);
/// let c1 = sm4ed::<1>(c0, a); // c1 represents c[0..=1], etc.
/// let c2 = sm4ed::<2>(c1, a);
/// let c3 = sm4ed::<3>(c2, a);
/// return c3; // c3 represents c[0..=3]
/// # }
/// ```
#[unstable(feature = "riscv_ext_intrinsics", issue = "114544")]
#[target_feature(enable = "zksed")]
#[rustc_legacy_const_generics(2)]
#[cfg_attr(test, assert_instr(sm4ed, BS = 0))]
#[inline]
pub fn sm4ed<const BS: u8>(rs1: u32, rs2: u32) -> u32 {
Expand Down Expand Up @@ -253,16 +252,15 @@ pub fn sm4ed<const BS: u8>(rs1: u32, rs2: u32) -> u32 {
/// # #[cfg(target_arch = "riscv32")] use core::arch::riscv32::sm4ed;
/// # #[cfg(target_arch = "riscv64")] use core::arch::riscv64::sm4ed;
/// let a = x1 ^ x2 ^ x3 ^ rk;
/// let c0 = sm4ed(x0, a, 0);
/// let c1 = sm4ed(c0, a, 1); // c1 represents c[0..=1], etc.
/// let c2 = sm4ed(c1, a, 2);
/// let c3 = sm4ed(c2, a, 3);
/// let c0 = sm4ed::<0>(x0, a);
/// let c1 = sm4ed::<1>(c0, a); // c1 represents c[0..=1], etc.
/// let c2 = sm4ed::<2>(c1, a);
/// let c3 = sm4ed::<3>(c2, a);
/// return c3; // c3 represents c[0..=3]
/// # }
/// ```
#[unstable(feature = "riscv_ext_intrinsics", issue = "114544")]
#[target_feature(enable = "zksed")]
#[rustc_legacy_const_generics(2)]
#[cfg_attr(test, assert_instr(sm4ks, BS = 0))]
#[inline]
pub fn sm4ks<const BS: u8>(rs1: u32, rs2: u32) -> u32 {
Expand Down
11 changes: 0 additions & 11 deletions crates/stdarch-gen-hexagon/src/scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,17 +521,6 @@ fn generate_functions(intrinsics: &[ScalarIntrinsic]) -> String {
output.push_str("#[inline(always)]\n");
}

// Immediate parameters become const generics but are passed as positional
// arguments at the call site: Q6_R_add_RI(rs, 42) rather than Q6_R_add_RI::<42>(rs).
// This matches the assembly syntax where the immediate is an operand.
if !imm_params.is_empty() {
let indices: Vec<String> = imm_params.iter().map(|(i, _, _)| i.to_string()).collect();
output.push_str(&format!(
"#[rustc_legacy_const_generics({})]\n",
indices.join(", ")
));
}

if let Some(instr) = extract_instr_name(&info.asm_syntax) {
if imm_params.is_empty() {
output.push_str(&format!("#[cfg_attr(test, assert_instr({}))]\n", instr));
Expand Down
30 changes: 7 additions & 23 deletions crates/stdarch-verify/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,37 +110,21 @@ fn functions(input: TokenStream, dirs: &[&str]) -> TokenStream {
quote! { None }
};

let required_const = find_required_const("rustc_args_required_const", &f.attrs);
let mut legacy_const_generics =
find_required_const("rustc_legacy_const_generics", &f.attrs);
if !required_const.is_empty() && !legacy_const_generics.is_empty() {
panic!(
"Can't have both #[rustc_args_required_const] and \
#[rustc_legacy_const_generics]"
);
}
let mut legacy_const_generics = find_legacy_const_generics(&f.attrs);

// Newer intrinsics don't have `rustc_legacy_const_generics` - assume they belong at
// the end of the argument list
if required_const.is_empty() && legacy_const_generics.is_empty() {
if legacy_const_generics.is_empty() {
legacy_const_generics =
(arguments.len()..(arguments.len() + const_arguments.len())).collect();
}

// The list of required consts, used to verify the arguments, comes from either the
// `rustc_args_required_const` or the `rustc_legacy_const_generics` attribute.
let required_const = if required_const.is_empty() {
legacy_const_generics.clone()
} else {
required_const
};

legacy_const_generics.sort();
for (idx, ty) in legacy_const_generics
.into_iter()
.iter()
.zip(const_arguments.into_iter())
{
arguments.insert(idx, ty);
arguments.insert(*idx, ty);
}

// Strip leading underscore from fn name when building a test
Expand All @@ -162,7 +146,7 @@ fn functions(input: TokenStream, dirs: &[&str]) -> TokenStream {
target_feature: #target_feature,
instrs: &[#(#instrs),*],
file: stringify!(#path),
required_const: &[#(#required_const),*],
legacy_const_generics: &[#(#legacy_const_generics),*],
has_test: #has_test,
doc: #doc
}
Expand Down Expand Up @@ -596,7 +580,7 @@ fn find_doc(attrs: &[syn::Attribute]) -> String {
.collect()
}

fn find_required_const(name: &str, attrs: &[syn::Attribute]) -> Vec<usize> {
fn find_legacy_const_generics(attrs: &[syn::Attribute]) -> Vec<usize> {
attrs
.iter()
.filter_map(|a| {
Expand All @@ -607,7 +591,7 @@ fn find_required_const(name: &str, attrs: &[syn::Attribute]) -> Vec<usize> {
}
})
.flat_map(|l| {
if l.path.segments[0].ident == name {
if l.path.segments[0].ident == "rustc_legacy_const_generics" {
syn::parse2::<RustcArgsRequiredConst>(l.tokens.clone())
.unwrap()
.args
Expand Down
10 changes: 6 additions & 4 deletions crates/stdarch-verify/tests/arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ struct Function {
target_feature: Option<&'static str>,
instrs: &'static [&'static str],
file: &'static str,
required_const: &'static [usize],
legacy_const_generics: &'static [usize],
has_test: bool,
doc: &'static str,
}
Expand Down Expand Up @@ -333,7 +333,9 @@ fn matches(rust: &Function, arm: &Intrinsic) -> Result<(), String> {
// SVE uses generic type parameters to handle void pointers
(Type::ConstPtr(Type::GenericParam("T")), Type::ConstPtr(Type::Void)) => (),
// SVE const generics use i32 over u64 for usability reasons
(Type::PrimSigned(32), Type::PrimUnsigned(64)) if rust.required_const.contains(&i) => {
(Type::PrimSigned(32), Type::PrimUnsigned(64))
if rust.legacy_const_generics.contains(&i) =>
{
()
}
// svset doesn't have its const argument last as we assumed when building the Function
Expand All @@ -343,12 +345,12 @@ fn matches(rust: &Function, arm: &Intrinsic) -> Result<(), String> {
}
if *arm_const {
nconst += 1;
if !rust.required_const.contains(&i) && !rust.name.starts_with("svset") {
if !rust.legacy_const_generics.contains(&i) && !rust.name.starts_with("svset") {
bail!("argument const mismatch");
}
}
}
if nconst != rust.required_const.len() {
if nconst != rust.legacy_const_generics.len() {
bail!("wrong number of const arguments");
}

Expand Down
48 changes: 29 additions & 19 deletions crates/stdarch-verify/tests/mips.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct Function {
target_feature: Option<&'static str>,
instrs: &'static [&'static str],
file: &'static str,
required_const: &'static [usize],
legacy_const_generics: &'static [usize],
has_test: bool,
doc: &'static str,
}
Expand Down Expand Up @@ -94,6 +94,26 @@ enum MsaTy {
MutVoidPtr,
}

impl MsaTy {
fn is_const(&self) -> bool {
matches!(
self,
MsaTy::imm0_1
| MsaTy::imm0_3
| MsaTy::imm0_7
| MsaTy::imm0_15
| MsaTy::imm0_31
| MsaTy::imm0_63
| MsaTy::imm0_255
| MsaTy::imm_n16_15
| MsaTy::imm_n512_511
| MsaTy::imm_n1024_1022
| MsaTy::imm_n2048_2044
| MsaTy::imm_n4096_4088
)
}
}

impl<'a> From<&'a str> for MsaTy {
fn from(s: &'a str) -> MsaTy {
match s {
Expand Down Expand Up @@ -169,6 +189,11 @@ impl std::convert::TryFrom<&'static str> for MsaIntrinsic {
arg_tys.push(MsaTy::from(arg));
}

// Some of the functions extracted from mips-msa.h have immediate arguments between non-immediate arguments,
// but Rust const generics are not function arguments and cannot be expressed in a way that matches the header.
// For the sake of verification, rust const generics are treated as "the last arguments to a function".
arg_tys.sort_by(|arg1, arg2| arg1.is_const().cmp(&arg2.is_const()));

Comment on lines +192 to +196

@TechnoPorg TechnoPorg Oct 12, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels gross, but then again, so does having const arguments in the middle of other non-const arguments 🤷‍♀️

View changes since the review

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree but we can't really do better. It's fine

// The instruction is the intrinsic name without the __msa_ prefix.
let instruction = &id[6..];
let mut instruction = instruction.to_string();
Expand Down Expand Up @@ -313,30 +338,15 @@ fn matches(rust: &Function, mips: &MsaIntrinsic) -> Result<(), String> {
),
}

let is_const = matches!(
mips_arg,
MsaTy::imm0_1
| MsaTy::imm0_3
| MsaTy::imm0_7
| MsaTy::imm0_15
| MsaTy::imm0_31
| MsaTy::imm0_63
| MsaTy::imm0_255
| MsaTy::imm_n16_15
| MsaTy::imm_n512_511
| MsaTy::imm_n1024_1022
| MsaTy::imm_n2048_2044
| MsaTy::imm_n4096_4088
);
if is_const {
if mips_arg.is_const() {
nconst += 1;
if !rust.required_const.contains(&i) {
if !rust.legacy_const_generics.contains(&i) {
bail!("argument const mismatch");
}
}
}

if nconst != rust.required_const.len() {
if nconst != rust.legacy_const_generics.len() {
bail!("wrong number of const arguments");
}

Expand Down
4 changes: 2 additions & 2 deletions crates/stdarch-verify/tests/x86-intel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ struct Function {
target_feature: Option<&'static str>,
instrs: &'static [&'static str],
file: &'static str,
required_const: &'static [usize],
legacy_const_generics: &'static [usize],
has_test: bool,
doc: &'static str,
}
Expand Down Expand Up @@ -555,7 +555,7 @@ fn matches(rust: &Function, intel: &Intrinsic) -> Result<(), String> {
bail!("wrong number of arguments on {}", rust.name);
}
for (i, (a, b)) in intel.parameters.iter().zip(rust.arguments).enumerate() {
let is_const = rust.required_const.contains(&i);
let is_const = rust.legacy_const_generics.contains(&i);
equate(b, &a.type_, &a.etype, &intel, is_const)?;
}
}
Expand Down
Loading