Remove ACLE submodule

This involves moving from the ACLE intrinsic definitions (which aren't
available for SVE at this point) to a JSON file. This was derived from
ARM's documentation[^1], and then relicensed under `MIT OR Apache-2.0` for
use in this repository.

[^1]: https://developer.arm.com/architectures/instruction-sets/intrinsics
This commit is contained in:
Adam Gemmell 2023-04-11 15:46:03 +01:00 committed by Amanieu d'Antras
parent 284b9706d0
commit 0125fa17c8
14 changed files with 87743 additions and 579 deletions

View File

@ -1,3 +0,0 @@
[submodule "crates/intrinsic-test/acle"]
path = crates/intrinsic-test/acle
url = https://github.com/ARM-software/acle.git

View File

@ -10,7 +10,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
qemu-user \
make \
file \
clang-13 \
clang-15 \
lld
ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \

View File

@ -10,7 +10,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
qemu-user \
make \
file \
clang-13 \
clang-15 \
lld
ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER="qemu-arm -L /usr/arm-linux-gnueabihf" \

View File

@ -137,10 +137,10 @@ esac
if [ "${TARGET}" = "aarch64-unknown-linux-gnu" ]; then
export CPPFLAGS="-fuse-ld=lld -I/usr/aarch64-linux-gnu/include/ -I/usr/aarch64-linux-gnu/include/c++/9/aarch64-linux-gnu/"
RUST_LOG=warn cargo run ${INTRINSIC_TEST} --release --bin intrinsic-test -- crates/intrinsic-test/acle/tools/intrinsic_db/advsimd.csv --runner "${CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER}" --cppcompiler "clang++-13" --skip crates/intrinsic-test/missing_aarch64.txt
RUST_LOG=warn cargo run ${INTRINSIC_TEST} --release --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json --runner "${CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER}" --cppcompiler "clang++-15" --skip crates/intrinsic-test/missing_aarch64.txt
elif [ "${TARGET}" = "armv7-unknown-linux-gnueabihf" ]; then
export CPPFLAGS="-fuse-ld=lld -I/usr/arm-linux-gnueabihf/include/ -I/usr/arm-linux-gnueabihf/include/c++/9/arm-linux-gnueabihf/"
RUST_LOG=warn cargo run ${INTRINSIC_TEST} --release --bin intrinsic-test -- crates/intrinsic-test/acle/tools/intrinsic_db/advsimd.csv --runner "${CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER}" --cppcompiler "clang++-13" --skip crates/intrinsic-test/missing_arm.txt --a32
RUST_LOG=warn cargo run ${INTRINSIC_TEST} --release --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json --runner "${CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER}" --cppcompiler "clang++-15" --skip crates/intrinsic-test/missing_arm.txt --a32
fi
if [ "$NORUN" != "1" ] && [ "$NOSTD" != 1 ]; then

View File

@ -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"

@ -1 +0,0 @@
Subproject commit 5626f85f469f419db16f20b1614863aeb377c22b

View File

@ -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

View File

@ -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

View File

@ -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);
}
}

View File

@ -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)]

View File

@ -0,0 +1,98 @@
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)]
#[serde(deny_unknown_fields)]
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()],
})
}

View File

@ -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);
}

View File

@ -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,
})
}
}
}
}

File diff suppressed because it is too large Load Diff