summaryrefslogtreecommitdiffstats
path: root/library/stdarch/crates/intrinsic-test
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:19 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 03:57:19 +0000
commita0b8f38ab54ac451646aa00cd5e91b6c76f22a84 (patch)
treefc451898ccaf445814e26b46664d78702178101d /library/stdarch/crates/intrinsic-test
parentAdding debian version 1.71.1+dfsg1-2. (diff)
downloadrustc-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')
-rw-r--r--library/stdarch/crates/intrinsic-test/Cargo.toml7
-rw-r--r--library/stdarch/crates/intrinsic-test/missing_aarch64.txt72
-rw-r--r--library/stdarch/crates/intrinsic-test/missing_arm.txt142
-rw-r--r--library/stdarch/crates/intrinsic-test/src/acle_csv_parser.rs363
-rw-r--r--library/stdarch/crates/intrinsic-test/src/argument.rs45
-rw-r--r--library/stdarch/crates/intrinsic-test/src/json_parser.rs97
-rw-r--r--library/stdarch/crates/intrinsic-test/src/main.rs44
-rw-r--r--library/stdarch/crates/intrinsic-test/src/types.rs69
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(&notices)
- .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(&notices, &intrinsics, cpp_compiler, a32) {
std::process::exit(2);
}
- if !build_rust(&notices, spdx_lic, &intrinsics, &toolchain, a32) {
+ if !build_rust(&notices, &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,
+ })
+ }
+ }
+ }
}