From 100dc2dd0680383e765f435931d3f2e45f9f9f46 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 7 May 2026 13:09:16 +0100 Subject: [PATCH 1/3] intrinsic-test: simplify `ArmIntrinsicType` `ArmIntrinsicType` does not need a `target` field and `parse_intrinsic_type` can be a free function that returns an `IntrinsicType` which can be wrapped in an `ArmIntrinsicType` by the caller. --- crates/intrinsic-test/src/arm/intrinsic.rs | 9 +- crates/intrinsic-test/src/arm/json_parser.rs | 19 +-- crates/intrinsic-test/src/arm/mod.rs | 4 +- crates/intrinsic-test/src/arm/types.rs | 130 +++++++++---------- 4 files changed, 75 insertions(+), 87 deletions(-) diff --git a/crates/intrinsic-test/src/arm/intrinsic.rs b/crates/intrinsic-test/src/arm/intrinsic.rs index 29343bee4c..a54e585719 100644 --- a/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/crates/intrinsic-test/src/arm/intrinsic.rs @@ -2,21 +2,18 @@ use crate::common::intrinsic_helpers::IntrinsicType; use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] -pub struct ArmIntrinsicType { - pub data: IntrinsicType, - pub target: String, -} +pub struct ArmIntrinsicType(pub IntrinsicType); impl Deref for ArmIntrinsicType { type Target = IntrinsicType; fn deref(&self) -> &Self::Target { - &self.data + &self.0 } } impl DerefMut for ArmIntrinsicType { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.data + &mut self.0 } } diff --git a/crates/intrinsic-test/src/arm/json_parser.rs b/crates/intrinsic-test/src/arm/json_parser.rs index c1563a7364..5a86abdc6d 100644 --- a/crates/intrinsic-test/src/arm/json_parser.rs +++ b/crates/intrinsic-test/src/arm/json_parser.rs @@ -1,4 +1,5 @@ use super::intrinsic::ArmIntrinsicType; +use crate::arm::types::parse_intrinsic_type; use crate::common::argument::{Argument, ArgumentList}; use crate::common::constraint::Constraint; use crate::common::intrinsic::Intrinsic; @@ -58,7 +59,6 @@ struct JsonIntrinsic { pub fn get_neon_intrinsics( filename: &Path, - target: &str, ) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); @@ -68,7 +68,7 @@ pub fn get_neon_intrinsics( .into_iter() .filter_map(|intr| { if intr.simd_isa == "Neon" { - Some(json_to_intrinsic(intr, target).expect("Couldn't parse JSON")) + Some(json_to_intrinsic(intr).expect("Couldn't parse JSON")) } else { None } @@ -79,11 +79,10 @@ pub fn get_neon_intrinsics( fn json_to_intrinsic( mut intr: JsonIntrinsic, - target: &str, ) -> Result, Box> { let name = intr.name.replace(['[', ']'], ""); - let results = ArmIntrinsicType::from_c(&intr.return_type.value, target)?; + let result_ty = ArmIntrinsicType(parse_intrinsic_type(&intr.return_type.value)?); let args = intr .arguments @@ -95,16 +94,18 @@ fn json_to_intrinsic( let metadata = metadata.and_then(|a| a.remove(arg_name)); let arg_prep: Option = metadata.and_then(|a| a.try_into().ok()); let constraint: Option = arg_prep.and_then(|a| a.try_into().ok()); - let ty = ArmIntrinsicType::from_c(type_name, target) - .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); + let arg_ty = ArmIntrinsicType( + parse_intrinsic_type(type_name) + .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")), + ); let mut arg = - Argument::::new(i, String::from(arg_name), ty, constraint); + Argument::::new(i, String::from(arg_name), arg_ty, constraint); // The JSON doesn't list immediates as const let IntrinsicType { ref mut constant, .. - } = arg.ty.data; + } = *arg.ty; if arg.name.starts_with("imm") { *constant = true } @@ -117,7 +118,7 @@ fn json_to_intrinsic( Ok(Intrinsic { name, arguments, - results, + results: result_ty, arch_tags: intr.architectures, }) } diff --git a/crates/intrinsic-test/src/arm/mod.rs b/crates/intrinsic-test/src/arm/mod.rs index 80f5ae17d7..8935b3ca66 100644 --- a/crates/intrinsic-test/src/arm/mod.rs +++ b/crates/intrinsic-test/src/arm/mod.rs @@ -35,8 +35,8 @@ impl SupportedArchitectureTest for ArmArchitectureTest { fn create(cli_options: ProcessedCli) -> Self { let a32 = cli_options.target.starts_with("armv7"); - let mut intrinsics = get_neon_intrinsics(&cli_options.filename, &cli_options.target) - .expect("Error parsing input file"); + let mut intrinsics = + get_neon_intrinsics(&cli_options.filename).expect("Error parsing input file"); intrinsics.sort_by(|a, b| a.name.cmp(&b.name)); intrinsics.dedup(); diff --git a/crates/intrinsic-test/src/arm/types.rs b/crates/intrinsic-test/src/arm/types.rs index e9614eba21..40035b35d5 100644 --- a/crates/intrinsic-test/src/arm/types.rs +++ b/crates/intrinsic-test/src/arm/types.rs @@ -42,7 +42,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { simd_len, vec_len, .. - } = &self.data + } = **self { let quad = if simd_len.unwrap_or(1) * bl > 64 { "q" @@ -69,79 +69,69 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { } } -impl ArmIntrinsicType { - pub fn from_c(s: &str, target: &str) -> Result { - const CONST_STR: &str = "const"; - if let Some(s) = s.strip_suffix('*') { - let (s, constant) = match s.trim().strip_suffix(CONST_STR) { - Some(stripped) => (stripped, true), - None => (s, false), +pub fn parse_intrinsic_type(s: &str) -> Result { + const CONST_STR: &str = "const"; + if let Some(s) = s.strip_suffix('*') { + let (s, constant) = match s.trim().strip_suffix(CONST_STR) { + Some(stripped) => (stripped, true), + None => (s, false), + }; + let s = s.trim_end(); + let mut ty = parse_intrinsic_type(s)?; + ty.ptr = true; + ty.ptr_constant = constant; + Ok(ty) + } else { + // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t] + let (mut s, constant) = match s.strip_prefix(CONST_STR) { + Some(stripped) => (stripped.trim(), true), + None => (s, false), + }; + s = s.strip_suffix("_t").unwrap_or(s); + let mut parts = s.split('x'); // [[{bitlen}], [{simdlen}], [{vec_len}] ] + let start = parts.next().ok_or("Impossible to parse type")?; + if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) { + let (arg_kind, bit_len) = start.split_at(digit_start); + let arg_kind = arg_kind.parse::()?; + let bit_len = bit_len.parse::().map_err(|err| err.to_string())?; + let simd_len = match parts.next() { + Some(part) => Some( + part.parse::() + .map_err(|_| "Couldn't parse simd_len: {part}")?, + ), + None => None, }; - let s = s.trim_end(); - let temp_return = ArmIntrinsicType::from_c(s, target); - temp_return.map(|mut op| { - op.ptr = true; - op.ptr_constant = constant; - op + let vec_len = match parts.next() { + Some(part) => Some( + part.parse::() + .map_err(|_| "Couldn't parse vec_len: {part}")?, + ), + None => None, + }; + Ok(IntrinsicType { + ptr: false, + ptr_constant: false, + constant, + kind: arg_kind, + bit_len: Some(bit_len), + simd_len, + vec_len, }) } else { - // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t] - let (mut s, constant) = match s.strip_prefix(CONST_STR) { - Some(stripped) => (stripped.trim(), true), - None => (s, false), + let kind = start.parse::()?; + let bit_len = match kind { + TypeKind::Int(_) => Some(32), + _ => None, }; - s = s.strip_suffix("_t").unwrap_or(s); - let mut parts = s.split('x'); // [[{bitlen}], [{simdlen}], [{vec_len}] ] - let start = parts.next().ok_or("Impossible to parse type")?; - if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) { - let (arg_kind, bit_len) = start.split_at(digit_start); - let arg_kind = arg_kind.parse::()?; - let bit_len = bit_len.parse::().map_err(|err| err.to_string())?; - let simd_len = match parts.next() { - Some(part) => Some( - part.parse::() - .map_err(|_| "Couldn't parse simd_len: {part}")?, - ), - None => None, - }; - let vec_len = match parts.next() { - Some(part) => Some( - part.parse::() - .map_err(|_| "Couldn't parse vec_len: {part}")?, - ), - None => None, - }; - Ok(ArmIntrinsicType { - data: IntrinsicType { - ptr: false, - ptr_constant: false, - constant, - kind: arg_kind, - bit_len: Some(bit_len), - simd_len, - vec_len, - }, - target: target.to_string(), - }) - } else { - let kind = start.parse::()?; - let bit_len = match kind { - TypeKind::Int(_) => Some(32), - _ => None, - }; - Ok(ArmIntrinsicType { - data: IntrinsicType { - ptr: false, - ptr_constant: false, - constant, - kind: start.parse::()?, - bit_len, - simd_len: None, - vec_len: None, - }, - target: target.to_string(), - }) - } + Ok(IntrinsicType { + ptr: false, + ptr_constant: false, + constant, + kind: start.parse::()?, + bit_len, + simd_len: None, + vec_len: None, + }) } } } From 038aec8460ae10d1a3de5e3674b1bfc746d75881 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 7 May 2026 15:05:56 +0000 Subject: [PATCH 2/3] intrinsic-test: parsing of SVE types Updates `parse_intrinsic_type` to support SVE intrinsic types, with the limited changes required to `IntrinsicType` and relevant users of the `simd_len` to preserve existing behaviour, assuming the tool never attempts to generate tests for SVE intrinsics (which it doesn't yet) --- crates/intrinsic-test/src/arm/types.rs | 177 +++++++++++------- .../src/common/intrinsic_helpers.rs | 78 +++++--- crates/intrinsic-test/src/x86/types.rs | 8 +- 3 files changed, 170 insertions(+), 93 deletions(-) diff --git a/crates/intrinsic-test/src/arm/types.rs b/crates/intrinsic-test/src/arm/types.rs index 40035b35d5..cd420f1067 100644 --- a/crates/intrinsic-test/src/arm/types.rs +++ b/crates/intrinsic-test/src/arm/types.rs @@ -1,5 +1,7 @@ use super::intrinsic::ArmIntrinsicType; -use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind}; +use crate::common::intrinsic_helpers::{ + IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, +}; impl IntrinsicTypeDefinition for ArmIntrinsicType { /// Gets a string containing the typename for this type in C format. @@ -9,8 +11,14 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { if let Some(bit_len) = self.bit_len { match (self.simd_len, self.vec_len) { (None, None) => format!("{prefix}{bit_len}_t"), - (Some(simd), None) => format!("{prefix}{bit_len}x{simd}_t"), - (Some(simd), Some(vec)) => format!("{prefix}{bit_len}x{simd}x{vec}_t"), + (Some(SimdLen::Fixed(simd)), None) => format!("{prefix}{bit_len}x{simd}_t"), + (Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{prefix}{bit_len}x{simd}x{vec}_t") + } + (Some(SimdLen::Scalable), None) => format!("sv{prefix}{bit_len}_t"), + (Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{prefix}{bit_len}x{vec}_t") + } (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case } } else { @@ -25,8 +33,14 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { if let Some(bit_len) = self.bit_len { match (self.simd_len, self.vec_len) { (None, None) => format!("{rust_prefix}{bit_len}"), - (Some(simd), None) => format!("{c_prefix}{bit_len}x{simd}_t"), - (Some(simd), Some(vec)) => format!("{c_prefix}{bit_len}x{simd}x{vec}_t"), + (Some(SimdLen::Fixed(simd)), None) => format!("{c_prefix}{bit_len}x{simd}_t"), + (Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{c_prefix}{bit_len}x{simd}x{vec}_t") + } + (Some(SimdLen::Scalable), None) => format!("sv{c_prefix}{bit_len}_t"), + (Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{c_prefix}{bit_len}x{vec}_t") + } (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case } } else { @@ -39,16 +53,11 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { if let IntrinsicType { kind: k, bit_len: Some(bl), - simd_len, vec_len, .. } = **self { - let quad = if simd_len.unwrap_or(1) * bl > 64 { - "q" - } else { - "" - }; + let quad = if self.num_lanes() * bl > 64 { "q" } else { "" }; format!( "vld{len}{quad}_{type}{size}", @@ -71,67 +80,97 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { pub fn parse_intrinsic_type(s: &str) -> Result { const CONST_STR: &str = "const"; + const ENUM_STR: &str = "enum "; + + // Recurse to handle pointers.. if let Some(s) = s.strip_suffix('*') { - let (s, constant) = match s.trim().strip_suffix(CONST_STR) { - Some(stripped) => (stripped, true), - None => (s, false), + let s = s.trim(); + let (s, constant) = if s.ends_with(CONST_STR) || s.starts_with(CONST_STR) { + ( + s.trim_start_matches(CONST_STR).trim_end_matches(CONST_STR), + true, + ) + } else { + (s, false) }; - let s = s.trim_end(); - let mut ty = parse_intrinsic_type(s)?; + + let mut ty = parse_intrinsic_type(s.trim())?; ty.ptr = true; ty.ptr_constant = constant; - Ok(ty) - } else { - // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t] - let (mut s, constant) = match s.strip_prefix(CONST_STR) { - Some(stripped) => (stripped.trim(), true), - None => (s, false), - }; - s = s.strip_suffix("_t").unwrap_or(s); - let mut parts = s.split('x'); // [[{bitlen}], [{simdlen}], [{vec_len}] ] - let start = parts.next().ok_or("Impossible to parse type")?; - if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) { - let (arg_kind, bit_len) = start.split_at(digit_start); - let arg_kind = arg_kind.parse::()?; - let bit_len = bit_len.parse::().map_err(|err| err.to_string())?; - let simd_len = match parts.next() { - Some(part) => Some( - part.parse::() - .map_err(|_| "Couldn't parse simd_len: {part}")?, - ), - None => None, - }; - let vec_len = match parts.next() { - Some(part) => Some( - part.parse::() - .map_err(|_| "Couldn't parse vec_len: {part}")?, - ), - None => None, - }; - Ok(IntrinsicType { - ptr: false, - ptr_constant: false, - constant, - kind: arg_kind, - bit_len: Some(bit_len), - simd_len, - vec_len, - }) - } else { - let kind = start.parse::()?; - let bit_len = match kind { - TypeKind::Int(_) => Some(32), - _ => None, - }; - Ok(IntrinsicType { - ptr: false, - ptr_constant: false, - constant, - kind: start.parse::()?, - bit_len, - simd_len: None, - vec_len: None, - }) - } + return Ok(ty); } + + // [const ][sv]TYPE[{element_bits}[x{num_lanes}[x{num_vecs}]]][_t] + // | [enum ]TYPE + let (mut s, constant) = match (s.strip_prefix(CONST_STR), s.strip_prefix(ENUM_STR)) { + (Some(const_strip), _) => (const_strip, true), + (_, Some(enum_strip)) => (enum_strip, true), + (None, None) => (s, false), + }; + s = s.trim(); + s = s.strip_suffix("_t").unwrap_or(s); + + // Consider the following types as examples: + // A) `svuint32x3_t` + // B) `float16x4x2_t` + // C) `svbool_t` + + let sve = s.starts_with("sv"); + + let mut parts = s.split('x'); + let start = parts.next().ok_or("failed to parse type")?; + + // Continuing the previous examples.. + // A) kind=TypeKind::Int(Sign::Unsigned), bit_len=Some(32) + // B) kind=TypeKind::Float, bit_len=Some(16) + // C) kind=TypeKind::Bool, bit_len=None + let (kind, bit_len) = if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) { + let (element_kind, element_bits) = start.split_at(digit_start); + let element_kind = element_kind.parse::()?; + let element_bits = element_bits.parse::().map_err(|err| err.to_string())?; + (element_kind, Some(element_bits)) + } else { + let element_kind = start.parse::()?; + (element_kind, None) + }; + + let bit_len = match (bit_len, kind) { + (None, TypeKind::SvPattern | TypeKind::SvPrefetchOp | TypeKind::Int(_)) => Some(32), + (None, TypeKind::Bool) => Some(8), + _ => bit_len, + }; + + // Continuing the previous examples.. + // A) second_len=Some(3) + // B) second_len=Some(4) + // C) second_len=None + let second_len = parts.next().map(|part| { + part.parse::() + .expect("failed to parse second part of type") + }); + + // Continuing the previous examples.. + // A) third_len=None + // B) third_len=Some(2) + // C) third_len=None + let third_len = parts.next().map(|part| { + part.parse::() + .expect("failed to parse third part of type") + }); + + let (simd_len, vec_len) = if sve { + (Some(SimdLen::Scalable), second_len) + } else { + (second_len.map(SimdLen::Fixed), third_len) + }; + + Ok(IntrinsicType { + ptr: false, + ptr_constant: false, + constant, + kind, + bit_len, + simd_len, + vec_len, + }) } diff --git a/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/crates/intrinsic-test/src/common/intrinsic_helpers.rs index cb8740fa07..a894d5c016 100644 --- a/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -15,6 +15,7 @@ pub enum Sign { #[derive(Debug, PartialEq, Copy, Clone)] pub enum TypeKind { + Bool, BFloat, Float, Int(Sign), @@ -23,6 +24,8 @@ pub enum TypeKind { Void, Mask, Vector, + SvPattern, + SvPrefetchOp, } impl FromStr for TypeKind { @@ -30,17 +33,22 @@ impl FromStr for TypeKind { fn from_str(s: &str) -> Result { match s { - "bfloat" | "BF16" => Ok(Self::BFloat), - "float" | "double" | "FP16" | "FP32" | "FP64" => Ok(Self::Float), - "int" | "long" | "short" | "SI8" | "SI16" | "SI32" | "SI64" => { + "svbool" | "bool" => Ok(Self::Bool), + "svbfloat" | "bfloat" | "BF16" => Ok(Self::BFloat), + "svfloat" | "float" | "double" | "FP16" | "FP32" | "FP64" => Ok(Self::Float), + "svint" | "int" | "long" | "short" | "SI8" | "SI16" | "SI32" | "SI64" => { Ok(Self::Int(Sign::Signed)) } "poly" => Ok(Self::Poly), "char" => Ok(Self::Char(Sign::Signed)), - "uint" | "unsigned" | "UI8" | "UI16" | "UI32" | "UI64" => Ok(Self::Int(Sign::Unsigned)), + "svuint" | "uint" | "unsigned" | "UI8" | "UI16" | "UI32" | "UI64" => { + Ok(Self::Int(Sign::Unsigned)) + } "void" => Ok(Self::Void), "MASK" => Ok(Self::Mask), "M128" | "M256" | "M512" => Ok(Self::Vector), + "svpattern" => Ok(Self::SvPattern), + "svprfop" => Ok(Self::SvPrefetchOp), _ => Err(format!("Impossible to parse argument kind {s}")), } } @@ -52,6 +60,7 @@ impl fmt::Display for TypeKind { f, "{}", match self { + Self::Bool => "bool", Self::BFloat => "bfloat", Self::Float => "float", Self::Int(Sign::Signed) => "int", @@ -62,6 +71,8 @@ impl fmt::Display for TypeKind { Self::Char(Sign::Unsigned) => "unsigned char", Self::Mask => "mask", Self::Vector => "vector", + Self::SvPattern => "svpattern", + Self::SvPrefetchOp => "svprfop", } ) } @@ -71,6 +82,7 @@ impl TypeKind { /// Returns the type component of a C typedef for a type of the form of `{type}{size}_t` pub fn c_prefix(&self) -> &str { match self { + Self::Bool => "bool", Self::Float => "float", Self::Int(Sign::Signed) => "int", Self::Int(Sign::Unsigned) => "uint", @@ -98,6 +110,21 @@ impl TypeKind { } } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum SimdLen { + Scalable, + Fixed(u32), +} + +impl std::fmt::Display for SimdLen { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Scalable => unimplemented!(), + Self::Fixed(len) => ::fmt(len, f), + } + } +} + #[derive(Debug, PartialEq, Clone)] pub struct IntrinsicType { /// Is this an immediate? @@ -115,13 +142,13 @@ pub struct IntrinsicType { /// Number of bits of this type (e.g. 32 for `u32`). pub bit_len: Option, - /// Length of a SIMD vector (i.e. 4 for `uint32x4_t`). + /// Length of a SIMD vector (i.e. `Fixed(4)` for `uint32x4_t`). /// /// A value of `None` means this is not a SIMD type. The number of lanes of a type with /// `simd_len=None` can be assumed to be one, though it is important to maintain a distinction - /// between `simd_len=None` and `simd_len=Some(1)` so as to differentiate between `u64` and - /// `uint64x1_t`. - pub simd_len: Option, + /// between `simd_len=None` and `simd_len=Some(Fixed(1))` so as to differentiate between `u64` + /// and `uint64x1_t`. A value of `Some(Scalable)` indicates that this is a scalable vector. + pub simd_len: Option, /// Number of rows of a SIMD matrix (i.e. 2 for `uint8x8x2_t`). /// @@ -147,7 +174,13 @@ impl IntrinsicType { /// Returns the number of lanes of the type pub fn num_lanes(&self) -> u32 { - self.simd_len.unwrap_or(1) + self.simd_len + .as_ref() + .map(|len| match len { + SimdLen::Scalable => unimplemented!(), + SimdLen::Fixed(len) => *len, + }) + .unwrap_or(1) } /// Returns the number of vectors of the type @@ -179,14 +212,14 @@ impl IntrinsicType { bit_len: Some(bit_len @ (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 16 | 32 | 64)), kind: kind @ (TypeKind::Int(_) | TypeKind::Poly | TypeKind::Char(_) | TypeKind::Mask), - simd_len, vec_len, .. } => { format!( "[\n{body}\n]", - body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1)) - .format_with(",\n", |i, fmt| { + body = (0..(self.num_lanes() * vec_len.unwrap_or(1) + loads - 1)).format_with( + ",\n", + |i, fmt| { let src = value_for_array(*bit_len, i); assert!(src == 0 || src.ilog2() < *bit_len); if *kind == TypeKind::Int(Sign::Signed) && (src >> (*bit_len - 1)) != 0 @@ -199,37 +232,39 @@ impl IntrinsicType { } else { fmt(&format_args!("{src:#x}")) } - }) + } + ) ) } IntrinsicType { kind: TypeKind::Float, bit_len: Some(bit_len @ (16 | 32 | 64)), - simd_len, vec_len, .. } => { format!( "[\n{body}\n]", - body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1)) - .format_with(",\n", |i, fmt| fmt(&format_args!( + body = (0..(self.num_lanes() * vec_len.unwrap_or(1) + loads - 1)).format_with( + ",\n", + |i, fmt| fmt(&format_args!( "f{bit_len}::from_bits({src:#x})", src = value_for_array(*bit_len, i) - ))) + )) + ) ) } IntrinsicType { kind: TypeKind::Vector, bit_len: Some(128 | 256 | 512), - simd_len, vec_len, .. } => { let effective_bit_len = 32; format!( "[\n{body}\n]", - body = (0..(vec_len.unwrap_or(1) * simd_len.unwrap_or(1) + loads - 1)) - .format_with(",\n", |i, fmt| { + body = (0..(vec_len.unwrap_or(1) * self.num_lanes() + loads - 1)).format_with( + ",\n", + |i, fmt| { let src = value_for_array(effective_bit_len, i); assert!(src == 0 || src.ilog2() < effective_bit_len); if (src >> (effective_bit_len - 1)) != 0 { @@ -241,7 +276,8 @@ impl IntrinsicType { } else { fmt(&format_args!("{src:#x}")) } - }) + } + ) ) } _ => unimplemented!("populate random: {self:#?}"), diff --git a/crates/intrinsic-test/src/x86/types.rs b/crates/intrinsic-test/src/x86/types.rs index c6ea15e150..a0e14c77d6 100644 --- a/crates/intrinsic-test/src/x86/types.rs +++ b/crates/intrinsic-test/src/x86/types.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use itertools::Itertools; use super::intrinsic::X86IntrinsicType; -use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind}; +use crate::common::intrinsic_helpers::{ + IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, +}; use crate::x86::xml_parser::Parameter; impl IntrinsicTypeDefinition for X86IntrinsicType { @@ -187,7 +189,7 @@ impl X86IntrinsicType { Ok(num_bits) => self .data .bit_len - .and_then(|bit_len| Some(num_bits / bit_len)), + .and_then(|bit_len| Some(SimdLen::Fixed(num_bits / bit_len))), Err(_) => None, }; } @@ -297,7 +299,7 @@ impl X86IntrinsicType { // - _mm512_conj_pch if param.type_data == "__m512h" && param.etype == "FP32" { data.bit_len = Some(16); - data.simd_len = Some(32); + data.simd_len = Some(SimdLen::Fixed(32)); } let mut result = X86IntrinsicType { From a009d5d32c15d24d854464f463b8475ac4893447 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 8 May 2026 15:30:20 +0000 Subject: [PATCH 3/3] intrinsic-test: add SVE-specific constraints --- crates/intrinsic-test/src/arm/json_parser.rs | 35 ++++++++++++++----- .../intrinsic-test/src/common/constraint.rs | 27 +++++++++++--- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/crates/intrinsic-test/src/arm/json_parser.rs b/crates/intrinsic-test/src/arm/json_parser.rs index 5a86abdc6d..fb0d16154b 100644 --- a/crates/intrinsic-test/src/arm/json_parser.rs +++ b/crates/intrinsic-test/src/arm/json_parser.rs @@ -3,7 +3,7 @@ use crate::arm::types::parse_intrinsic_type; use crate::common::argument::{Argument, ArgumentList}; use crate::common::constraint::Constraint; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::IntrinsicType; +use crate::common::intrinsic_helpers::{IntrinsicType, TypeKind}; use serde::Deserialize; use serde_json::Value; use std::collections::HashMap; @@ -90,18 +90,37 @@ fn json_to_intrinsic( .enumerate() .map(|(i, arg)| { let (type_name, arg_name) = Argument::::type_and_name_from_c(&arg); + + let arg_ty = parse_intrinsic_type(type_name) + .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); + let metadata = intr.args_prep.as_mut(); let metadata = metadata.and_then(|a| a.remove(arg_name)); let arg_prep: Option = metadata.and_then(|a| a.try_into().ok()); - let constraint: Option = arg_prep.and_then(|a| a.try_into().ok()); - let arg_ty = ArmIntrinsicType( - parse_intrinsic_type(type_name) - .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")), + let constraint: Option = + arg_prep.and_then(|a| a.try_into().ok()).or_else(|| { + if arg_ty.kind() == TypeKind::SvPattern { + Some(Constraint::SvPattern) + } else if arg_ty.kind() == TypeKind::SvPrefetchOp { + Some(Constraint::SvPrefetchOp) + } else if arg_name == "imm_rotation" { + if name.starts_with("svcadd_") || name.starts_with("svqcadd_") { + Some(Constraint::SvImmRotationAdd) + } else { + Some(Constraint::SvImmRotation) + } + } else { + None + } + }); + + let mut arg = Argument::::new( + i, + String::from(arg_name), + ArmIntrinsicType(arg_ty), + constraint, ); - let mut arg = - Argument::::new(i, String::from(arg_name), arg_ty, constraint); - // The JSON doesn't list immediates as const let IntrinsicType { ref mut constant, .. diff --git a/crates/intrinsic-test/src/common/constraint.rs b/crates/intrinsic-test/src/common/constraint.rs index c78eb3541c..ab52d866ab 100644 --- a/crates/intrinsic-test/src/common/constraint.rs +++ b/crates/intrinsic-test/src/common/constraint.rs @@ -10,15 +10,34 @@ pub enum Constraint { Range(Range), /// Test discrete values, e.g. `vec![1, 2, 4, 8]`. Set(Vec), + /// Values of `core::arch::aarch64::svpattern` + SvPattern, + /// Values of `core::arch::aarch64::svprfop` + SvPrefetchOp, + // Values of the `imm_rotation` argument in SVE intrinsics where arguments contain complex + // pairs and `imm_rotation` corresponds to the rotation. + SvImmRotation, + // Values of the `imm_rotation` argument in SVE intrinsics where arguments contain complex + // pairs and `imm_rotation` corresponds to the rotation (this variant is specifically for + // `svcadd` and `svqcadd` where only 90 and 270 are valid arguments). + SvImmRotationAdd, } impl Constraint { /// Returns an iterator over the values of this constraint - pub fn iter<'a>(&'a self) -> impl Iterator + 'a { + pub fn iter(&self) -> Box + '_> { match self { - Constraint::Equal(i) => std::slice::Iter::default().copied().chain(*i..*i + 1), - Constraint::Range(range) => std::slice::Iter::default().copied().chain(range.clone()), - Constraint::Set(items) => items.iter().copied().chain(std::ops::Range::default()), + Constraint::Equal(i) => Box::new(std::iter::once(*i)), + Constraint::Range(range) => Box::new(range.clone()), + Constraint::Set(items) => Box::new(items.iter().copied().chain(Range::default())), + // These values are discriminants of the `svpattern` enum + Constraint::SvPattern => Box::new((0..=13).chain(29..=31)), + // These values are discriminants of the `svprfop` enum + Constraint::SvPrefetchOp => Box::new((0..=5).chain(8..=14)), + // Valid rotations for intrinsics operating on complex pairs: 0, 90, 180, 270 + Constraint::SvImmRotation => Box::new((0..=270).step_by(90)), + // Valid rotations for `svcadd` and `svqcadd`: 0, 270 + Constraint::SvImmRotationAdd => Box::new((90..=270).step_by(180)), } } }