| use crate::common::argument::{Argument, ArgumentList}; |
| use crate::common::intrinsic::Intrinsic; |
| use crate::common::intrinsic_helpers::TypeKind; |
| use crate::x86::constraint::map_constraints; |
| |
| use regex::Regex; |
| use serde::{Deserialize, Deserializer}; |
| use std::path::Path; |
| |
| use super::intrinsic::X86IntrinsicType; |
| |
| // Custom deserializer function to convert strings to u32 |
| fn string_to_u32<'de, D>(deserializer: D) -> Result<u32, D::Error> |
| where |
| D: Deserializer<'de>, |
| { |
| let s = String::deserialize(deserializer)?; |
| return s.as_str().parse::<u32>().or(Ok(0u32)); |
| } |
| |
| #[derive(Deserialize)] |
| struct Data { |
| #[serde(rename = "intrinsic", default)] |
| intrinsics: Vec<XMLIntrinsic>, |
| } |
| |
| #[derive(Deserialize)] |
| struct XMLIntrinsic { |
| #[serde(rename = "return")] |
| pub return_data: Parameter, |
| #[serde(rename = "@name")] |
| pub name: String, |
| #[serde(rename = "@tech")] |
| tech: String, |
| #[serde(rename = "CPUID", default)] |
| cpuid: Vec<String>, |
| #[serde(rename = "parameter", default)] |
| parameters: Vec<Parameter>, |
| } |
| |
| #[derive(Debug, PartialEq, Clone, Deserialize)] |
| pub struct Parameter { |
| #[serde(rename = "@varname", default)] |
| pub var_name: String, |
| #[serde(rename = "@type", default)] |
| pub type_data: String, |
| #[serde(rename = "@etype", default)] |
| pub etype: String, |
| #[serde(rename = "@memwidth", default, deserialize_with = "string_to_u32")] |
| pub memwidth: u32, |
| #[serde(rename = "@immwidth", default, deserialize_with = "string_to_u32")] |
| pub imm_width: u32, |
| #[serde(rename = "@immtype", default)] |
| pub imm_type: String, |
| } |
| |
| pub fn get_xml_intrinsics( |
| filename: &Path, |
| ) -> Result<Vec<Intrinsic<X86IntrinsicType>>, Box<dyn std::error::Error>> { |
| 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<Intrinsic<X86IntrinsicType>> = data |
| .intrinsics |
| .into_iter() |
| .filter(|intrinsic| { |
| intrinsic.tech != "SVML" |
| && intrinsic.tech != "MMX" |
| && !intrinsic.cpuid.contains(&"MPX".to_string()) |
| && intrinsic.return_data.type_data != "__m64" |
| && !intrinsic |
| .parameters |
| .iter() |
| .any(|param| param.type_data.contains("__m64")) |
| }) |
| .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 xml_to_intrinsic( |
| intr: XMLIntrinsic, |
| ) -> Result<Intrinsic<X86IntrinsicType>, Box<dyn std::error::Error>> { |
| let name = intr.name; |
| let result = X86IntrinsicType::from_param(&intr.return_data); |
| let args_check = intr.parameters.into_iter().enumerate().map(|(i, param)| { |
| let ty = X86IntrinsicType::from_param(¶m); |
| if ty.is_err() { |
| None |
| } else { |
| let effective_imm_width = if name == "_mm_mpsadbw_epu8" && param.var_name == "imm8" { |
| 3 |
| } else { |
| param.imm_width |
| }; |
| let constraint = map_constraints(&name, ¶m.imm_type, effective_imm_width); |
| let arg = Argument::<X86IntrinsicType>::new( |
| i, |
| param.var_name.clone(), |
| ty.unwrap(), |
| constraint, |
| ); |
| Some(arg) |
| } |
| }); |
| |
| let args = args_check.collect::<Vec<_>>(); |
| if args.iter().any(|elem| elem.is_none()) { |
| return Err(Box::from("intrinsic isn't fully supported in this test!")); |
| } |
| let mut args = args |
| .into_iter() |
| .map(|e| e.unwrap()) |
| .filter(|arg| arg.ty.ptr || arg.ty.kind != TypeKind::Void) |
| .collect::<Vec<_>>(); |
| |
| let mut args_test = args.iter(); |
| |
| // if one of the args has etype="MASK" and type="__m<int>d", |
| // then set the bit_len and simd_len accordingly |
| let re = Regex::new(r"__m\d+").unwrap(); |
| let is_mask = |arg: &Argument<X86IntrinsicType>| arg.ty.param.etype.as_str() == "MASK"; |
| let is_vector = |arg: &Argument<X86IntrinsicType>| re.is_match(arg.ty.param.type_data.as_str()); |
| let pos = args_test.position(|arg| is_mask(arg) && is_vector(arg)); |
| if let Some(index) = pos { |
| args[index].ty.bit_len = args[0].ty.bit_len; |
| } |
| |
| args.iter_mut().for_each(|arg| arg.ty.update_simd_len()); |
| |
| let arguments = ArgumentList::<X86IntrinsicType> { args }; |
| |
| if let Err(message) = result { |
| return Err(Box::from(message)); |
| } |
| |
| Ok(Intrinsic { |
| name, |
| arguments, |
| results: result.unwrap(), |
| arch_tags: intr.cpuid, |
| }) |
| } |