mirror of
https://github.com/rust-lang/rust.git
synced 2025-10-02 10:18:25 +00:00
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:
parent
284b9706d0
commit
0125fa17c8
3
library/stdarch/.gitmodules
vendored
3
library/stdarch/.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "crates/intrinsic-test/acle"]
|
||||
path = crates/intrinsic-test/acle
|
||||
url = https://github.com/ARM-software/acle.git
|
@ -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 \
|
||||
|
@ -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" \
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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)]
|
||||
|
98
library/stdarch/crates/intrinsic-test/src/json_parser.rs
Normal file
98
library/stdarch/crates/intrinsic-test/src/json_parser.rs
Normal 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()],
|
||||
})
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
87470
library/stdarch/intrinsics_data/arm_intrinsics.json
Normal file
87470
library/stdarch/intrinsics_data/arm_intrinsics.json
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user