diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:19 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-30 03:57:19 +0000 |
commit | a0b8f38ab54ac451646aa00cd5e91b6c76f22a84 (patch) | |
tree | fc451898ccaf445814e26b46664d78702178101d /library/stdarch/crates/intrinsic-test | |
parent | Adding debian version 1.71.1+dfsg1-2. (diff) | |
download | rustc-a0b8f38ab54ac451646aa00cd5e91b6c76f22a84.tar.xz rustc-a0b8f38ab54ac451646aa00cd5e91b6c76f22a84.zip |
Merging upstream version 1.72.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'library/stdarch/crates/intrinsic-test')
8 files changed, 268 insertions, 571 deletions
diff --git a/library/stdarch/crates/intrinsic-test/Cargo.toml b/library/stdarch/crates/intrinsic-test/Cargo.toml index 9b6162ab8..d977dd659 100644 --- a/library/stdarch/crates/intrinsic-test/Cargo.toml +++ b/library/stdarch/crates/intrinsic-test/Cargo.toml @@ -1,13 +1,16 @@ [package] name = "intrinsic-test" version = "0.1.0" -authors = ["Jamie Cunliffe <Jamie.Cunliffe@arm.com>"] -edition = "2021" +authors = ["Jamie Cunliffe <Jamie.Cunliffe@arm.com>", + "James McGregor <James.McGregor2@arm.com", + "Adam Gemmell <Adam.Gemmell@arm.com"] license = "MIT OR Apache-2.0" +edition = "2021" [dependencies] lazy_static = "1.4.0" serde = { version = "1", features = ["derive"] } +serde_json = "1.0" csv = "1.1" clap = "2.33.3" regex = "1.4.2" diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64.txt index 93fc126e5..b09d677af 100644 --- a/library/stdarch/crates/intrinsic-test/missing_aarch64.txt +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64.txt @@ -23,39 +23,6 @@ vusdotq_lane_s32 vusdotq_s32 vusdot_s32 -# Implemented in Clang but missing from CSV -vcmla_f64 -vcmla_lane_f64 -vcmla_laneq_f64 -vcmlaq_lane_f64 -vcmlaq_laneq_f64 -vcmlaq_rot180_lane_f64 -vcmlaq_rot180_laneq_f64 -vcmlaq_rot270_lane_f64 -vcmlaq_rot270_laneq_f64 -vcmlaq_rot90_lane_f64 -vcmlaq_rot90_laneq_f64 -vcmla_rot180_f64 -vcmla_rot180_lane_f64 -vcmla_rot180_laneq_f64 -vcmla_rot270_f64 -vcmla_rot270_lane_f64 -vcmla_rot270_laneq_f64 -vcmla_rot90_f64 -vcmla_rot90_lane_f64 -vcmla_rot90_laneq_f64 - -# Implemented in Clang and stdarch but missing from CSV -vmov_n_p64 -vmovq_n_p64 -vreinterpret_f32_p64 -vreinterpret_p64_s64 -vreinterpretq_f32_p128 -vreinterpretq_f32_p64 -vreinterpretq_p128_p64 -vreinterpretq_p64_p128 -vtst_p16 -vtstq_p16 # Missing from both Clang and stdarch vrnd32x_f64 @@ -67,30 +34,17 @@ vrnd64xq_f64 vrnd64z_f64 vrnd64zq_f64 -# QEMU 6.0 doesn't support these instructions -vmmlaq_s32 -vmmlaq_u32 -vsm3partw1q_u32 -vsm3partw2q_u32 -vsm3ss1q_u32 -vsm3tt1aq_u32 -vsm3tt1bq_u32 -vsm3tt2aq_u32 -vsm3tt2bq_u32 -vsm4ekeyq_u32 -vsm4eq_u32 -vusmmlaq_s32 - # LLVM select error in debug builds -vqshlu_n_s16 -vqshlu_n_s32 -vqshlu_n_s64 -vqshlu_n_s8 -vqshlub_n_s8 -vqshlud_n_s64 -vqshluh_n_s16 -vqshluq_n_s16 -vqshluq_n_s32 -vqshluq_n_s64 -vqshluq_n_s8 -vqshlus_n_s32 +#vqshlu_n_s16 +#vqshlu_n_s32 +#vqshlu_n_s64 +#vqshlu_n_s8 +#vqshlub_n_s8 +#vqshlud_n_s64 +#vqshluh_n_s16 +#vqshluq_n_s16 +#vqshluq_n_s32 +#vqshluq_n_s64 +#vqshluq_n_s8 +#vqshlus_n_s32 + diff --git a/library/stdarch/crates/intrinsic-test/missing_arm.txt b/library/stdarch/crates/intrinsic-test/missing_arm.txt index 3d7ead062..3acc61678 100644 --- a/library/stdarch/crates/intrinsic-test/missing_arm.txt +++ b/library/stdarch/crates/intrinsic-test/missing_arm.txt @@ -23,15 +23,6 @@ vusdotq_lane_s32 vusdotq_s32 vusdot_s32 -# Implemented in Clang and stdarch but missing from CSV -vtst_p16 -vtstq_p16 - -# QEMU 6.0 doesn't support these instructions -vmmlaq_s32 -vmmlaq_u32 -vusmmlaq_s32 - # Implemented in Clang and stdarch for A64 only even though CSV claims A32 support __crc32d __crc32cd @@ -214,110 +205,29 @@ vrndx_f32 vrndxq_f32 # LLVM select error in debug builds -vqrshrn_n_s16 -vqrshrn_n_s32 -vqrshrn_n_s64 -vqrshrn_n_u16 -vqrshrn_n_u32 -vqrshrn_n_u64 -vqrshrun_n_s16 -vqrshrun_n_s32 -vqrshrun_n_s64 -vqshrn_n_s16 -vqshrn_n_s32 -vqshrn_n_s64 -vqshrn_n_u16 -vqshrn_n_u32 -vqshrn_n_u64 -vqshrun_n_s16 -vqshrun_n_s32 -vqshrun_n_s64 -vrshrn_n_s16 -vrshrn_n_s32 -vrshrn_n_s64 -vrshrn_n_u16 -vrshrn_n_u32 -vrshrn_n_u64 -vshrq_n_u64 -vshr_n_u64 - -# Failing tests: stdarch has incorrect results compared to Clang -vqshlu_n_s16 -vqshlu_n_s32 -vqshlu_n_s64 -vqshlu_n_s8 -vqshluq_n_s16 -vqshluq_n_s32 -vqshluq_n_s64 -vqshluq_n_s8 -vsli_n_p16 -vsli_n_p8 -vsli_n_s16 -vsli_n_s32 -vsli_n_s64 -vsli_n_s8 -vsli_n_u16 -vsli_n_u32 -vsli_n_u64 -vsli_n_u8 -vsliq_n_p16 -vsliq_n_p8 -vsliq_n_s16 -vsliq_n_s32 -vsliq_n_s64 -vsliq_n_s8 -vsliq_n_u16 -vsliq_n_u32 -vsliq_n_u64 -vsliq_n_u8 -vsri_n_p16 -vsri_n_p8 -vsri_n_s16 -vsri_n_s32 -vsri_n_s64 -vsri_n_s8 -vsri_n_u16 -vsri_n_u32 -vsri_n_u64 -vsri_n_u8 -vsriq_n_p16 -vsriq_n_p8 -vsriq_n_s16 -vsriq_n_s32 -vsriq_n_s64 -vsriq_n_s8 -vsriq_n_u16 -vsriq_n_u32 -vsriq_n_u64 -vsriq_n_u8 - -# These produce a different result on Clang depending on the optimization level. -# This is definitely a bug in LLVM. -vadd_f32 -vaddq_f32 -vcvt_s32_f32 -vcvt_u32_f32 -vcvtq_s32_f32 -vcvtq_u32_f32 -vfma_f32 -vfma_n_f32 -vfmaq_f32 -vfmaq_n_f32 -vfms_f32 -vfmsq_f32 -vmla_f32 -vmla_lane_f32 -vmla_n_f32 -vmlaq_f32 -vmlaq_lane_f32 -vmlaq_n_f32 -vmls_f32 -vmls_lane_f32 -vmls_n_f32 -vmlsq_f32 -vmlsq_lane_f32 -vmlsq_n_f32 -vmul_lane_f32 -vmul_n_f32 -vmulq_lane_f32 -vmulq_n_f32 +#vqrshrn_n_s16 +#vqrshrn_n_s32 +#vqrshrn_n_s64 +#vqrshrn_n_u16 +#vqrshrn_n_u32 +#vqrshrn_n_u64 +#vqrshrun_n_s16 +#vqrshrun_n_s32 +#vqrshrun_n_s64 +#vqshrn_n_s16 +#vqshrn_n_s32 +#vqshrn_n_s64 +#vqshrn_n_u16 +#vqshrn_n_u32 +#vqshrn_n_u64 +#vqshrun_n_s16 +#vqshrun_n_s32 +#vqshrun_n_s64 +#vrshrn_n_s16 +#vrshrn_n_s32 +#vrshrn_n_s64 +#vrshrn_n_u16 +#vrshrn_n_u32 +#vrshrn_n_u64 +#vshrq_n_u64 +#vshr_n_u64 diff --git a/library/stdarch/crates/intrinsic-test/src/acle_csv_parser.rs b/library/stdarch/crates/intrinsic-test/src/acle_csv_parser.rs deleted file mode 100644 index d21041676..000000000 --- a/library/stdarch/crates/intrinsic-test/src/acle_csv_parser.rs +++ /dev/null @@ -1,363 +0,0 @@ -use itertools::Itertools; -use regex::Regex; -use serde::Deserialize; - -use crate::argument::{Argument, ArgumentList, Constraint}; -use crate::intrinsic::Intrinsic; -use crate::types::{IntrinsicType, TypeKind}; - -pub struct CsvMetadata { - notices: String, - spdx_lic: String, -} - -impl CsvMetadata { - fn new<'a>(header: impl Iterator<Item = &'a str>) -> Self { - lazy_static! { - static ref SPDX_LICENSE_IDENTIFIER: Regex = - Regex::new(r#"SPDX-License-Identifier:(.*)"#).unwrap(); - } - - let notices = header.map(|line| format!("{line}\n")).collect::<String>(); - let spdx_lic = match SPDX_LICENSE_IDENTIFIER - .captures_iter(¬ices) - .exactly_one() - { - Ok(caps) => { - let cap = caps.get(1).unwrap().as_str().trim(); - // Ensure that (unlikely) ACLE licence changes don't go unnoticed. - assert_eq!(cap, "Apache-2.0"); - cap.to_string() - } - Err(caps_iter) => panic!( - "Expected exactly one SPDX-License-Identifier, found {}.", - caps_iter.count() - ), - }; - - Self { notices, spdx_lic } - } - - pub fn spdx_license_identifier(&self) -> &str { - self.spdx_lic.as_str() - } - - pub fn notices_lines(&self) -> impl Iterator<Item = &str> { - self.notices.lines() - } -} - -pub fn get_acle_intrinsics(filename: &str) -> (CsvMetadata, Vec<Intrinsic>) { - let data = std::fs::read_to_string(filename).expect("Failed to open ACLE intrinsics file"); - - let comment_header = data.lines().map_while(|l| l.strip_prefix("<COMMENT>\t")); - let meta = CsvMetadata::new(comment_header); - - let data = data - .lines() - .filter_map(|l| { - (!(l.starts_with("<COMMENT>") || l.is_empty() || l.starts_with("<SECTION>"))) - .then(|| l.replace("<HEADER>\t", "")) - }) - .join("\n"); - - let mut csv_reader = csv::ReaderBuilder::new() - .delimiter(b'\t') - .from_reader(data.as_bytes()); - - let mut intrinsics: Vec<Intrinsic> = csv_reader - .deserialize() - .filter_map(|x: Result<ACLEIntrinsicLine, _>| x.ok().map(|i| i.into())) - .collect::<Vec<_>>(); - - // Intrinsics such as vshll_n_s8 exist twice in the ACLE with different constraints. - intrinsics.sort_by(|a, b| a.name.cmp(&b.name)); - let (intrinsics, duplicates) = intrinsics.partition_dedup_by(|a, b| a.name == b.name); - for duplicate in duplicates { - let name = &duplicate.name; - let constraints = duplicate - .arguments - .args - .drain(..) - .filter(|a| a.has_constraint()); - let intrinsic = intrinsics.iter_mut().find(|i| &i.name == name).unwrap(); - - for mut constraint in constraints { - let real_constraint = intrinsic - .arguments - .args - .iter_mut() - .find(|a| a.name == constraint.name) - .unwrap(); - real_constraint - .constraints - .push(constraint.constraints.pop().unwrap()); - } - } - - (meta, intrinsics.to_vec()) -} - -impl Into<Intrinsic> for ACLEIntrinsicLine { - fn into(self) -> Intrinsic { - let signature = self.intrinsic; - let (ret_ty, remaining) = signature.split_once(' ').unwrap(); - - let results = - type_from_c(ret_ty).unwrap_or_else(|_| panic!("Failed to parse return type: {ret_ty}")); - - let (name, args) = remaining.split_once('(').unwrap(); - let args = args.trim_end_matches(')'); - - // Typo in ACLE data - let args = args.replace("int16x8q_t", "int16x8_t"); - - let arg_prep = self.argument_preparation.as_str(); - let args = args - .split(',') - .enumerate() - .map(move |(idx, arg)| { - let arg = arg.trim(); - if arg.starts_with("__builtin_constant_p") { - handle_constraint(idx, arg, arg_prep) - } else { - from_c(idx, arg) - } - }) - .collect(); - let arguments = ArgumentList { args }; - let a64_only = match &*self.supported_architectures { - "A64" => true, - "v7/A32/A64" | "A32/A64" => false, - _ => panic!("Invalid supported architectures"), - }; - - Intrinsic { - name: name.to_string(), - arguments, - results, - a64_only, - } - } -} - -fn handle_constraint(idx: usize, arg: &str, prep: &str) -> Argument { - let prep = prep.replace(' ', ""); - - let name = arg - .trim_start_matches("__builtin_constant_p") - .trim_start_matches(|ref c| c == &' ' || c == &'(') - .trim_end_matches(')') - .to_string(); - - let ty = IntrinsicType::Type { - constant: true, - kind: TypeKind::Int, - bit_len: Some(32), - simd_len: None, - vec_len: None, - }; - - let constraints = prep - .split(';') - .find_map(|p| handle_range_constraint(&name, p).or_else(|| handle_eq_constraint(&name, p))) - .map(|c| vec![c]) - .unwrap_or_default(); - - Argument { - pos: idx, - name, - ty, - constraints, - } -} - -fn handle_range_constraint(name: &str, data: &str) -> Option<Constraint> { - lazy_static! { - static ref RANGE_CONSTRAINT: Regex = - Regex::new(r#"([0-9]+)<=([[:alnum:]]+)<=([0-9]+)"#).unwrap(); - } - - let captures = RANGE_CONSTRAINT.captures(data)?; - if captures.get(2).map(|c| c.as_str() == name).unwrap_or(false) { - match (captures.get(1), captures.get(3)) { - (Some(start), Some(end)) => { - let start = start.as_str().parse::<i64>().unwrap(); - let end = end.as_str().parse::<i64>().unwrap() + 1; - Some(Constraint::Range(start..end)) - } - _ => panic!("Invalid constraint"), - } - } else { - None - } -} - -fn handle_eq_constraint(name: &str, data: &str) -> Option<Constraint> { - lazy_static! { - static ref EQ_CONSTRAINT: Regex = Regex::new(r#"([[:alnum:]]+)==([0-9]+)"#).unwrap(); - } - let captures = EQ_CONSTRAINT.captures(data)?; - if captures.get(1).map(|c| c.as_str() == name).unwrap_or(false) { - captures - .get(2) - .map(|c| Constraint::Equal(c.as_str().parse::<i64>().unwrap())) - } else { - None - } -} - -fn from_c(pos: usize, s: &str) -> Argument { - let name_index = s - .chars() - .rev() - .take_while(|c| c != &'*' && c != &' ') - .count(); - - let name_start = s.len() - name_index; - let name = s[name_start..].to_string(); - let s = s[..name_start].trim(); - - Argument { - pos, - name, - ty: type_from_c(s).unwrap_or_else(|_| panic!("Failed to parse type: {s}")), - constraints: vec![], - } -} - -fn type_from_c(s: &str) -> Result<IntrinsicType, String> { - const CONST_STR: &str = "const "; - - if let Some(s) = s.strip_suffix('*') { - let (s, constant) = if s.ends_with(CONST_STR) { - (&s[..s.len() - (CONST_STR.len() + 1)], true) - } else { - (s, false) - }; - - let s = s.trim_end(); - - Ok(IntrinsicType::Ptr { - constant, - child: Box::new(type_from_c(s)?), - }) - } else { - // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t] - - let (mut s, constant) = if let Some(s) = s.strip_prefix(CONST_STR) { - (s, true) - } else { - (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::<TypeKind>()?; - let bit_len = bit_len.parse::<u32>().map_err(|err| err.to_string())?; - - let simd_len = parts.next().map(|part| part.parse::<u32>().ok()).flatten(); - let vec_len = parts.next().map(|part| part.parse::<u32>().ok()).flatten(); - - Ok(IntrinsicType::Type { - constant, - kind: arg_kind, - bit_len: Some(bit_len), - simd_len, - vec_len, - }) - } else { - Ok(IntrinsicType::Type { - constant, - kind: start.parse::<TypeKind>()?, - bit_len: None, - simd_len: None, - vec_len: None, - }) - } - } -} - -#[derive(Deserialize, Debug, PartialEq, Clone)] -struct ACLEIntrinsicLine { - #[serde(rename = "Intrinsic")] - intrinsic: String, - #[serde(rename = "Argument preparation")] - argument_preparation: String, - #[serde(rename = "AArch64 Instruction")] - aarch64_instruction: String, - #[serde(rename = "Result")] - result: String, - #[serde(rename = "Supported architectures")] - supported_architectures: String, -} - -#[cfg(test)] -mod test { - use super::*; - use crate::argument::Argument; - use crate::types::{IntrinsicType, TypeKind}; - - #[test] - fn parse_simd() { - let expected = Argument { - pos: 0, - name: "a".into(), - ty: IntrinsicType::Type { - constant: false, - kind: TypeKind::Int, - bit_len: Some(32), - simd_len: Some(4), - vec_len: None, - }, - constraints: vec![], - }; - let actual = from_c(0, "int32x4_t a"); - assert_eq!(expected, actual); - } - - #[test] - fn parse_simd_with_vec() { - let expected = Argument { - pos: 0, - name: "a".into(), - ty: IntrinsicType::Type { - constant: false, - kind: TypeKind::Int, - bit_len: Some(32), - simd_len: Some(4), - vec_len: Some(2), - }, - constraints: vec![], - }; - let actual = from_c(0, "int32x4x2_t a"); - assert_eq!(expected, actual); - } - - #[test] - fn test_ptr() { - let expected = Argument { - pos: 0, - name: "ptr".into(), - ty: crate::types::IntrinsicType::Ptr { - constant: true, - child: Box::new(IntrinsicType::Type { - constant: false, - kind: TypeKind::Int, - bit_len: Some(8), - simd_len: None, - vec_len: None, - }), - }, - constraints: vec![], - }; - let actual = from_c(0, "int8_t const *ptr"); - assert_eq!(expected, actual); - } -} diff --git a/library/stdarch/crates/intrinsic-test/src/argument.rs b/library/stdarch/crates/intrinsic-test/src/argument.rs index 798854c03..c2f9f9450 100644 --- a/library/stdarch/crates/intrinsic-test/src/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/argument.rs @@ -1,5 +1,6 @@ use std::ops::Range; +use crate::json_parser::ArgPrep; use crate::types::{IntrinsicType, TypeKind}; use crate::Language; @@ -22,6 +23,26 @@ pub enum Constraint { Range(Range<i64>), } +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 { @@ -47,6 +68,30 @@ impl Argument { pub fn has_constraint(&self) -> bool { !self.constraints.is_empty() } + + 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..]) + } + + pub fn from_c(pos: usize, arg: &str, arg_prep: Option<ArgPrep>) -> Argument { + let (ty, var_name) = Self::type_and_name_from_c(arg); + + let ty = IntrinsicType::from_c(ty) + .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); + + let constraint = arg_prep.and_then(|a| a.try_into().ok()); + + Argument { + pos, + name: String::from(var_name), + ty, + constraints: constraint.map_or(vec![], |r| vec![r]), + } + } } #[derive(Debug, PartialEq, Clone)] diff --git a/library/stdarch/crates/intrinsic-test/src/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/json_parser.rs new file mode 100644 index 000000000..bc6fa4a9e --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/src/json_parser.rs @@ -0,0 +1,97 @@ +use std::collections::HashMap; + +use serde::Deserialize; + +use crate::argument::{Argument, ArgumentList}; +use crate::intrinsic::Intrinsic; +use crate::types::IntrinsicType; + +#[derive(Deserialize, Debug)] +#[serde(deny_unknown_fields)] +struct ReturnType { + value: String, +} + +#[derive(Deserialize, Debug)] +#[serde(untagged, deny_unknown_fields)] +pub enum ArgPrep { + Register { + #[serde(rename = "register")] + reg: String, + }, + Immediate { + #[serde(rename = "minimum")] + min: i64, + #[serde(rename = "maximum")] + max: i64, + }, + Nothing {}, +} + +#[derive(Deserialize, Debug)] +struct JsonIntrinsic { + #[serde(rename = "SIMD_ISA")] + simd_isa: String, + name: String, + arguments: Vec<String>, + return_type: ReturnType, + #[serde(rename = "Arguments_Preparation")] + args_prep: Option<HashMap<String, ArgPrep>>, + #[serde(rename = "Architectures")] + architectures: Vec<String>, +} + +pub fn get_neon_intrinsics(filename: &str) -> Result<Vec<Intrinsic>, 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"); + + let parsed = json + .into_iter() + .filter_map(|intr| { + if intr.simd_isa == "Neon" { + Some(json_to_intrinsic(intr).expect("Couldn't parse JSON")) + } else { + None + } + }) + .collect(); + Ok(parsed) +} + +fn json_to_intrinsic(mut intr: JsonIntrinsic) -> Result<Intrinsic, Box<dyn std::error::Error>> { + let name = intr.name.replace(['[', ']'], ""); + + let results = IntrinsicType::from_c(&intr.return_type.value)?; + + let mut args_prep = intr.args_prep.as_mut(); + let args = intr + .arguments + .into_iter() + .enumerate() + .map(|(i, arg)| { + let arg_name = Argument::type_and_name_from_c(&arg).1; + let arg_prep = args_prep.as_mut().and_then(|a| a.remove(arg_name)); + let mut arg = Argument::from_c(i, &arg, arg_prep); + // The JSON doesn't list immediates as const + if let IntrinsicType::Type { + ref mut constant, .. + } = arg.ty + { + if arg.name.starts_with("imm") { + *constant = true + } + } + arg + }) + .collect(); + + let arguments = ArgumentList { args }; + + Ok(Intrinsic { + name, + arguments, + results, + a64_only: intr.architectures == vec!["A64".to_string()], + }) +} diff --git a/library/stdarch/crates/intrinsic-test/src/main.rs b/library/stdarch/crates/intrinsic-test/src/main.rs index 5a29c4767..76d2da3ab 100644 --- a/library/stdarch/crates/intrinsic-test/src/main.rs +++ b/library/stdarch/crates/intrinsic-test/src/main.rs @@ -1,7 +1,5 @@ #![feature(slice_partition_dedup)] #[macro_use] -extern crate lazy_static; -#[macro_use] extern crate log; use std::fs::File; @@ -14,12 +12,12 @@ use itertools::Itertools; use rayon::prelude::*; use types::TypeKind; -use crate::acle_csv_parser::{get_acle_intrinsics, CsvMetadata}; use crate::argument::Argument; +use crate::json_parser::get_neon_intrinsics; -mod acle_csv_parser; mod argument; mod intrinsic; +mod json_parser; mod types; mod values; @@ -191,7 +189,8 @@ fn compile_c(c_filename: &str, intrinsic: &Intrinsic, compiler: &str, a32: bool) let output = Command::new("sh") .arg("-c") .arg(format!( - "{cpp} {cppflags} {arch_flags} -Wno-narrowing -O2 -target {target} -o c_programs/{intrinsic} {filename}", + // -ffp-contract=off emulates Rust's approach of not fusing separate mul-add operations + "{cpp} {cppflags} {arch_flags} -ffp-contract=off -Wno-narrowing -O2 -target {target} -o c_programs/{intrinsic} {filename}", target = if a32 { "armv7-unknown-linux-gnueabihf" } else { "aarch64-unknown-linux-gnu" }, arch_flags = if a32 { "-march=armv8.6-a+crypto+crc+dotprod" } else { "-march=armv8.6-a+crypto+sha3+crc+dotprod" }, filename = c_filename, @@ -218,20 +217,14 @@ fn compile_c(c_filename: &str, intrinsic: &Intrinsic, compiler: &str, a32: bool) } } -fn build_notices(csv_metadata: &CsvMetadata, line_prefix: &str) -> String { - let mut notices = format!( +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 CSV specification, published with the following notices: -{line_prefix} +{line_prefix}test are derived from a JSON specification, published under the same license as the +{line_prefix}`intrinsic-test` crate.\n " - ); - let lines = csv_metadata - .notices_lines() - .map(|line| format!("{line_prefix} {line}\n")); - notices.extend(lines); - notices.push_str("\n"); - notices + ) } fn build_c(notices: &str, intrinsics: &Vec<Intrinsic>, compiler: &str, a32: bool) -> bool { @@ -250,13 +243,7 @@ fn build_c(notices: &str, intrinsics: &Vec<Intrinsic>, compiler: &str, a32: bool .is_none() } -fn build_rust( - notices: &str, - spdx_lic: &str, - intrinsics: &Vec<Intrinsic>, - toolchain: &str, - a32: bool, -) -> bool { +fn build_rust(notices: &str, intrinsics: &[Intrinsic], toolchain: &str, a32: bool) -> bool { intrinsics.iter().for_each(|i| { let rust_dir = format!(r#"rust_programs/{}"#, i.name); let _ = std::fs::create_dir_all(&rust_dir); @@ -275,7 +262,7 @@ fn build_rust( name = "intrinsic-test-programs" version = "{version}" authors = ["{authors}"] -license = "{spdx_lic}" +license = "{license}" edition = "2018" [workspace] [dependencies] @@ -283,6 +270,7 @@ core_arch = {{ path = "../crates/core_arch" }} {binaries}"#, version = env!("CARGO_PKG_VERSION"), authors = env!("CARGO_PKG_AUTHORS"), + license = env!("CARGO_PKG_LICENSE"), binaries = intrinsics .iter() .map(|i| { @@ -394,8 +382,9 @@ fn main() { Default::default() }; let a32 = matches.is_present("A32"); + let mut intrinsics = get_neon_intrinsics(filename).expect("Error parsing input file"); - let (csv_metadata, intrinsics) = get_acle_intrinsics(filename); + intrinsics.sort_by(|a, b| a.name.cmp(&b.name)); let mut intrinsics = intrinsics .into_iter() @@ -418,14 +407,13 @@ fn main() { .collect::<Vec<_>>(); intrinsics.dedup(); - let notices = build_notices(&csv_metadata, "// "); - let spdx_lic = csv_metadata.spdx_license_identifier(); + let notices = build_notices("// "); if !build_c(¬ices, &intrinsics, cpp_compiler, a32) { std::process::exit(2); } - if !build_rust(¬ices, spdx_lic, &intrinsics, &toolchain, a32) { + if !build_rust(¬ices, &intrinsics, &toolchain, a32) { std::process::exit(3); } diff --git a/library/stdarch/crates/intrinsic-test/src/types.rs b/library/stdarch/crates/intrinsic-test/src/types.rs index 7442ad5e6..0e8bbb112 100644 --- a/library/stdarch/crates/intrinsic-test/src/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/types.rs @@ -110,11 +110,11 @@ impl IntrinsicType { /// 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 { ref child, .. } => child.inner_size(), + match self { + IntrinsicType::Ptr { child, .. } => child.inner_size(), IntrinsicType::Type { bit_len: Some(bl), .. - } => bl, + } => *bl, _ => unreachable!(""), } } @@ -433,4 +433,67 @@ impl IntrinsicType { _ => todo!("get_lane_function IntrinsicType: {:#?}", self), } } + + pub fn from_c(s: &str) -> Result<IntrinsicType, String> { + 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(); + Ok(IntrinsicType::Ptr { + constant, + child: Box::new(IntrinsicType::from_c(s)?), + }) + } 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::<TypeKind>()?; + let bit_len = bit_len.parse::<u32>().map_err(|err| err.to_string())?; + let simd_len = match parts.next() { + Some(part) => Some( + part.parse::<u32>() + .map_err(|_| "Couldn't parse simd_len: {part}")?, + ), + None => None, + }; + let vec_len = match parts.next() { + Some(part) => Some( + part.parse::<u32>() + .map_err(|_| "Couldn't parse vec_len: {part}")?, + ), + None => None, + }; + Ok(IntrinsicType::Type { + constant, + kind: arg_kind, + bit_len: Some(bit_len), + simd_len, + vec_len, + }) + } else { + let kind = start.parse::<TypeKind>()?; + let bit_len = match kind { + TypeKind::Int => Some(32), + _ => None, + }; + Ok(IntrinsicType::Type { + constant, + kind: start.parse::<TypeKind>()?, + bit_len, + simd_len: None, + vec_len: None, + }) + } + } + } } |