blob: d7b066485184bdcdf4c7d590335ccc078e325aff [file] [log] [blame]
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 fn get_acle_intrinsics(filename: &str) -> Vec<Intrinsic> {
let data = std::fs::read_to_string(filename).expect("Failed to open ACLE intrinsics file");
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());
}
}
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);
}
}