diff options
| author | Madhav Madhusoodanan <f20200049@pilani.bits-pilani.ac.in> | 2025-04-16 14:31:42 +0530 |
|---|---|---|
| committer | Amanieu d'Antras <amanieu@gmail.com> | 2025-05-27 23:27:38 +0000 |
| commit | 57c357591e22c32865715f4b00bbc66442f0a8d6 (patch) | |
| tree | e43e1a62f3d58dc8b7aec5b1d48978ff0244233c /library/stdarch/crates/intrinsic-test/src | |
| parent | a993b4427cda5a2a5a4d3d34f341c11ee00b3cdd (diff) | |
| download | rust-57c357591e22c32865715f4b00bbc66442f0a8d6.tar.gz rust-57c357591e22c32865715f4b00bbc66442f0a8d6.zip | |
introduced generic types and code refactor
Diffstat (limited to 'library/stdarch/crates/intrinsic-test/src')
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/arm/constraint.rs | 60 | ||||
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/arm/functions.rs | 38 | ||||
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs | 115 | ||||
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs | 36 | ||||
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/arm/mod.rs | 11 | ||||
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/arm/types.rs | 574 | ||||
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/common/argument.rs (renamed from library/stdarch/crates/intrinsic-test/src/arm/argument.rs) | 167 | ||||
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/common/format.rs (renamed from library/stdarch/crates/intrinsic-test/src/arm/format.rs) | 0 | ||||
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs | 91 | ||||
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/common/intrinsic_types.rs | 352 | ||||
| -rw-r--r-- | library/stdarch/crates/intrinsic-test/src/common/mod.rs | 4 |
11 files changed, 808 insertions, 640 deletions
diff --git a/library/stdarch/crates/intrinsic-test/src/arm/constraint.rs b/library/stdarch/crates/intrinsic-test/src/arm/constraint.rs new file mode 100644 index 00000000000..777cbd0099a --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/src/arm/constraint.rs @@ -0,0 +1,60 @@ +use super::json_parser::ArgPrep; + +use crate::common::argument::MetadataDefinition; +use serde::Deserialize; +use serde_json::Value; +use std::ops::Range; + +#[derive(Debug, PartialEq, Clone, Deserialize)] +pub enum Constraint { + Equal(i64), + Range(Range<i64>), +} + +impl Constraint { + pub fn to_range(&self) -> Range<i64> { + match self { + Constraint::Equal(eq) => *eq..*eq + 1, + Constraint::Range(range) => range.clone(), + } + } +} + +impl MetadataDefinition for Constraint { + fn from_metadata(metadata: Option<Value>) -> Vec<Box<Self>> { + let arg_prep: Option<ArgPrep> = metadata.and_then(|a| { + if let Value::Object(_) = a { + a.try_into().ok() + } else { + None + } + }); + let constraint: Option<Constraint> = arg_prep.and_then(|a| a.try_into().ok()); + vec![constraint] + .into_iter() + .filter_map(|a| a) + .map(|a| Box::new(a)) + .collect() + } +} + +/// ARM-specific +impl TryFrom<ArgPrep> for Constraint { + type Error = (); + + fn try_from(prep: ArgPrep) -> Result<Self, Self::Error> { + let parsed_ints = match prep { + ArgPrep::Immediate { min, max } => Ok((min, max)), + _ => Err(()), + }; + if let Ok((min, max)) = parsed_ints { + if min == max { + Ok(Constraint::Equal(min)) + } else { + Ok(Constraint::Range(min..max + 1)) + } + } else { + Err(()) + } + } +} diff --git a/library/stdarch/crates/intrinsic-test/src/arm/functions.rs b/library/stdarch/crates/intrinsic-test/src/arm/functions.rs index a23bac991ff..8158dfd88da 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/functions.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/functions.rs @@ -1,9 +1,12 @@ -use super::argument::Argument; use super::config::{AARCH_CONFIGURATIONS, POLY128_OSTREAM_DEF, build_notices}; -use super::format::Indentation; -use super::intrinsic::Intrinsic; +use super::intrinsic::ArmIntrinsicType; +use crate::arm::constraint::Constraint; +use crate::common::argument::Argument; +use crate::common::format::Indentation; use crate::common::gen_c::{compile_c, create_c_filenames, generate_c_program}; use crate::common::gen_rust::{compile_rust, create_rust_filenames, generate_rust_program}; +use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; +use crate::common::intrinsic_types::IntrinsicTypeDefinition; use crate::common::write_file; use itertools::Itertools; use rayon::prelude::*; @@ -14,14 +17,14 @@ const PASSES: u32 = 20; fn gen_code_c( indentation: Indentation, - intrinsic: &Intrinsic, - constraints: &[&Argument], + intrinsic: &Intrinsic<ArmIntrinsicType, Constraint>, + constraints: &[&Argument<ArmIntrinsicType, Constraint>], name: String, target: &str, ) -> String { if let Some((current, constraints)) = constraints.split_last() { let range = current - .constraints + .metadata .iter() .map(|c| c.to_range()) .flat_map(|r| r.into_iter()); @@ -52,11 +55,15 @@ fn gen_code_c( } } -fn generate_c_program_arm(header_files: &[&str], intrinsic: &Intrinsic, target: &str) -> String { +fn generate_c_program_arm( + header_files: &[&str], + intrinsic: &Intrinsic<ArmIntrinsicType, Constraint>, + target: &str, +) -> String { let constraints = intrinsic .arguments .iter() - .filter(|i| i.has_constraint()) + .filter(|&i| i.has_constraint()) .collect_vec(); let indentation = Indentation::default(); @@ -82,13 +89,13 @@ fn generate_c_program_arm(header_files: &[&str], intrinsic: &Intrinsic, target: fn gen_code_rust( indentation: Indentation, - intrinsic: &Intrinsic, - constraints: &[&Argument], + intrinsic: &Intrinsic<ArmIntrinsicType, Constraint>, + constraints: &[&Argument<ArmIntrinsicType, Constraint>], name: String, ) -> String { if let Some((current, constraints)) = constraints.split_last() { let range = current - .constraints + .metadata .iter() .map(|c| c.to_range()) .flat_map(|r| r.into_iter()); @@ -118,7 +125,10 @@ fn gen_code_rust( } } -fn generate_rust_program_arm(intrinsic: &Intrinsic, target: &str) -> String { +fn generate_rust_program_arm( + intrinsic: &Intrinsic<ArmIntrinsicType, Constraint>, + target: &str, +) -> String { let constraints = intrinsic .arguments .iter() @@ -220,7 +230,7 @@ fn compile_c_arm( } pub fn build_c( - intrinsics: &Vec<Intrinsic>, + intrinsics: &Vec<Intrinsic<ArmIntrinsicType, Constraint>>, compiler: Option<&str>, target: &str, cxx_toolchain_dir: Option<&str>, @@ -252,7 +262,7 @@ pub fn build_c( } pub fn build_rust( - intrinsics: &[Intrinsic], + intrinsics: &[Intrinsic<ArmIntrinsicType, Constraint>], toolchain: Option<&str>, target: &str, linker: Option<&str>, diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index b2d242ab736..126e4712f99 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -1,40 +1,83 @@ -use super::argument::ArgumentList; -use super::format::Indentation; -use super::types::{IntrinsicType, TypeKind}; +use super::constraint::Constraint; +use crate::common::argument::ArgumentList; +use crate::common::format::Indentation; +use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; +use crate::common::intrinsic_types::{ + BaseIntrinsicTypeDefinition, IntrinsicType, IntrinsicTypeDefinition, TypeKind, +}; +use crate::common::types::Language; -/// An intrinsic -#[derive(Debug, PartialEq, Clone)] -pub struct Intrinsic { - /// The function name of this intrinsic. - pub name: String, +#[derive(Debug, Clone, PartialEq)] +pub struct ArmIntrinsicType(pub IntrinsicType); - /// Any arguments for this intrinsic. - pub arguments: ArgumentList, +impl BaseIntrinsicTypeDefinition for ArmIntrinsicType { + fn kind(&self) -> TypeKind { + self.0.kind() + } + fn inner_size(&self) -> u32 { + self.0.inner_size() + } + fn num_lanes(&self) -> u32 { + self.0.num_lanes() + } + fn num_vectors(&self) -> u32 { + self.0.num_vectors() + } + fn is_simd(&self) -> bool { + self.0.is_simd() + } + fn is_ptr(&self) -> bool { + self.0.is_ptr() + } + fn c_scalar_type(&self) -> String { + self.0.c_scalar_type() + } + fn rust_scalar_type(&self) -> String { + self.0.rust_scalar_type() + } + fn c_promotion(&self) -> &str { + self.0.c_promotion() + } + fn populate_random(&self, indentation: Indentation, loads: u32, language: &Language) -> String { + self.0.populate_random(indentation, loads, language) + } + fn is_rust_vals_array_const(&self) -> bool { + self.0.is_rust_vals_array_const() + } + fn as_call_param_c(&self, name: &String) -> String { + self.0.as_call_param_c(name) + } +} - /// The return type of this intrinsic. - pub results: IntrinsicType, +impl IntrinsicDefinition<ArmIntrinsicType, Constraint> for Intrinsic<ArmIntrinsicType, Constraint> { + fn arguments(&self) -> ArgumentList<ArmIntrinsicType, Constraint> { + self.arguments.clone() + } - /// Whether this intrinsic is only available on A64. - pub a64_only: bool, -} + fn results(&self) -> ArmIntrinsicType { + self.results.clone() + } + + fn name(&self) -> String { + self.name.clone() + } -impl Intrinsic { /// Generates a std::cout for the intrinsics results that will match the /// rust debug output format for the return type. The generated line assumes /// there is an int i in scope which is the current pass number. - pub fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { - let lanes = if self.results.num_vectors() > 1 { - (0..self.results.num_vectors()) + fn print_result_c(&self, indentation: Indentation, additional: &str) -> String { + let lanes = if self.results().num_vectors() > 1 { + (0..self.results().num_vectors()) .map(|vector| { format!( r#""{ty}(" << {lanes} << ")""#, - ty = self.results.c_single_vector_type(), - lanes = (0..self.results.num_lanes()) + ty = self.results().c_single_vector_type(), + lanes = (0..self.results().num_lanes()) .map(move |idx| -> std::string::String { format!( "{cast}{lane_fn}(__return_value.val[{vector}], {lane})", - cast = self.results.c_promotion(), - lane_fn = self.results.get_lane_function(), + cast = self.results().c_promotion(), + lane_fn = self.results().get_lane_function(), lane = idx, vector = vector, ) @@ -45,13 +88,13 @@ impl Intrinsic { }) .collect::<Vec<_>>() .join(r#" << ", " << "#) - } else if self.results.num_lanes() > 1 { - (0..self.results.num_lanes()) + } else if self.results().num_lanes() > 1 { + (0..self.results().num_lanes()) .map(|idx| -> std::string::String { format!( "{cast}{lane_fn}(__return_value, {lane})", - cast = self.results.c_promotion(), - lane_fn = self.results.get_lane_function(), + cast = self.results().c_promotion(), + lane_fn = self.results().get_lane_function(), lane = idx ) }) @@ -61,22 +104,22 @@ impl Intrinsic { format!( "{promote}cast<{cast}>(__return_value)", cast = match self.results.kind() { - TypeKind::Float if self.results.inner_size() == 16 => "float16_t".to_string(), - TypeKind::Float if self.results.inner_size() == 32 => "float".to_string(), - TypeKind::Float if self.results.inner_size() == 64 => "double".to_string(), - TypeKind::Int => format!("int{}_t", self.results.inner_size()), - TypeKind::UInt => format!("uint{}_t", self.results.inner_size()), - TypeKind::Poly => format!("poly{}_t", self.results.inner_size()), + TypeKind::Float if self.results().inner_size() == 16 => "float16_t".to_string(), + TypeKind::Float if self.results().inner_size() == 32 => "float".to_string(), + TypeKind::Float if self.results().inner_size() == 64 => "double".to_string(), + TypeKind::Int => format!("int{}_t", self.results().inner_size()), + TypeKind::UInt => format!("uint{}_t", self.results().inner_size()), + TypeKind::Poly => format!("poly{}_t", self.results().inner_size()), ty => todo!("print_result_c - Unknown type: {:#?}", ty), }, - promote = self.results.c_promotion(), + promote = self.results().c_promotion(), ) }; format!( r#"{indentation}std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#, - ty = if self.results.is_simd() { - format!("{}(", self.results.c_type()) + ty = if self.results().is_simd() { + format!("{}(", self.results().c_type()) } else { String::from("") }, diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index 001d721fa6b..2f49b84c67f 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -1,6 +1,8 @@ -use super::argument::{Argument, ArgumentList}; -use super::intrinsic::Intrinsic; -use super::types::IntrinsicType; +use super::constraint::Constraint; +use super::intrinsic::ArmIntrinsicType; +use crate::common::argument::{Argument, ArgumentList}; +use crate::common::intrinsic::Intrinsic; +use crate::common::intrinsic_types::{IntrinsicType, IntrinsicTypeDefinition}; use serde::Deserialize; use serde_json::Value; use std::collections::HashMap; @@ -53,7 +55,7 @@ struct JsonIntrinsic { pub fn get_neon_intrinsics( filename: &Path, target: &String, -) -> Result<Vec<Intrinsic>, Box<dyn std::error::Error>> { +) -> Result<Vec<Intrinsic<ArmIntrinsicType, Constraint>>, Box<dyn std::error::Error>> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); let json: Vec<JsonIntrinsic> = serde_json::from_reader(reader).expect("Couldn't parse JSON"); @@ -74,37 +76,39 @@ pub fn get_neon_intrinsics( fn json_to_intrinsic( mut intr: JsonIntrinsic, target: &String, -) -> Result<Intrinsic, Box<dyn std::error::Error>> { +) -> Result<Intrinsic<ArmIntrinsicType, Constraint>, Box<dyn std::error::Error>> { let name = intr.name.replace(['[', ']'], ""); - let results = IntrinsicType::from_c(&intr.return_type.value, target)?; + let results = ArmIntrinsicType::from_c(&intr.return_type.value, target)?; let args = intr .arguments .into_iter() .enumerate() .map(|(i, arg)| { - // let arg_name = Argument::type_and_name_from_c(&arg).1; - let mut arg = Argument::from_c(i, &arg, target, intr.args_prep.as_mut()); + let arg_name = Argument::<ArmIntrinsicType, Constraint>::type_and_name_from_c(&arg).1; + let metadata = intr.args_prep.as_mut(); + let metadata = metadata.and_then(|a| a.remove(arg_name)); + let mut arg = + Argument::<ArmIntrinsicType, Constraint>::from_c(i, &arg, target, metadata); + // The JSON doesn't list immediates as const - if let IntrinsicType::Type { + let IntrinsicType { ref mut constant, .. - } = arg.ty - { - if arg.name.starts_with("imm") { - *constant = true - } + } = arg.ty.0; + if arg.name.starts_with("imm") { + *constant = true } arg }) .collect(); - let arguments = ArgumentList { args }; + let arguments = ArgumentList::<ArmIntrinsicType, Constraint> { args }; Ok(Intrinsic { name, arguments, - results, + results: *results, a64_only: intr.architectures == vec!["A64".to_string()], }) } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 8d94250c53b..cef32c3fb61 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -1,21 +1,22 @@ -mod argument; mod config; -mod format; +mod constraint; mod functions; mod intrinsic; mod json_parser; mod types; +use crate::arm::constraint::Constraint; +use crate::arm::intrinsic::ArmIntrinsicType; use crate::common::SupportedArchitectureTest; use crate::common::compare::compare_outputs; +use crate::common::intrinsic::Intrinsic; +use crate::common::intrinsic_types::{BaseIntrinsicTypeDefinition, TypeKind}; use crate::common::types::ProcessedCli; use functions::{build_c, build_rust}; -use intrinsic::Intrinsic; use json_parser::get_neon_intrinsics; -use types::TypeKind; pub struct ArmArchitectureTest { - intrinsics: Vec<Intrinsic>, + intrinsics: Vec<Intrinsic<ArmIntrinsicType, Constraint>>, cli_options: ProcessedCli, } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index a579e9699d6..db08c3a52dd 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -1,474 +1,125 @@ -use std::fmt; -use std::str::FromStr; - -use itertools::Itertools as _; - -use super::format::Indentation; +use super::intrinsic::ArmIntrinsicType; +use crate::common::intrinsic_types::{IntrinsicType, IntrinsicTypeDefinition, TypeKind}; use crate::common::types::Language; -use crate::common::values::value_for_array; - -#[derive(Debug, PartialEq, Copy, Clone)] -pub enum TypeKind { - BFloat, - Float, - Int, - UInt, - Poly, - Void, -} - -impl FromStr for TypeKind { - type Err = String; - - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "bfloat" => Ok(Self::BFloat), - "float" => Ok(Self::Float), - "int" => Ok(Self::Int), - "poly" => Ok(Self::Poly), - "uint" | "unsigned" => Ok(Self::UInt), - "void" => Ok(Self::Void), - _ => Err(format!("Impossible to parse argument kind {s}")), - } - } -} - -impl fmt::Display for TypeKind { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "{}", - match self { - Self::BFloat => "bfloat", - Self::Float => "float", - Self::Int => "int", - Self::UInt => "uint", - Self::Poly => "poly", - Self::Void => "void", - } - ) - } -} - -impl TypeKind { - /// Gets the type part of a c typedef for a type that's in the form of {type}{size}_t. - pub fn c_prefix(&self) -> &str { - match self { - Self::Float => "float", - Self::Int => "int", - Self::UInt => "uint", - Self::Poly => "poly", - _ => unreachable!("Not used: {:#?}", self), - } - } - - /// Gets the rust prefix for the type kind i.e. i, u, f. - pub fn rust_prefix(&self) -> &str { - match self { - Self::Float => "f", - Self::Int => "i", - Self::UInt => "u", - Self::Poly => "u", - _ => unreachable!("Unused type kind: {:#?}", self), - } - } -} - -#[derive(Debug, PartialEq, Clone)] -pub enum IntrinsicType { - Ptr { - constant: bool, - child: Box<IntrinsicType>, - }, - Type { - constant: bool, - kind: TypeKind, - /// The bit length of this type (e.g. 32 for u32). - bit_len: Option<u32>, - - /// Length of the SIMD vector (i.e. 4 for uint32x4_t), A value of `None` - /// means this is not a simd type. A `None` can be assumed to be 1, - /// although in some places a distinction is needed between `u64` and - /// `uint64x1_t` this signals that. - simd_len: Option<u32>, - - /// The number of rows for SIMD matrices (i.e. 2 for uint8x8x2_t). - /// A value of `None` represents a type that does not contain any - /// rows encoded in the type (e.g. uint8x8_t). - /// A value of `None` can be assumed to be 1 though. - vec_len: Option<u32>, - - target: String, - }, -} - -impl IntrinsicType { - /// Get the TypeKind for this type, recursing into pointers. - pub fn kind(&self) -> TypeKind { - match *self { - IntrinsicType::Ptr { ref child, .. } => child.kind(), - IntrinsicType::Type { kind, .. } => kind, - } - } - - /// Get the size of a single element inside this type, recursing into - /// pointers, i.e. a pointer to a u16 would be 16 rather than the size - /// of a pointer. - pub fn inner_size(&self) -> u32 { - match self { - IntrinsicType::Ptr { child, .. } => child.inner_size(), - IntrinsicType::Type { - bit_len: Some(bl), .. - } => *bl, - _ => unreachable!(""), - } - } - - pub fn num_lanes(&self) -> u32 { - match *self { - IntrinsicType::Ptr { ref child, .. } => child.num_lanes(), - IntrinsicType::Type { - simd_len: Some(sl), .. - } => sl, - _ => 1, - } - } - - pub fn num_vectors(&self) -> u32 { - match *self { - IntrinsicType::Ptr { ref child, .. } => child.num_vectors(), - IntrinsicType::Type { - vec_len: Some(vl), .. - } => vl, - _ => 1, - } - } - - /// Determine if the type is a simd type, this will treat a type such as - /// `uint64x1` as simd. - pub fn is_simd(&self) -> bool { - match *self { - IntrinsicType::Ptr { ref child, .. } => child.is_simd(), - IntrinsicType::Type { - simd_len: None, - vec_len: None, - .. - } => false, - _ => true, - } - } - - pub fn is_ptr(&self) -> bool { - match *self { - IntrinsicType::Ptr { .. } => true, - IntrinsicType::Type { .. } => false, - } - } - - /// Move to Argument - pub fn c_scalar_type(&self) -> String { - format!( - "{prefix}{bits}_t", - prefix = self.kind().c_prefix(), - bits = self.inner_size() - ) - } - - /// Move to Argument - pub fn rust_scalar_type(&self) -> String { - format!( - "{prefix}{bits}", - prefix = self.kind().rust_prefix(), - bits = self.inner_size() - ) - } +impl IntrinsicTypeDefinition for ArmIntrinsicType { /// Gets a string containing the typename for this type in C format. - /// - /// ARM-specific - pub fn c_type(&self) -> String { - match self { - IntrinsicType::Ptr { child, .. } => child.c_type(), - IntrinsicType::Type { - constant, - kind, - bit_len: Some(bit_len), - simd_len: None, - vec_len: None, - .. - } => format!( - "{}{}{}_t", - if *constant { "const " } else { "" }, - kind.c_prefix(), - bit_len - ), - IntrinsicType::Type { - kind, - bit_len: Some(bit_len), - simd_len: Some(simd_len), - vec_len: None, - .. - } => format!("{}{bit_len}x{simd_len}_t", kind.c_prefix()), - IntrinsicType::Type { - kind, - bit_len: Some(bit_len), - simd_len: Some(simd_len), - vec_len: Some(vec_len), - .. - } => format!("{}{bit_len}x{simd_len}x{vec_len}_t", kind.c_prefix()), - _ => todo!("{:#?}", self), - } - } - - /// ARM-specific - pub fn c_single_vector_type(&self) -> String { - match self { - IntrinsicType::Ptr { child, .. } => child.c_single_vector_type(), - IntrinsicType::Type { - kind, - bit_len: Some(bit_len), - simd_len: Some(simd_len), - vec_len: Some(_), - .. - } => format!("{}{bit_len}x{simd_len}_t", kind.c_prefix()), - _ => unreachable!("Shouldn't be called on this type"), - } - } - - /// ARM-specific - pub fn rust_type(&self) -> String { - match self { - IntrinsicType::Ptr { child, .. } => child.c_type(), - IntrinsicType::Type { - kind, - bit_len: Some(bit_len), - simd_len: None, - vec_len: None, - .. - } => format!("{}{bit_len}", kind.rust_prefix()), - IntrinsicType::Type { - kind, - bit_len: Some(bit_len), - simd_len: Some(simd_len), - vec_len: None, - .. - } => format!("{}{bit_len}x{simd_len}_t", kind.c_prefix()), - IntrinsicType::Type { - kind, - bit_len: Some(bit_len), - simd_len: Some(simd_len), - vec_len: Some(vec_len), - .. - } => format!("{}{bit_len}x{simd_len}x{vec_len}_t", kind.c_prefix()), - _ => todo!("{:#?}", self), - } - } - - /// Gets a cast for this type if needs promotion. - /// This is required for 8 bit types due to printing as the 8 bit types use - /// a char and when using that in `std::cout` it will print as a character, - /// which means value of 0 will be printed as a null byte. - /// - /// This is also needed for polynomial types because we want them to be - /// printed as unsigned integers to match Rust's `Debug` impl. - pub fn c_promotion(&self) -> &str { - match *self { - IntrinsicType::Type { - kind, - bit_len: Some(8), - .. - } => match kind { - TypeKind::Int => "(int)", - TypeKind::UInt => "(unsigned int)", - TypeKind::Poly => "(unsigned int)(uint8_t)", - _ => "", - }, - IntrinsicType::Type { - kind: TypeKind::Poly, - bit_len: Some(bit_len), - .. - } => match bit_len { - 8 => unreachable!("handled above"), - 16 => "(uint16_t)", - 32 => "(uint32_t)", - 64 => "(uint64_t)", - 128 => "", - _ => panic!("invalid bit_len"), - }, - _ => "", + fn c_type(&self) -> String { + let prefix = self.0.kind.c_prefix(); + let const_prefix = if self.0.constant { "const " } else { "" }; + + if let (Some(bit_len), simd_len, vec_len) = + (self.0.bit_len, self.0.simd_len, self.0.vec_len) + { + match (simd_len, vec_len) { + (None, None) => format!("{}{}{}_t", const_prefix, prefix, bit_len), + (Some(simd), None) => format!("{}{bit_len}x{simd}_t", prefix), + (Some(simd), Some(vec)) => format!("{}{bit_len}x{simd}x{vec}_t", prefix), + (None, Some(_)) => todo!("{:#?}", self), // Likely an invalid case + } + } else { + todo!("{:#?}", self) } } - /// Generates an initialiser for an array, which can be used to initialise an argument for the - /// intrinsic call. - /// - /// This is determistic based on the pass number. - /// - /// * `loads`: The number of values that need to be loaded from the argument array - /// * e.g for argument type uint32x2, loads=2 results in a string representing 4 32-bit values - /// - /// Returns a string such as - /// * `{0x1, 0x7F, 0xFF}` if `language` is `Language::C` - /// * `[0x1 as _, 0x7F as _, 0xFF as _]` if `language` is `Language::Rust` - pub fn populate_random( - &self, - indentation: Indentation, - loads: u32, - language: &Language, - ) -> String { - match self { - IntrinsicType::Ptr { child, .. } => child.populate_random(indentation, loads, language), - IntrinsicType::Type { - bit_len: Some(bit_len @ (8 | 16 | 32 | 64)), - kind: kind @ (TypeKind::Int | TypeKind::UInt | TypeKind::Poly), - simd_len, - vec_len, - .. - } => { - let (prefix, suffix) = match language { - Language::Rust => ("[", "]"), - Language::C => ("{", "}"), - }; - let body_indentation = indentation.nested(); - format!( - "{prefix}\n{body}\n{indentation}{suffix}", - body = (0..(simd_len.unwrap_or(1) * 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 && (src >> (*bit_len - 1)) != 0 { - // `src` is a two's complement representation of a negative value. - let mask = !0u64 >> (64 - *bit_len); - let ones_compl = src ^ mask; - let twos_compl = ones_compl + 1; - if (twos_compl == src) && (language == &Language::C) { - // `src` is INT*_MIN. C requires `-0x7fffffff - 1` to avoid - // undefined literal overflow behaviour. - fmt(&format_args!("{body_indentation}-{ones_compl:#x} - 1")) - } else { - fmt(&format_args!("{body_indentation}-{twos_compl:#x}")) - } - } else { - fmt(&format_args!("{body_indentation}{src:#x}")) - } - }) - ) - } - IntrinsicType::Type { - kind: TypeKind::Float, - bit_len: Some(bit_len @ (16 | 32 | 64)), - simd_len, - vec_len, - .. - } => { - let (prefix, cast_prefix, cast_suffix, suffix) = match (language, bit_len) { - (&Language::Rust, 16) => ("[", "f16::from_bits(", ")", "]"), - (&Language::Rust, 32) => ("[", "f32::from_bits(", ")", "]"), - (&Language::Rust, 64) => ("[", "f64::from_bits(", ")", "]"), - (&Language::C, 16) => ("{", "cast<float16_t, uint16_t>(", ")", "}"), - (&Language::C, 32) => ("{", "cast<float, uint32_t>(", ")", "}"), - (&Language::C, 64) => ("{", "cast<double, uint64_t>(", ")", "}"), - _ => unreachable!(), - }; - format!( - "{prefix}\n{body}\n{indentation}{suffix}", - body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1)) - .format_with(",\n", |i, fmt| fmt(&format_args!( - "{indentation}{cast_prefix}{src:#x}{cast_suffix}", - indentation = indentation.nested(), - src = value_for_array(*bit_len, i) - ))) - ) + fn c_single_vector_type(&self) -> String { + if let (Some(bit_len), Some(simd_len)) = (self.0.bit_len, self.0.simd_len) { + let prefix = self.0.kind.c_prefix(); + format!("{}{bit_len}x{simd_len}_t", prefix) + } else { + unreachable!("Shouldn't be called on this type") + } + } + + fn rust_type(&self) -> String { + let rust_prefix = self.0.kind.rust_prefix(); + let c_prefix = self.0.kind.rust_prefix(); + if self.0.ptr_constant { + self.c_type() + } else if let (Some(bit_len), simd_len, vec_len) = + (self.0.bit_len, self.0.simd_len, self.0.vec_len) + { + match (simd_len, vec_len) { + (None, None) => format!("{}{bit_len}", rust_prefix), + (Some(simd), None) => format!("{}{bit_len}x{simd}_t", c_prefix), + (Some(simd), Some(vec)) => format!("{}{bit_len}x{simd}x{vec}_t", c_prefix), + (None, Some(_)) => todo!("{:#?}", self), // Likely an invalid case } - _ => unimplemented!("populate random: {:#?}", self), + } else { + todo!("{:#?}", self) } } /// Determines the load function for this type. - /// - /// ARM-specific fn get_load_function(&self, language: Language) -> String { - match self { - IntrinsicType::Ptr { child, .. } => child.get_load_function(language), - IntrinsicType::Type { - kind: k, - bit_len: Some(bl), - simd_len, - vec_len, - target, - .. - } => { - let quad = if simd_len.unwrap_or(1) * bl > 64 { - "q" - } else { - "" - }; + if let IntrinsicType { + kind: k, + bit_len: Some(bl), + simd_len, + vec_len, + target, + .. + } = &self.0 + { + let quad = if simd_len.unwrap_or(1) * bl > 64 { + "q" + } else { + "" + }; - let choose_workaround = language == Language::C && target.contains("v7"); - format!( - "vld{len}{quad}_{type}{size}", - type = match k { - TypeKind::UInt => "u", - TypeKind::Int => "s", - TypeKind::Float => "f", - // The ACLE doesn't support 64-bit polynomial loads on Armv7 - // if armv7 and bl == 64, use "s", else "p" - TypeKind::Poly => if choose_workaround && *bl == 64 {"s"} else {"p"}, - x => todo!("get_load_function TypeKind: {:#?}", x), - }, - size = bl, - quad = quad, - len = vec_len.unwrap_or(1), - ) - } - _ => todo!("get_load_function IntrinsicType: {:#?}", self), + let choose_workaround = language == Language::C && target.contains("v7"); + format!( + "vld{len}{quad}_{type}{size}", + type = match k { + TypeKind::UInt => "u", + TypeKind::Int => "s", + TypeKind::Float => "f", + // The ACLE doesn't support 64-bit polynomial loads on Armv7 + // if armv7 and bl == 64, use "s", else "p" + TypeKind::Poly => if choose_workaround && *bl == 64 {"s"} else {"p"}, + x => todo!("get_load_function TypeKind: {:#?}", x), + }, + size = bl, + quad = quad, + len = vec_len.unwrap_or(1), + ) + } else { + todo!("get_load_function IntrinsicType: {:#?}", self) } } - pub fn get_load_function_c(&self) -> String { - self.get_load_function(Language::C) - } - - pub fn get_load_function_rust(&self) -> String { - self.get_load_function(Language::Rust) - } - /// Determines the get lane function for this type. - /// - /// ARM-specific - pub fn get_lane_function(&self) -> String { - match self { - IntrinsicType::Ptr { child, .. } => child.get_lane_function(), - IntrinsicType::Type { - kind: k, - bit_len: Some(bl), - simd_len, - .. - } => { - let quad = if (simd_len.unwrap_or(1) * bl) > 64 { - "q" - } else { - "" - }; - format!( - "vget{quad}_lane_{type}{size}", - type = match k { - TypeKind::UInt => "u", - TypeKind::Int => "s", - TypeKind::Float => "f", - TypeKind::Poly => "p", - x => todo!("get_load_function TypeKind: {:#?}", x), - }, - size = bl, - quad = quad, - ) - } - _ => todo!("get_lane_function IntrinsicType: {:#?}", self), + fn get_lane_function(&self) -> String { + if let IntrinsicType { + kind: k, + bit_len: Some(bl), + simd_len, + .. + } = &self.0 + { + let quad = if (simd_len.unwrap_or(1) * bl) > 64 { + "q" + } else { + "" + }; + format!( + "vget{quad}_lane_{type}{size}", + type = match k { + TypeKind::UInt => "u", + TypeKind::Int => "s", + TypeKind::Float => "f", + TypeKind::Poly => "p", + x => todo!("get_load_function TypeKind: {:#?}", x), + }, + size = bl, + quad = quad, + ) + } else { + todo!("get_lane_function IntrinsicType: {:#?}", self) } } - /// ARM-specific - pub fn from_c(s: &str, target: &String) -> Result<IntrinsicType, String> { + fn from_c(s: &str, target: &String) -> Result<Box<Self>, String> { const CONST_STR: &str = "const"; if let Some(s) = s.strip_suffix('*') { let (s, constant) = match s.trim().strip_suffix(CONST_STR) { @@ -476,9 +127,12 @@ impl IntrinsicType { None => (s, false), }; let s = s.trim_end(); - Ok(IntrinsicType::Ptr { - constant, - child: Box::new(IntrinsicType::from_c(s, target)?), + let temp_return = ArmIntrinsicType::from_c(s, target); + temp_return.and_then(|mut op| { + let edited = op.as_mut(); + edited.0.ptr = true; + edited.0.ptr_constant = constant; + Ok(op) }) } else { // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t] @@ -507,28 +161,32 @@ impl IntrinsicType { ), None => None, }; - Ok(IntrinsicType::Type { + Ok(Box::new(ArmIntrinsicType(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::<TypeKind>()?; let bit_len = match kind { TypeKind::Int => Some(32), _ => None, }; - Ok(IntrinsicType::Type { + Ok(Box::new(ArmIntrinsicType(IntrinsicType { + ptr: false, + ptr_constant: false, constant, kind: start.parse::<TypeKind>()?, bit_len, simd_len: None, vec_len: None, target: target.to_string(), - }) + }))) } } } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index 24fe8c9a93a..08d6ca15239 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -1,67 +1,35 @@ -use super::format::Indentation; -use super::json_parser::ArgPrep; -use super::types::{IntrinsicType, TypeKind}; +use crate::common::format::Indentation; +use crate::common::intrinsic_types::IntrinsicTypeDefinition; use crate::common::types::Language; -use serde::Deserialize; use serde_json::Value; -use std::collections::HashMap; -use std::ops::Range; /// An argument for the intrinsic. #[derive(Debug, PartialEq, Clone)] -pub struct Argument { +pub struct Argument<T: IntrinsicTypeDefinition, M: MetadataDefinition> { /// The argument's index in the intrinsic function call. pub pos: usize, /// The argument name. pub name: String, /// The type of the argument. - pub ty: IntrinsicType, + pub ty: T, /// Any constraints that are on this argument - pub constraints: Vec<Constraint>, + pub metadata: Vec<M>, } -#[derive(Debug, PartialEq, Clone, Deserialize)] -pub enum Constraint { - Equal(i64), - Range(Range<i64>), +pub trait MetadataDefinition { + fn from_metadata(metadata: Option<Value>) -> Vec<Box<Self>>; } -/// ARM-specific -impl TryFrom<ArgPrep> for Constraint { - type Error = (); - - fn try_from(prep: ArgPrep) -> Result<Self, Self::Error> { - let parsed_ints = match prep { - ArgPrep::Immediate { min, max } => Ok((min, max)), - _ => Err(()), - }; - if let Ok((min, max)) = parsed_ints { - if min == max { - Ok(Constraint::Equal(min)) - } else { - Ok(Constraint::Range(min..max + 1)) - } - } else { - Err(()) - } - } -} - -impl Constraint { - pub fn to_range(&self) -> Range<i64> { - match self { - Constraint::Equal(eq) => *eq..*eq + 1, - Constraint::Range(range) => range.clone(), - } - } -} - -impl Argument { - fn to_c_type(&self) -> String { +impl<T, M> Argument<T, M> +where + T: IntrinsicTypeDefinition, + M: MetadataDefinition, +{ + pub fn to_c_type(&self) -> String { self.ty.c_type() } - fn is_simd(&self) -> bool { + pub fn is_simd(&self) -> bool { self.ty.is_simd() } @@ -70,7 +38,7 @@ impl Argument { } pub fn has_constraint(&self) -> bool { - !self.constraints.is_empty() + !self.metadata.is_empty() } pub fn type_and_name_from_c(arg: &str) -> (&str, &str) { @@ -81,86 +49,65 @@ impl Argument { (arg[..split_index + 1].trim_end(), &arg[split_index + 1..]) } - // ARM-specific + /// The binding keyword (e.g. "const" or "let") for the array of possible test inputs. + fn rust_vals_array_binding(&self) -> impl std::fmt::Display { + if self.ty.is_rust_vals_array_const() { + "const" + } else { + "let" + } + } + + /// The name (e.g. "A_VALS" or "a_vals") for the array of possible test inputs. + fn rust_vals_array_name(&self) -> impl std::fmt::Display { + if self.ty.is_rust_vals_array_const() { + format!("{}_VALS", self.name.to_uppercase()) + } else { + format!("{}_vals", self.name.to_lowercase()) + } + } + pub fn from_c( pos: usize, arg: &str, target: &String, - metadata: Option<&mut HashMap<String, Value>>, - ) -> Argument { + metadata: Option<Value>, + ) -> Argument<T, M> { let (ty, var_name) = Self::type_and_name_from_c(arg); - let ty = IntrinsicType::from_c(ty, target) - .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); + let ty = + T::from_c(ty, target).unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); - let arg_name = Argument::type_and_name_from_c(&arg).1; - let arg = metadata.and_then(|a| a.remove(arg_name)); - let arg_prep: Option<ArgPrep> = arg.and_then(|a| { - if let Value::Object(_) = a { - a.try_into().ok() - } else { - None - } - }); - let constraint = arg_prep.and_then(|a| a.try_into().ok()); + let metadata: Vec<M> = M::from_metadata(metadata).into_iter().map(|b| *b).collect(); Argument { pos, name: String::from(var_name), - ty, - constraints: constraint.map_or(vec![], |r| vec![r]), + ty: *ty, + metadata, } } - fn is_rust_vals_array_const(&self) -> bool { - use TypeKind::*; - match self.ty { - // Floats have to be loaded at runtime for stable NaN conversion. - IntrinsicType::Type { kind: Float, .. } => false, - IntrinsicType::Type { - kind: Int | UInt | Poly, - .. - } => true, - _ => unimplemented!(), - } - } - - /// The binding keyword (e.g. "const" or "let") for the array of possible test inputs. - pub fn rust_vals_array_binding(&self) -> impl std::fmt::Display { - if self.is_rust_vals_array_const() { - "const" - } else { - "let" - } - } - - /// The name (e.g. "A_VALS" or "a_vals") for the array of possible test inputs. - pub fn rust_vals_array_name(&self) -> impl std::fmt::Display { - if self.is_rust_vals_array_const() { - format!("{}_VALS", self.name.to_uppercase()) - } else { - format!("{}_vals", self.name.to_lowercase()) - } + fn as_call_param_c(&self) -> String { + self.ty.as_call_param_c(&self.name) } } #[derive(Debug, PartialEq, Clone)] -pub struct ArgumentList { - pub args: Vec<Argument>, +pub struct ArgumentList<T: IntrinsicTypeDefinition, M: MetadataDefinition> { + pub args: Vec<Argument<T, M>>, } -impl ArgumentList { +impl<T, M> ArgumentList<T, M> +where + T: IntrinsicTypeDefinition, + M: MetadataDefinition, +{ /// Converts the argument list into the call parameters for a C function call. /// e.g. this would generate something like `a, &b, c` pub fn as_call_param_c(&self) -> String { - self.args - .iter() - .map(|arg| match arg.ty { - IntrinsicType::Ptr { .. } => { - format!("&{}", arg.name) - } - IntrinsicType::Type { .. } => arg.name.clone(), - }) + self.iter() + .map(|arg| arg.as_call_param_c()) .collect::<Vec<String>>() .join(", ") } @@ -168,8 +115,7 @@ impl ArgumentList { /// Converts the argument list into the call parameters for a Rust function. /// e.g. this would generate something like `a, b, c` pub fn as_call_param_rust(&self) -> String { - self.args - .iter() + self.iter() .filter(|a| !a.has_constraint()) .map(|arg| arg.name.clone()) .collect::<Vec<String>>() @@ -177,8 +123,7 @@ impl ArgumentList { } pub fn as_constraint_parameters_rust(&self) -> String { - self.args - .iter() + self.iter() .filter(|a| a.has_constraint()) .map(|arg| arg.name.clone()) .collect::<Vec<String>>() @@ -241,7 +186,7 @@ impl ArgumentList { ty = arg.to_c_type(), name = arg.name, load = if arg.is_simd() { - arg.ty.get_load_function_c() + arg.ty.get_load_function(Language::C) } else { "*".to_string() } @@ -263,7 +208,7 @@ impl ArgumentList { name = arg.name, vals_name = arg.rust_vals_array_name(), load = if arg.is_simd() { - arg.ty.get_load_function_rust() + arg.ty.get_load_function(Language::Rust) } else { "*".to_string() }, @@ -273,7 +218,7 @@ impl ArgumentList { .collect() } - pub fn iter(&self) -> std::slice::Iter<'_, Argument> { + pub fn iter(&self) -> std::slice::Iter<'_, Argument<T, M>> { self.args.iter() } } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/format.rs b/library/stdarch/crates/intrinsic-test/src/common/format.rs index 9ee331d7f7a..9ee331d7f7a 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/format.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/format.rs diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs new file mode 100644 index 00000000000..e2124897f3d --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -0,0 +1,91 @@ +use crate::common::argument::ArgumentList; +use crate::common::format::Indentation; +use crate::common::intrinsic_types::IntrinsicTypeDefinition; + +use super::argument::MetadataDefinition; + +/// An intrinsic +#[derive(Debug, PartialEq, Clone)] +pub struct Intrinsic<T: IntrinsicTypeDefinition, M: MetadataDefinition> { + /// The function name of this intrinsic. + pub name: String, + + /// Any arguments for this intrinsic. + pub arguments: ArgumentList<T, M>, + + /// The return type of this intrinsic. + pub results: T, + + /// Whether this intrinsic is only available on A64. + pub a64_only: bool, +} + +pub trait IntrinsicDefinition<T, M> +where + T: IntrinsicTypeDefinition, + M: MetadataDefinition, +{ + fn arguments(&self) -> ArgumentList<T, M>; + + fn results(&self) -> T; + + fn name(&self) -> String; + + /// Generates a std::cout for the intrinsics results that will match the + /// rust debug output format for the return type. The generated line assumes + /// there is an int i in scope which is the current pass number. + fn print_result_c(&self, _indentation: Indentation, _additional: &str) -> String { + unimplemented!("Architectures need to implement print_result_c!") + } + + fn generate_loop_c( + &self, + indentation: Indentation, + additional: &str, + passes: u32, + _target: &str, + ) -> String { + let body_indentation = indentation.nested(); + format!( + "{indentation}for (int i=0; i<{passes}; i++) {{\n\ + {loaded_args}\ + {body_indentation}auto __return_value = {intrinsic_call}({args});\n\ + {print_result}\n\ + {indentation}}}", + loaded_args = self.arguments().load_values_c(body_indentation), + intrinsic_call = self.name(), + args = self.arguments().as_call_param_c(), + print_result = self.print_result_c(body_indentation, additional) + ) + } + + fn generate_loop_rust( + &self, + indentation: Indentation, + additional: &str, + passes: u32, + ) -> String { + let constraints = self.arguments().as_constraint_parameters_rust(); + let constraints = if !constraints.is_empty() { + format!("::<{constraints}>") + } else { + constraints + }; + + let indentation2 = indentation.nested(); + let indentation3 = indentation2.nested(); + format!( + "{indentation}for i in 0..{passes} {{\n\ + {indentation2}unsafe {{\n\ + {loaded_args}\ + {indentation3}let __return_value = {intrinsic_call}{const}({args});\n\ + {indentation3}println!(\"Result {additional}-{{}}: {{:.150?}}\", i + 1, __return_value);\n\ + {indentation2}}}\n\ + {indentation}}}", + loaded_args = self.arguments().load_values_rust(indentation3), + intrinsic_call = self.name(), + const = constraints, + args = self.arguments().as_call_param_rust(), + ) + } +} diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_types.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_types.rs new file mode 100644 index 00000000000..9a08ec066c1 --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_types.rs @@ -0,0 +1,352 @@ +use std::fmt; +use std::str::FromStr; + +use itertools::Itertools as _; + +use crate::common::format::Indentation; +use crate::common::types::Language; +use crate::common::values::value_for_array; + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum TypeKind { + BFloat, + Float, + Int, + UInt, + Poly, + Void, +} + +impl FromStr for TypeKind { + type Err = String; + + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "bfloat" => Ok(Self::BFloat), + "float" => Ok(Self::Float), + "int" => Ok(Self::Int), + "poly" => Ok(Self::Poly), + "uint" | "unsigned" => Ok(Self::UInt), + "void" => Ok(Self::Void), + _ => Err(format!("Impossible to parse argument kind {s}")), + } + } +} + +impl fmt::Display for TypeKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::BFloat => "bfloat", + Self::Float => "float", + Self::Int => "int", + Self::UInt => "uint", + Self::Poly => "poly", + Self::Void => "void", + } + ) + } +} + +impl TypeKind { + /// Gets the type part of a c typedef for a type that's in the form of {type}{size}_t. + pub fn c_prefix(&self) -> &str { + match self { + Self::Float => "float", + Self::Int => "int", + Self::UInt => "uint", + Self::Poly => "poly", + _ => unreachable!("Not used: {:#?}", self), + } + } + + /// Gets the rust prefix for the type kind i.e. i, u, f. + pub fn rust_prefix(&self) -> &str { + match self { + Self::Float => "f", + Self::Int => "i", + Self::UInt => "u", + Self::Poly => "u", + _ => unreachable!("Unused type kind: {:#?}", self), + } + } +} + +#[derive(Debug, PartialEq, Clone)] +pub struct IntrinsicType { + pub constant: bool, + + /// whether this object is a const pointer + pub ptr_constant: bool, + + pub ptr: bool, + + pub kind: TypeKind, + /// The bit length of this type (e.g. 32 for u32). + pub bit_len: Option<u32>, + + /// Length of the SIMD vector (i.e. 4 for uint32x4_t), A value of `None` + /// means this is not a simd type. A `None` can be assumed to be 1, + /// although in some places a distinction is needed between `u64` and + /// `uint64x1_t` this signals that. + pub simd_len: Option<u32>, + + /// The number of rows for SIMD matrices (i.e. 2 for uint8x8x2_t). + /// A value of `None` represents a type that does not contain any + /// rows encoded in the type (e.g. uint8x8_t). + /// A value of `None` can be assumed to be 1 though. + pub vec_len: Option<u32>, + + pub target: String, +} + +pub trait BaseIntrinsicTypeDefinition { + /// Get the TypeKind for this type, recursing into pointers. + fn kind(&self) -> TypeKind; + + /// Get the size of a single element inside this type, recursing into + /// pointers, i.e. a pointer to a u16 would be 16 rather than the size + /// of a pointer. + fn inner_size(&self) -> u32; + + fn num_lanes(&self) -> u32; + + fn num_vectors(&self) -> u32; + + /// Determine if the type is a simd type, this will treat a type such as + /// `uint64x1` as simd. + fn is_simd(&self) -> bool; + + fn is_ptr(&self) -> bool; + + fn c_scalar_type(&self) -> String; + + fn rust_scalar_type(&self) -> String; + + /// Gets a cast for this type if needs promotion. + /// This is required for 8 bit types due to printing as the 8 bit types use + /// a char and when using that in `std::cout` it will print as a character, + /// which means value of 0 will be printed as a null byte. + /// + /// This is also needed for polynomial types because we want them to be + /// printed as unsigned integers to match Rust's `Debug` impl. + fn c_promotion(&self) -> &str; + + /// Generates an initialiser for an array, which can be used to initialise an argument for the + /// intrinsic call. + /// + /// This is determistic based on the pass number. + /// + /// * `loads`: The number of values that need to be loaded from the argument array + /// * e.g for argument type uint32x2, loads=2 results in a string representing 4 32-bit values + /// + /// Returns a string such as + /// * `{0x1, 0x7F, 0xFF}` if `language` is `Language::C` + /// * `[0x1 as _, 0x7F as _, 0xFF as _]` if `language` is `Language::Rust` + fn populate_random(&self, indentation: Indentation, loads: u32, language: &Language) -> String; + + fn is_rust_vals_array_const(&self) -> bool; + + fn as_call_param_c(&self, name: &String) -> String; +} + +impl BaseIntrinsicTypeDefinition for IntrinsicType { + fn kind(&self) -> TypeKind { + self.kind + } + + fn inner_size(&self) -> u32 { + if let Some(bl) = self.bit_len { + bl + } else { + unreachable!("") + } + } + + fn num_lanes(&self) -> u32 { + if let Some(sl) = self.simd_len { sl } else { 1 } + } + + fn num_vectors(&self) -> u32 { + if let Some(vl) = self.vec_len { vl } else { 1 } + } + + fn is_simd(&self) -> bool { + self.simd_len.is_some() || self.vec_len.is_some() + } + + fn is_ptr(&self) -> bool { + self.ptr + } + + fn c_scalar_type(&self) -> String { + format!( + "{prefix}{bits}_t", + prefix = self.kind().c_prefix(), + bits = self.inner_size() + ) + } + + fn rust_scalar_type(&self) -> String { + format!( + "{prefix}{bits}", + prefix = self.kind().rust_prefix(), + bits = self.inner_size() + ) + } + + fn c_promotion(&self) -> &str { + match *self { + IntrinsicType { + kind, + bit_len: Some(8), + .. + } => match kind { + TypeKind::Int => "(int)", + TypeKind::UInt => "(unsigned int)", + TypeKind::Poly => "(unsigned int)(uint8_t)", + _ => "", + }, + IntrinsicType { + kind: TypeKind::Poly, + bit_len: Some(bit_len), + .. + } => match bit_len { + 8 => unreachable!("handled above"), + 16 => "(uint16_t)", + 32 => "(uint32_t)", + 64 => "(uint64_t)", + 128 => "", + _ => panic!("invalid bit_len"), + }, + _ => "", + } + } + + fn populate_random(&self, indentation: Indentation, loads: u32, language: &Language) -> String { + match self { + IntrinsicType { + bit_len: Some(bit_len @ (8 | 16 | 32 | 64)), + kind: kind @ (TypeKind::Int | TypeKind::UInt | TypeKind::Poly), + simd_len, + vec_len, + .. + } => { + let (prefix, suffix) = match language { + Language::Rust => ("[", "]"), + Language::C => ("{", "}"), + }; + let body_indentation = indentation.nested(); + format!( + "{prefix}\n{body}\n{indentation}{suffix}", + body = (0..(simd_len.unwrap_or(1) * 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 && (src >> (*bit_len - 1)) != 0 { + // `src` is a two's complement representation of a negative value. + let mask = !0u64 >> (64 - *bit_len); + let ones_compl = src ^ mask; + let twos_compl = ones_compl + 1; + if (twos_compl == src) && (language == &Language::C) { + // `src` is INT*_MIN. C requires `-0x7fffffff - 1` to avoid + // undefined literal overflow behaviour. + fmt(&format_args!("{body_indentation}-{ones_compl:#x} - 1")) + } else { + fmt(&format_args!("{body_indentation}-{twos_compl:#x}")) + } + } else { + fmt(&format_args!("{body_indentation}{src:#x}")) + } + }) + ) + } + IntrinsicType { + kind: TypeKind::Float, + bit_len: Some(bit_len @ (16 | 32 | 64)), + simd_len, + vec_len, + .. + } => { + let (prefix, cast_prefix, cast_suffix, suffix) = match (language, bit_len) { + (&Language::Rust, 16) => ("[", "f16::from_bits(", ")", "]"), + (&Language::Rust, 32) => ("[", "f32::from_bits(", ")", "]"), + (&Language::Rust, 64) => ("[", "f64::from_bits(", ")", "]"), + (&Language::C, 16) => ("{", "cast<float16_t, uint16_t>(", ")", "}"), + (&Language::C, 32) => ("{", "cast<float, uint32_t>(", ")", "}"), + (&Language::C, 64) => ("{", "cast<double, uint64_t>(", ")", "}"), + _ => unreachable!(), + }; + format!( + "{prefix}\n{body}\n{indentation}{suffix}", + body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1)) + .format_with(",\n", |i, fmt| fmt(&format_args!( + "{indentation}{cast_prefix}{src:#x}{cast_suffix}", + indentation = indentation.nested(), + src = value_for_array(*bit_len, i) + ))) + ) + } + _ => unimplemented!("populate random: {:#?}", self), + } + } + + fn is_rust_vals_array_const(&self) -> bool { + match self { + // Floats have to be loaded at runtime for stable NaN conversion. + IntrinsicType { + kind: TypeKind::Float, + .. + } => false, + IntrinsicType { + kind: TypeKind::Int | TypeKind::UInt | TypeKind::Poly, + .. + } => true, + _ => unimplemented!(), + } + } + + fn as_call_param_c(&self, name: &String) -> String { + if self.ptr { + format!("&{}", name) + } else { + name.clone() + } + } +} + +pub trait IntrinsicTypeDefinition: BaseIntrinsicTypeDefinition { + /// Determines the load function for this type. + /// can be implemented in an `impl` block + fn get_load_function(&self, _language: Language) -> String { + unimplemented!("Different architectures must implement get_load_function!") + } + + /// can be implemented in an `impl` block + fn get_lane_function(&self) -> String { + unimplemented!("Different architectures must implement get_lane_function!") + } + + /// can be implemented in an `impl` block + fn from_c(_s: &str, _target: &String) -> Result<Box<Self>, String> { + unimplemented!("Different architectures must implement from_c!") + } + + /// Gets a string containing the typename for this type in C format. + /// can be directly defined in `impl` blocks + fn c_type(&self) -> String { + unimplemented!("Different architectures must implement c_type!") + } + + /// can be directly defined in `impl` blocks + fn c_single_vector_type(&self) -> String { + unimplemented!("Different architectures must implement c_single_vector_type!") + } + + /// can be defined in `impl` blocks + fn rust_type(&self) -> String { + unimplemented!("Different architectures must implement rust_type!") + } +} diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 1c9f8027760..7db1166c7d2 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -2,9 +2,13 @@ use crate::common::types::ProcessedCli; use std::fs::File; use std::io::Write; +pub mod argument; pub mod compare; +pub mod format; pub mod gen_c; pub mod gen_rust; +pub mod intrinsic; +pub mod intrinsic_types; pub mod types; pub mod values; |
