diff --git a/crates/intrinsic-test/Cargo.toml b/crates/intrinsic-test/Cargo.toml index 06051abc8d..905e47c1d5 100644 --- a/crates/intrinsic-test/Cargo.toml +++ b/crates/intrinsic-test/Cargo.toml @@ -22,3 +22,5 @@ pretty_env_logger = "0.5.0" rayon = "1.5.0" diff = "0.1.12" itertools = "0.14.0" +quick-xml = { version = "0.37.5", features = ["serialize", "overlapped-lists"] } +serde-xml-rs = "0.8.0" \ No newline at end of file diff --git a/crates/intrinsic-test/src/arm/argument.rs b/crates/intrinsic-test/src/arm/argument.rs new file mode 100644 index 0000000000..db5501e5d2 --- /dev/null +++ b/crates/intrinsic-test/src/arm/argument.rs @@ -0,0 +1,12 @@ +use crate::arm::intrinsic::ArmIntrinsicType; +use crate::common::argument::Argument; + +impl Argument { + pub fn type_and_name_from_c(arg: &str) -> (&str, &str) { + let split_index = arg + .rfind([' ', '*']) + .expect("Couldn't split type and argname"); + + (arg[..split_index + 1].trim_end(), &arg[split_index + 1..]) + } +} diff --git a/crates/intrinsic-test/src/arm/intrinsic.rs b/crates/intrinsic-test/src/arm/intrinsic.rs index 773dabf4d7..8ec558ac11 100644 --- a/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/crates/intrinsic-test/src/arm/intrinsic.rs @@ -2,7 +2,7 @@ use crate::common::argument::ArgumentList; use crate::common::indentation::Indentation; use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, TypeKind}; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] pub struct ArmIntrinsicType(pub IntrinsicType); @@ -15,6 +15,12 @@ impl Deref for ArmIntrinsicType { } } +impl DerefMut for ArmIntrinsicType { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + impl IntrinsicDefinition for Intrinsic { fn arguments(&self) -> ArgumentList { self.arguments.clone() @@ -73,8 +79,8 @@ impl IntrinsicDefinition for Intrinsic { 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::Int(true) => format!("int{}_t", self.results().inner_size()), + TypeKind::Int(false) => format!("uint{}_t", self.results().inner_size()), TypeKind::Poly => format!("poly{}_t", self.results().inner_size()), ty => todo!("print_result_c - Unknown type: {:#?}", ty), }, diff --git a/crates/intrinsic-test/src/arm/json_parser.rs b/crates/intrinsic-test/src/arm/json_parser.rs index 0ac47484b0..8b1a52535c 100644 --- a/crates/intrinsic-test/src/arm/json_parser.rs +++ b/crates/intrinsic-test/src/arm/json_parser.rs @@ -79,20 +79,26 @@ fn json_to_intrinsic( ) -> Result, Box> { let name = intr.name.replace(['[', ']'], ""); - let results = ArmIntrinsicType::from_c(&intr.return_type.value, target)?; - + let mut results = ArmIntrinsicType::from_c(&intr.return_type.value)?; + results.set_metadata("target".to_string(), target.to_string()); let args = intr .arguments .into_iter() .enumerate() .map(|(i, arg)| { - let arg_name = Argument::::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 arg_prep: Option = metadata.and_then(|a| a.try_into().ok()); + let (type_name, arg_name) = Argument::::type_and_name_from_c(&arg); + let ty = ArmIntrinsicType::from_c(type_name) + .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); + + let arg_prep = intr.args_prep.as_mut(); + let arg_prep = arg_prep.and_then(|a| a.remove(arg_name)); + let arg_prep: Option = arg_prep.and_then(|a| a.try_into().ok()); let constraint: Option = arg_prep.and_then(|a| a.try_into().ok()); - let mut arg = Argument::::from_c(i, &arg, target, constraint); + let mut arg = + Argument::::new(i, arg_name.to_string(), ty, constraint); + arg.ty + .set_metadata("target".to_string(), target.to_string()); // The JSON doesn't list immediates as const let IntrinsicType { @@ -110,7 +116,7 @@ fn json_to_intrinsic( Ok(Intrinsic { name, arguments, - results: *results, + results: results, arch_tags: intr.architectures, }) } diff --git a/crates/intrinsic-test/src/arm/mod.rs b/crates/intrinsic-test/src/arm/mod.rs index 6aaa49ff97..dfabc8e094 100644 --- a/crates/intrinsic-test/src/arm/mod.rs +++ b/crates/intrinsic-test/src/arm/mod.rs @@ -1,3 +1,4 @@ +mod argument; mod compile; mod config; mod intrinsic; diff --git a/crates/intrinsic-test/src/arm/types.rs b/crates/intrinsic-test/src/arm/types.rs index 9f3d6302f4..6182c44fce 100644 --- a/crates/intrinsic-test/src/arm/types.rs +++ b/crates/intrinsic-test/src/arm/types.rs @@ -1,25 +1,18 @@ +use std::collections::HashMap; + use super::intrinsic::ArmIntrinsicType; use crate::common::cli::Language; use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, TypeKind}; impl IntrinsicTypeDefinition for ArmIntrinsicType { /// Gets a string containing the typename for this type in C format. + /// This assumes that the metadata hashmap contains this value at the + /// "type" key 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!("{const_prefix}{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"), - (None, Some(_)) => todo!("{:#?}", self), // Likely an invalid case - } - } else { - todo!("{:#?}", self) - } + self.metadata + .get("type") + .expect("Failed to extract the C typename in Aarch!") + .to_string() } fn c_single_vector_type(&self) -> String { @@ -59,7 +52,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { bit_len: Some(bl), simd_len, vec_len, - target, + metadata, .. } = &self.0 { @@ -69,12 +62,16 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { "" }; - let choose_workaround = language == Language::C && target.contains("v7"); + let choose_workaround = language == Language::C + && metadata + .get("target") + .filter(|value| value.contains("v7")) + .is_some(); format!( "vld{len}{quad}_{type}{size}", type = match k { - TypeKind::UInt => "u", - TypeKind::Int => "s", + TypeKind::Int(false) => "u", + TypeKind::Int(true) => "s", TypeKind::Float => "f", // The ACLE doesn't support 64-bit polynomial loads on Armv7 // if armv7 and bl == 64, use "s", else "p" @@ -107,8 +104,8 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { format!( "vget{quad}_lane_{type}{size}", type = match k { - TypeKind::UInt => "u", - TypeKind::Int => "s", + TypeKind::Int(false) => "u", + TypeKind::Int(true) => "s", TypeKind::Float => "f", TypeKind::Poly => "p", x => todo!("get_load_function TypeKind: {:#?}", x), @@ -121,20 +118,21 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { } } - fn from_c(s: &str, target: &str) -> Result, String> { + fn from_c(s: &str) -> Result { const CONST_STR: &str = "const"; + let mut metadata: HashMap = HashMap::new(); + metadata.insert("type".to_string(), s.to_string()); 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 temp_return = ArmIntrinsicType::from_c(s, target); - temp_return.map(|mut op| { - let edited = op.as_mut(); - edited.0.ptr = true; - edited.0.ptr_constant = constant; - op + let temp_return = ArmIntrinsicType::from_c(s); + temp_return.and_then(|mut op| { + op.ptr = true; + op.ptr_constant = constant; + Ok(op) }) } else { // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t] @@ -163,7 +161,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { ), None => None, }; - Ok(Box::new(ArmIntrinsicType(IntrinsicType { + Ok(ArmIntrinsicType(IntrinsicType { ptr: false, ptr_constant: false, constant, @@ -171,15 +169,15 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { bit_len: Some(bit_len), simd_len, vec_len, - target: target.to_string(), - }))) + metadata, + })) } else { let kind = start.parse::()?; let bit_len = match kind { - TypeKind::Int => Some(32), + TypeKind::Int(_) => Some(32), _ => None, }; - Ok(Box::new(ArmIntrinsicType(IntrinsicType { + Ok(ArmIntrinsicType(IntrinsicType { ptr: false, ptr_constant: false, constant, @@ -187,8 +185,8 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { bit_len, simd_len: None, vec_len: None, - target: target.to_string(), - }))) + metadata, + })) } } } diff --git a/crates/intrinsic-test/src/common/argument.rs b/crates/intrinsic-test/src/common/argument.rs index 443ccb919f..b7e48eb3f4 100644 --- a/crates/intrinsic-test/src/common/argument.rs +++ b/crates/intrinsic-test/src/common/argument.rs @@ -20,6 +20,15 @@ impl Argument where T: IntrinsicTypeDefinition, { + pub fn new(pos: usize, name: String, ty: T, constraint: Option) -> Self { + Argument { + pos, + name, + ty, + constraint, + } + } + pub fn to_c_type(&self) -> String { self.ty.c_type() } @@ -36,14 +45,6 @@ where self.constraint.is_some() } - pub fn type_and_name_from_c(arg: &str) -> (&str, &str) { - let split_index = arg - .rfind([' ', '*']) - .expect("Couldn't split type and argname"); - - (arg[..split_index + 1].trim_end(), &arg[split_index + 1..]) - } - /// 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() { @@ -62,25 +63,6 @@ where } } - pub fn from_c( - pos: usize, - arg: &str, - target: &str, - constraint: Option, - ) -> Argument { - let (ty, var_name) = Self::type_and_name_from_c(arg); - - let ty = - T::from_c(ty, target).unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); - - Argument { - pos, - name: String::from(var_name), - ty: *ty, - constraint, - } - } - fn as_call_param_c(&self) -> String { self.ty.as_call_param_c(&self.name) } diff --git a/crates/intrinsic-test/src/common/gen_c.rs b/crates/intrinsic-test/src/common/gen_c.rs index 84c28cc4bf..5ea56f6ea5 100644 --- a/crates/intrinsic-test/src/common/gen_c.rs +++ b/crates/intrinsic-test/src/common/gen_c.rs @@ -105,7 +105,6 @@ pub fn generate_c_test_loop( indentation: Indentation, additional: &str, passes: u32, - _target: &str, ) -> String { let body_indentation = indentation.nested(); format!( @@ -157,7 +156,7 @@ pub fn generate_c_constraint_blocks( }) .join("\n") } else { - generate_c_test_loop(intrinsic, indentation, &name, PASSES, target) + generate_c_test_loop(intrinsic, indentation, &name, PASSES) } } diff --git a/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/crates/intrinsic-test/src/common/intrinsic_helpers.rs index 3d200b1946..3ee1a45baa 100644 --- a/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::fmt; use std::ops::Deref; use std::str::FromStr; @@ -12,10 +13,13 @@ use super::values::value_for_array; pub enum TypeKind { BFloat, Float, - Int, - UInt, + + // if signed, then the inner value is true + Int(bool), + Char(bool), Poly, Void, + Mask, } impl FromStr for TypeKind { @@ -23,12 +27,14 @@ impl FromStr for TypeKind { fn from_str(s: &str) -> Result { match s { - "bfloat" => Ok(Self::BFloat), - "float" => Ok(Self::Float), - "int" => Ok(Self::Int), + "bfloat" | "BF16" => Ok(Self::BFloat), + "float" | "double" | "FP16" | "FP32" | "FP64" => Ok(Self::Float), + "int" | "long" | "short" | "SI8" | "SI16" | "SI32" | "SI64" => Ok(Self::Int(true)), "poly" => Ok(Self::Poly), - "uint" | "unsigned" => Ok(Self::UInt), + "char" => Ok(Self::Char(true)), + "uint" | "unsigned" | "UI8" | "UI16" | "UI32" | "UI64" => Ok(Self::Int(false)), "void" => Ok(Self::Void), + "MASK" => Ok(Self::Mask), _ => Err(format!("Impossible to parse argument kind {s}")), } } @@ -42,10 +48,13 @@ impl fmt::Display for TypeKind { match self { Self::BFloat => "bfloat", Self::Float => "float", - Self::Int => "int", - Self::UInt => "uint", + Self::Int(true) => "int", + Self::Int(false) => "uint", Self::Poly => "poly", Self::Void => "void", + Self::Char(true) => "char", + Self::Char(false) => "unsigned char", + Self::Mask => "mask", } ) } @@ -56,9 +65,10 @@ impl TypeKind { pub fn c_prefix(&self) -> &str { match self { Self::Float => "float", - Self::Int => "int", - Self::UInt => "uint", + Self::Int(true) => "int", + Self::Int(false) => "uint", Self::Poly => "poly", + Self::Char(true) => "char", _ => unreachable!("Not used: {:#?}", self), } } @@ -67,8 +77,8 @@ impl TypeKind { pub fn rust_prefix(&self) -> &str { match self { Self::Float => "f", - Self::Int => "i", - Self::UInt => "u", + Self::Int(true) => "i", + Self::Int(false) => "u", Self::Poly => "u", _ => unreachable!("Unused type kind: {:#?}", self), } @@ -100,7 +110,8 @@ pub struct IntrinsicType { /// A value of `None` can be assumed to be 1 though. pub vec_len: Option, - pub target: String, + // pub target: String, + pub metadata: HashMap, } impl IntrinsicType { @@ -112,7 +123,7 @@ impl IntrinsicType { if let Some(bl) = self.bit_len { bl } else { - unreachable!("") + unreachable!("{}", self.kind) } } @@ -132,12 +143,26 @@ impl IntrinsicType { self.ptr } + pub fn set_bit_len(&mut self, value: Option) { + self.bit_len = value; + } + + pub fn set_metadata(&mut self, key: String, value: String) { + self.metadata.insert(key, value); + } + pub fn c_scalar_type(&self) -> String { - format!( - "{prefix}{bits}_t", - prefix = self.kind().c_prefix(), - bits = self.inner_size() - ) + match self { + IntrinsicType { + kind: TypeKind::Char(_), + .. + } => String::from("char"), + _ => format!( + "{prefix}{bits}_t", + prefix = self.kind().c_prefix(), + bits = self.inner_size() + ), + } } pub fn rust_scalar_type(&self) -> String { @@ -155,8 +180,8 @@ impl IntrinsicType { bit_len: Some(8), .. } => match kind { - TypeKind::Int => "(int)", - TypeKind::UInt => "(unsigned int)", + TypeKind::Int(true) => "(int)", + TypeKind::Int(false) => "(unsigned int)", TypeKind::Poly => "(unsigned int)(uint8_t)", _ => "", }, @@ -172,6 +197,21 @@ impl IntrinsicType { 128 => "", _ => panic!("invalid bit_len"), }, + IntrinsicType { + kind: TypeKind::Float, + bit_len: Some(bit_len), + .. + } => match bit_len { + 16 => "(float16_t)", + 32 => "(float)", + 64 => "(double)", + 128 => "", + _ => panic!("invalid bit_len"), + }, + IntrinsicType { + kind: TypeKind::Char(_), + .. + } => "(char)", _ => "", } } @@ -185,7 +225,7 @@ impl IntrinsicType { match self { IntrinsicType { bit_len: Some(bit_len @ (8 | 16 | 32 | 64)), - kind: kind @ (TypeKind::Int | TypeKind::UInt | TypeKind::Poly), + kind: kind @ (TypeKind::Int(_) | TypeKind::Poly | TypeKind::Char(_)), simd_len, vec_len, .. @@ -201,7 +241,7 @@ impl IntrinsicType { .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 { + if *kind == TypeKind::Int(true) && (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; @@ -257,7 +297,7 @@ impl IntrinsicType { .. } => false, IntrinsicType { - kind: TypeKind::Int | TypeKind::UInt | TypeKind::Poly, + kind: TypeKind::Int(_) | TypeKind::Poly, .. } => true, _ => unimplemented!(), @@ -282,7 +322,9 @@ pub trait IntrinsicTypeDefinition: Deref { fn get_lane_function(&self) -> String; /// can be implemented in an `impl` block - fn from_c(_s: &str, _target: &str) -> Result, String>; + fn from_c(_s: &str) -> Result + where + Self: Sized; /// Gets a string containing the typename for this type in C format. /// can be directly defined in `impl` blocks diff --git a/crates/intrinsic-test/src/main.rs b/crates/intrinsic-test/src/main.rs index 054138a0db..c11332b1f4 100644 --- a/crates/intrinsic-test/src/main.rs +++ b/crates/intrinsic-test/src/main.rs @@ -3,10 +3,12 @@ extern crate log; mod arm; mod common; +mod x86; use arm::ArmArchitectureTest; use common::SupportedArchitectureTest; use common::cli::{Cli, ProcessedCli}; +use x86::X86ArchitectureTest; fn main() { pretty_env_logger::init(); @@ -21,6 +23,8 @@ fn main() { Some(ArmArchitectureTest::create(processed_cli_options)) } + "x86_64-unknown-linux-gnu" => Some(X86ArchitectureTest::create(processed_cli_options)), + _ => None, }; diff --git a/crates/intrinsic-test/src/x86/config.rs b/crates/intrinsic-test/src/x86/config.rs new file mode 100644 index 0000000000..c3eeb14f29 --- /dev/null +++ b/crates/intrinsic-test/src/x86/config.rs @@ -0,0 +1,9 @@ +pub fn build_notices(line_prefix: &str) -> String { + format!( + "\ +{line_prefix}This is a transient test file, not intended for distribution. Some aspects of the +{line_prefix}test are derived from a JSON specification, published under the same license as the +{line_prefix}`intrinsic-test` crate.\n +" + ) +} diff --git a/crates/intrinsic-test/src/x86/intrinsic.rs b/crates/intrinsic-test/src/x86/intrinsic.rs new file mode 100644 index 0000000000..d02a42933a --- /dev/null +++ b/crates/intrinsic-test/src/x86/intrinsic.rs @@ -0,0 +1,105 @@ +use crate::common::argument::ArgumentList; +use crate::common::indentation::Indentation; +use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; +use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, TypeKind}; +use std::ops::{Deref, DerefMut}; + +#[derive(Debug, Clone, PartialEq)] +pub struct X86IntrinsicType(pub IntrinsicType); + +impl Deref for X86IntrinsicType { + type Target = IntrinsicType; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for X86IntrinsicType { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl IntrinsicDefinition for Intrinsic { + fn arguments(&self) -> ArgumentList { + self.arguments.clone() + } + + fn results(&self) -> X86IntrinsicType { + self.results.clone() + } + + fn name(&self) -> String { + self.name.clone() + } + + /// 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 { + 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()) + .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(), + lane = idx, + vector = vector, + ) + }) + .collect::>() + .join(r#" << ", " << "#) + ) + }) + .collect::>() + .join(r#" << ", " << "#) + } 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(), + lane = idx + ) + }) + .collect::>() + .join(r#" << ", " << "#) + } else { + format!( + "{promote}cast<{cast}>(__return_value)", + cast = match self.results.kind() { + TypeKind::Void => "void".to_string(), + TypeKind::Float if self.results().inner_size() == 64 => "double".to_string(), + TypeKind::Float if self.results().inner_size() == 32 => "float".to_string(), + // TypeKind::Float if self.results().inner_size() == 16 => "float16_t".to_string(), + // TypeKind::Int(true) if self.results().inner_size() == 64 => "long".to_string(), + // TypeKind::Int(false) if self.results().inner_size() == 64 => "unsigned long".to_string(), + // TypeKind::Int(true) if self.results().inner_size() == 32 => "int".to_string(), + // TypeKind::Int(false) if self.results().inner_size() == 32 => "unsigned int".to_string(), + // TypeKind::Int(true) if self.results().inner_size() == 16 => "short".to_string(), + // TypeKind::Int(false) if self.results().inner_size() == 16 => "unsigned short".to_string(), + _ => self.results.c_scalar_type(), + }, + 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()) + } else { + String::from("") + }, + close = if self.results.is_simd() { ")" } else { "" }, + ) + } +} diff --git a/crates/intrinsic-test/src/x86/mod.rs b/crates/intrinsic-test/src/x86/mod.rs new file mode 100644 index 0000000000..65100af989 --- /dev/null +++ b/crates/intrinsic-test/src/x86/mod.rs @@ -0,0 +1,76 @@ +mod config; +mod intrinsic; +mod types; +mod xml_parser; + +use crate::common::SupportedArchitectureTest; +use crate::common::cli::ProcessedCli; +use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition}; +use crate::common::intrinsic_helpers::TypeKind; +use crate::common::write_file::write_c_testfiles; +use config::build_notices; +use intrinsic::X86IntrinsicType; +use xml_parser::get_xml_intrinsics; + +pub struct X86ArchitectureTest { + intrinsics: Vec>, + cli_options: ProcessedCli, +} + +impl SupportedArchitectureTest for X86ArchitectureTest { + fn create(cli_options: ProcessedCli) -> Box { + let mut intrinsics = + get_xml_intrinsics(&cli_options.filename).expect("Error parsing input file"); + + intrinsics.sort_by(|a, b| a.name.cmp(&b.name)); + let intrinsics = intrinsics + .into_iter() + // Not sure how we would compare intrinsic that returns void. + .filter(|i| i.results.kind() != TypeKind::Void) + .filter(|i| i.results.kind() != TypeKind::BFloat) + .filter(|i| i.arguments().args.len() > 0) + .filter(|i| !i.arguments.iter().any(|a| a.ty.kind() == TypeKind::BFloat)) + // Skip pointers for now, we would probably need to look at the return + // type to work out how many elements we need to point to. + .filter(|i| !i.arguments.iter().any(|a| a.is_ptr())) + .filter(|i| !i.arguments.iter().any(|a| a.ty.inner_size() == 128)) + .filter(|i| !cli_options.skip.contains(&i.name)) + .collect::>(); + + Box::new(Self { + intrinsics: intrinsics, + cli_options: cli_options, + }) + } + + fn build_c_file(&self) -> bool { + // let compiler = self.cli_options.cpp_compiler.as_deref(); + let target = &self.cli_options.target; + // let cxx_toolchain_dir = self.cli_options.cxx_toolchain_dir.as_deref(); + let c_target = "x86_64"; + + /* let intrinsics_name_list = */ + write_c_testfiles( + &self + .intrinsics + .iter() + .map(|i| i as &dyn IntrinsicDefinition<_>) + .collect::>(), + target, + c_target, + &["immintrin.h"], + &build_notices("// "), + &[], + ); + + true + } + + fn build_rust_file(&self) -> bool { + todo!("build_rust_file in X86ArchitectureTest is not implemented") + } + + fn compare_outputs(&self) -> bool { + todo!("compare_outputs in X86ArchitectureTest is not implemented") + } +} diff --git a/crates/intrinsic-test/src/x86/types.rs b/crates/intrinsic-test/src/x86/types.rs new file mode 100644 index 0000000000..b3e29898d6 --- /dev/null +++ b/crates/intrinsic-test/src/x86/types.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; +use std::str::FromStr; + +use super::intrinsic::X86IntrinsicType; +use crate::common::cli::Language; +use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, TypeKind}; + +impl IntrinsicTypeDefinition for X86IntrinsicType { + /// Gets a string containing the type in C format. + /// This function assumes that this value is present in the metadata hashmap. + fn c_type(&self) -> String { + self.metadata + .get("type") + .expect("Failed to extract the C typename in X86!") + .to_string() + } + + fn c_single_vector_type(&self) -> String { + todo!("c_single_vector_type for X86IntrinsicType needs to be implemented!"); + } + + fn rust_type(&self) -> String { + todo!("rust_type for X86IntrinsicType needs to be implemented!"); + } + + /// Determines the load function for this type. + fn get_load_function(&self, _language: Language) -> String { + todo!("get_load_function for X86IntrinsicType needs to be implemented!"); + } + + /// Determines the get lane function for this type. + fn get_lane_function(&self) -> String { + todo!("get_lane_function for X86IntrinsicType needs to be implemented!"); + } + + fn from_c(s: &str) -> Result { + let mut s_copy = s.to_string(); + let mut metadata: HashMap = HashMap::new(); + metadata.insert("type".to_string(), s.to_string()); + s_copy = s_copy + .replace("*", "") + .replace("constexpr", "") + .replace("const", "") + .replace("literal", ""); + + let s_split = s_copy + .split(" ") + .filter_map(|s| if s.len() == 0 { None } else { Some(s) }) + .last(); + + // TODO: add more intrinsics by modifying + // functionality below this line. + // Currently all the intrinsics that have an "_" + // is ignored. + if s.matches("_").next().is_some() { + return Err(String::from("This functionality needs to be implemented")); + }; + + // TODO: make the unwrapping safe + let kind = TypeKind::from_str(s_split.unwrap()).expect("Unable to parse type!"); + + let kind = if s.find("unsigned").is_some() { + match kind { + TypeKind::Int(_) => TypeKind::Int(false), + TypeKind::Char(_) => TypeKind::Char(false), + a => a, + } + } else { + kind + }; + let ptr_constant = false; + let mut constant = false; + let mut ptr = false; + + if s.matches("const").next().is_some() { + constant = true; + }; + if s.matches("*").next().is_some() { + ptr = true; + }; + + Ok(X86IntrinsicType(IntrinsicType { + ptr, + ptr_constant, + constant, + kind, + bit_len: None, + simd_len: None, + vec_len: None, + metadata, + })) + } +} diff --git a/crates/intrinsic-test/src/x86/xml_parser.rs b/crates/intrinsic-test/src/x86/xml_parser.rs new file mode 100644 index 0000000000..ac7acd970e --- /dev/null +++ b/crates/intrinsic-test/src/x86/xml_parser.rs @@ -0,0 +1,159 @@ +use crate::common::argument::{Argument, ArgumentList}; +use crate::common::intrinsic::Intrinsic; +use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, TypeKind}; + +use serde::{Deserialize, Deserializer}; +use std::path::Path; + +use super::intrinsic::X86IntrinsicType; + +// Custom deserializer function to convert "TRUE"/"FALSE" strings to boolean +// fn string_to_bool<'de, D>(deserializer: D) -> Result +// where +// D: Deserializer<'de>, +// { +// let s = String::deserialize(deserializer)?; +// match s.as_str() { +// "TRUE" => Ok(true), +// "FALSE" => Ok(false), +// _ => Ok(false), // Default to false for any other value +// } +// } + +// Custom deserializer function to convert strings to u16 +fn string_to_u16<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + return Ok(s.as_str().parse::().unwrap_or(0u16)); +} + +#[derive(Deserialize)] +struct Data { + #[serde(rename = "intrinsic", default)] + intrinsics: Vec, +} + +#[derive(Deserialize)] +struct XMLIntrinsic { + #[serde(rename = "return")] + return_data: Parameter, + #[serde(rename = "@name")] + name: String, + // #[serde(rename = "@tech")] + // tech: String, + #[serde(rename = "CPUID", default)] + cpuid: Vec, + #[serde(rename = "parameter", default)] + parameters: Vec, + // #[serde(rename = "@sequence", default, deserialize_with = "string_to_bool")] + // generates_sequence: bool, + // #[serde(default)] + // instruction: Vec, +} + +#[derive(Deserialize)] +struct Parameter { + #[serde(rename = "@type")] + type_data: String, + #[serde(rename = "@etype", default)] + etype: String, + #[serde(rename = "@memwidth", default, deserialize_with = "string_to_u16")] + memwidth: u16, + #[serde(rename = "@varname", default)] + var_name: String, +} + +// #[derive(Deserialize, Debug)] +// struct Instruction { +// #[serde(rename = "@name")] +// name: String, +// } + +pub fn get_xml_intrinsics( + filename: &Path, +) -> Result>, Box> { + let file = std::fs::File::open(filename)?; + let reader = std::io::BufReader::new(file); + let data: Data = + quick_xml::de::from_reader(reader).expect("failed to deserialize the source XML file"); + + let parsed_intrinsics: Vec> = data + .intrinsics + .into_iter() + .filter_map(|intr| { + // Some(xml_to_intrinsic(intr, target).expect("Couldn't parse XML properly!")) + xml_to_intrinsic(intr).ok() + }) + .collect(); + + Ok(parsed_intrinsics) +} + +fn parse_observable(param: &Parameter) -> Result { + let ty = X86IntrinsicType::from_c(param.type_data.as_str()); + + if let Err(_) = ty { + return ty; + } + let mut ty_bit_len = param.etype.clone(); + ty_bit_len.retain(|c| c.is_numeric()); + let ty_bit_len = str::parse::(ty_bit_len.as_str()).ok(); + let mut ty = ty.unwrap(); + let ty_bit_len = match ty_bit_len { + None => match ty.kind { + TypeKind::Int(_) => Some(32), + TypeKind::Float => Some(32), + TypeKind::BFloat => Some(16), + TypeKind::Void => Some(param.memwidth as u32), + _ => None, + }, + ty => ty, + }; + ty.set_bit_len(ty_bit_len); + Ok(ty) +} + +fn xml_to_intrinsic( + intr: XMLIntrinsic, +) -> Result, Box> { + let name = intr.name; + let result = parse_observable(&intr.return_data); + let args_check = intr.parameters.into_iter().enumerate().map(|(i, param)| { + let ty = parse_observable(¶m); + if let Err(_) = ty { + return None; + } + let constraint = None; + let mut arg = Argument::::new(i, param.var_name, ty.unwrap(), constraint); + let IntrinsicType { + ref mut constant, .. + } = arg.ty.0; + if param.etype == "IMM" { + *constant = true + } + Some(arg) + }); + + let args = args_check.collect::>(); + if args.iter().any(|elem| elem.is_none()) { + return Err(Box::from("intrinsic isn't fully supported in this test!")); + } + let args = args + .into_iter() + .map(|e| e.unwrap()) + .filter(|arg| arg.ty.ptr || arg.ty.kind != TypeKind::Void) + .collect::>(); + let arguments = ArgumentList:: { args }; + + if let Err(message) = result { + return Err(Box::from(message)); + } + Ok(Intrinsic { + name, + arguments, + results: result.unwrap(), + arch_tags: intr.cpuid, + }) +}