introduced generic types and code refactor

This commit is contained in:
Madhav Madhusoodanan 2025-04-16 14:31:42 +05:30 committed by Amanieu d'Antras
parent a993b4427c
commit 57c357591e
11 changed files with 812 additions and 644 deletions

View file

@ -0,0 +1,60 @@
use super::json_parser::ArgPrep;
use crate::common::argument::MetadataDefinition;
use serde::Deserialize;
use serde_json::Value;
use std::ops::Range;
#[derive(Debug, PartialEq, Clone, Deserialize)]
pub enum Constraint {
Equal(i64),
Range(Range<i64>),
}
impl Constraint {
pub fn to_range(&self) -> Range<i64> {
match self {
Constraint::Equal(eq) => *eq..*eq + 1,
Constraint::Range(range) => range.clone(),
}
}
}
impl MetadataDefinition for Constraint {
fn from_metadata(metadata: Option<Value>) -> Vec<Box<Self>> {
let arg_prep: Option<ArgPrep> = metadata.and_then(|a| {
if let Value::Object(_) = a {
a.try_into().ok()
} else {
None
}
});
let constraint: Option<Constraint> = arg_prep.and_then(|a| a.try_into().ok());
vec![constraint]
.into_iter()
.filter_map(|a| a)
.map(|a| Box::new(a))
.collect()
}
}
/// ARM-specific
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(())
}
}
}

View file

@ -1,9 +1,12 @@
use super::argument::Argument;
use super::config::{AARCH_CONFIGURATIONS, POLY128_OSTREAM_DEF, build_notices};
use super::format::Indentation;
use super::intrinsic::Intrinsic;
use super::intrinsic::ArmIntrinsicType;
use crate::arm::constraint::Constraint;
use crate::common::argument::Argument;
use crate::common::format::Indentation;
use crate::common::gen_c::{compile_c, create_c_filenames, generate_c_program};
use crate::common::gen_rust::{compile_rust, create_rust_filenames, generate_rust_program};
use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition};
use crate::common::intrinsic_types::IntrinsicTypeDefinition;
use crate::common::write_file;
use itertools::Itertools;
use rayon::prelude::*;
@ -14,14 +17,14 @@ const PASSES: u32 = 20;
fn gen_code_c(
indentation: Indentation,
intrinsic: &Intrinsic,
constraints: &[&Argument],
intrinsic: &Intrinsic<ArmIntrinsicType, Constraint>,
constraints: &[&Argument<ArmIntrinsicType, Constraint>],
name: String,
target: &str,
) -> String {
if let Some((current, constraints)) = constraints.split_last() {
let range = current
.constraints
.metadata
.iter()
.map(|c| c.to_range())
.flat_map(|r| r.into_iter());
@ -52,11 +55,15 @@ fn gen_code_c(
}
}
fn generate_c_program_arm(header_files: &[&str], intrinsic: &Intrinsic, target: &str) -> String {
fn generate_c_program_arm(
header_files: &[&str],
intrinsic: &Intrinsic<ArmIntrinsicType, Constraint>,
target: &str,
) -> String {
let constraints = intrinsic
.arguments
.iter()
.filter(|i| i.has_constraint())
.filter(|&i| i.has_constraint())
.collect_vec();
let indentation = Indentation::default();
@ -82,13 +89,13 @@ fn generate_c_program_arm(header_files: &[&str], intrinsic: &Intrinsic, target:
fn gen_code_rust(
indentation: Indentation,
intrinsic: &Intrinsic,
constraints: &[&Argument],
intrinsic: &Intrinsic<ArmIntrinsicType, Constraint>,
constraints: &[&Argument<ArmIntrinsicType, Constraint>],
name: String,
) -> String {
if let Some((current, constraints)) = constraints.split_last() {
let range = current
.constraints
.metadata
.iter()
.map(|c| c.to_range())
.flat_map(|r| r.into_iter());
@ -118,7 +125,10 @@ fn gen_code_rust(
}
}
fn generate_rust_program_arm(intrinsic: &Intrinsic, target: &str) -> String {
fn generate_rust_program_arm(
intrinsic: &Intrinsic<ArmIntrinsicType, Constraint>,
target: &str,
) -> String {
let constraints = intrinsic
.arguments
.iter()
@ -220,7 +230,7 @@ fn compile_c_arm(
}
pub fn build_c(
intrinsics: &Vec<Intrinsic>,
intrinsics: &Vec<Intrinsic<ArmIntrinsicType, Constraint>>,
compiler: Option<&str>,
target: &str,
cxx_toolchain_dir: Option<&str>,
@ -252,7 +262,7 @@ pub fn build_c(
}
pub fn build_rust(
intrinsics: &[Intrinsic],
intrinsics: &[Intrinsic<ArmIntrinsicType, Constraint>],
toolchain: Option<&str>,
target: &str,
linker: Option<&str>,

View file

@ -1,40 +1,83 @@
use super::argument::ArgumentList;
use super::format::Indentation;
use super::types::{IntrinsicType, TypeKind};
use super::constraint::Constraint;
use crate::common::argument::ArgumentList;
use crate::common::format::Indentation;
use crate::common::intrinsic::{Intrinsic, IntrinsicDefinition};
use crate::common::intrinsic_types::{
BaseIntrinsicTypeDefinition, IntrinsicType, IntrinsicTypeDefinition, TypeKind,
};
use crate::common::types::Language;
/// An intrinsic
#[derive(Debug, PartialEq, Clone)]
pub struct Intrinsic {
/// The function name of this intrinsic.
pub name: String,
#[derive(Debug, Clone, PartialEq)]
pub struct ArmIntrinsicType(pub IntrinsicType);
/// Any arguments for this intrinsic.
pub arguments: ArgumentList,
/// The return type of this intrinsic.
pub results: IntrinsicType,
/// Whether this intrinsic is only available on A64.
pub a64_only: bool,
impl BaseIntrinsicTypeDefinition for ArmIntrinsicType {
fn kind(&self) -> TypeKind {
self.0.kind()
}
fn inner_size(&self) -> u32 {
self.0.inner_size()
}
fn num_lanes(&self) -> u32 {
self.0.num_lanes()
}
fn num_vectors(&self) -> u32 {
self.0.num_vectors()
}
fn is_simd(&self) -> bool {
self.0.is_simd()
}
fn is_ptr(&self) -> bool {
self.0.is_ptr()
}
fn c_scalar_type(&self) -> String {
self.0.c_scalar_type()
}
fn rust_scalar_type(&self) -> String {
self.0.rust_scalar_type()
}
fn c_promotion(&self) -> &str {
self.0.c_promotion()
}
fn populate_random(&self, indentation: Indentation, loads: u32, language: &Language) -> String {
self.0.populate_random(indentation, loads, language)
}
fn is_rust_vals_array_const(&self) -> bool {
self.0.is_rust_vals_array_const()
}
fn as_call_param_c(&self, name: &String) -> String {
self.0.as_call_param_c(name)
}
}
impl Intrinsic {
impl IntrinsicDefinition<ArmIntrinsicType, Constraint> for Intrinsic<ArmIntrinsicType, Constraint> {
fn arguments(&self) -> ArgumentList<ArmIntrinsicType, Constraint> {
self.arguments.clone()
}
fn results(&self) -> ArmIntrinsicType {
self.results.clone()
}
fn name(&self) -> String {
self.name.clone()
}
/// Generates a std::cout for the intrinsics results that will match the
/// rust debug output format for the return type. The generated line assumes
/// there is an int i in scope which is the current pass number.
pub fn print_result_c(&self, indentation: Indentation, additional: &str) -> String {
let lanes = if self.results.num_vectors() > 1 {
(0..self.results.num_vectors())
fn print_result_c(&self, indentation: Indentation, additional: &str) -> String {
let lanes = if self.results().num_vectors() > 1 {
(0..self.results().num_vectors())
.map(|vector| {
format!(
r#""{ty}(" << {lanes} << ")""#,
ty = self.results.c_single_vector_type(),
lanes = (0..self.results.num_lanes())
ty = self.results().c_single_vector_type(),
lanes = (0..self.results().num_lanes())
.map(move |idx| -> std::string::String {
format!(
"{cast}{lane_fn}(__return_value.val[{vector}], {lane})",
cast = self.results.c_promotion(),
lane_fn = self.results.get_lane_function(),
cast = self.results().c_promotion(),
lane_fn = self.results().get_lane_function(),
lane = idx,
vector = vector,
)
@ -45,13 +88,13 @@ impl Intrinsic {
})
.collect::<Vec<_>>()
.join(r#" << ", " << "#)
} else if self.results.num_lanes() > 1 {
(0..self.results.num_lanes())
} else if self.results().num_lanes() > 1 {
(0..self.results().num_lanes())
.map(|idx| -> std::string::String {
format!(
"{cast}{lane_fn}(__return_value, {lane})",
cast = self.results.c_promotion(),
lane_fn = self.results.get_lane_function(),
cast = self.results().c_promotion(),
lane_fn = self.results().get_lane_function(),
lane = idx
)
})
@ -61,22 +104,22 @@ impl Intrinsic {
format!(
"{promote}cast<{cast}>(__return_value)",
cast = match self.results.kind() {
TypeKind::Float if self.results.inner_size() == 16 => "float16_t".to_string(),
TypeKind::Float if self.results.inner_size() == 32 => "float".to_string(),
TypeKind::Float if self.results.inner_size() == 64 => "double".to_string(),
TypeKind::Int => format!("int{}_t", self.results.inner_size()),
TypeKind::UInt => format!("uint{}_t", self.results.inner_size()),
TypeKind::Poly => format!("poly{}_t", self.results.inner_size()),
TypeKind::Float if self.results().inner_size() == 16 => "float16_t".to_string(),
TypeKind::Float if self.results().inner_size() == 32 => "float".to_string(),
TypeKind::Float if self.results().inner_size() == 64 => "double".to_string(),
TypeKind::Int => format!("int{}_t", self.results().inner_size()),
TypeKind::UInt => format!("uint{}_t", self.results().inner_size()),
TypeKind::Poly => format!("poly{}_t", self.results().inner_size()),
ty => todo!("print_result_c - Unknown type: {:#?}", ty),
},
promote = self.results.c_promotion(),
promote = self.results().c_promotion(),
)
};
format!(
r#"{indentation}std::cout << "Result {additional}-" << i+1 << ": {ty}" << std::fixed << std::setprecision(150) << {lanes} << "{close}" << std::endl;"#,
ty = if self.results.is_simd() {
format!("{}(", self.results.c_type())
ty = if self.results().is_simd() {
format!("{}(", self.results().c_type())
} else {
String::from("")
},

View file

@ -1,6 +1,8 @@
use super::argument::{Argument, ArgumentList};
use super::intrinsic::Intrinsic;
use super::types::IntrinsicType;
use super::constraint::Constraint;
use super::intrinsic::ArmIntrinsicType;
use crate::common::argument::{Argument, ArgumentList};
use crate::common::intrinsic::Intrinsic;
use crate::common::intrinsic_types::{IntrinsicType, IntrinsicTypeDefinition};
use serde::Deserialize;
use serde_json::Value;
use std::collections::HashMap;
@ -53,7 +55,7 @@ struct JsonIntrinsic {
pub fn get_neon_intrinsics(
filename: &Path,
target: &String,
) -> Result<Vec<Intrinsic>, Box<dyn std::error::Error>> {
) -> Result<Vec<Intrinsic<ArmIntrinsicType, Constraint>>, 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");
@ -74,37 +76,39 @@ pub fn get_neon_intrinsics(
fn json_to_intrinsic(
mut intr: JsonIntrinsic,
target: &String,
) -> Result<Intrinsic, Box<dyn std::error::Error>> {
) -> Result<Intrinsic<ArmIntrinsicType, Constraint>, Box<dyn std::error::Error>> {
let name = intr.name.replace(['[', ']'], "");
let results = IntrinsicType::from_c(&intr.return_type.value, target)?;
let results = ArmIntrinsicType::from_c(&intr.return_type.value, target)?;
let args = intr
.arguments
.into_iter()
.enumerate()
.map(|(i, arg)| {
// let arg_name = Argument::type_and_name_from_c(&arg).1;
let mut arg = Argument::from_c(i, &arg, target, intr.args_prep.as_mut());
let arg_name = Argument::<ArmIntrinsicType, Constraint>::type_and_name_from_c(&arg).1;
let metadata = intr.args_prep.as_mut();
let metadata = metadata.and_then(|a| a.remove(arg_name));
let mut arg =
Argument::<ArmIntrinsicType, Constraint>::from_c(i, &arg, target, metadata);
// The JSON doesn't list immediates as const
if let IntrinsicType::Type {
let IntrinsicType {
ref mut constant, ..
} = arg.ty
{
if arg.name.starts_with("imm") {
*constant = true
}
} = arg.ty.0;
if arg.name.starts_with("imm") {
*constant = true
}
arg
})
.collect();
let arguments = ArgumentList { args };
let arguments = ArgumentList::<ArmIntrinsicType, Constraint> { args };
Ok(Intrinsic {
name,
arguments,
results,
results: *results,
a64_only: intr.architectures == vec!["A64".to_string()],
})
}

View file

@ -1,21 +1,22 @@
mod argument;
mod config;
mod format;
mod constraint;
mod functions;
mod intrinsic;
mod json_parser;
mod types;
use crate::arm::constraint::Constraint;
use crate::arm::intrinsic::ArmIntrinsicType;
use crate::common::SupportedArchitectureTest;
use crate::common::compare::compare_outputs;
use crate::common::intrinsic::Intrinsic;
use crate::common::intrinsic_types::{BaseIntrinsicTypeDefinition, TypeKind};
use crate::common::types::ProcessedCli;
use functions::{build_c, build_rust};
use intrinsic::Intrinsic;
use json_parser::get_neon_intrinsics;
use types::TypeKind;
pub struct ArmArchitectureTest {
intrinsics: Vec<Intrinsic>,
intrinsics: Vec<Intrinsic<ArmIntrinsicType, Constraint>>,
cli_options: ProcessedCli,
}

View file

@ -1,474 +1,125 @@
use std::fmt;
use std::str::FromStr;
use itertools::Itertools as _;
use super::format::Indentation;
use super::intrinsic::ArmIntrinsicType;
use crate::common::intrinsic_types::{IntrinsicType, IntrinsicTypeDefinition, TypeKind};
use crate::common::types::Language;
use crate::common::values::value_for_array;
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum TypeKind {
BFloat,
Float,
Int,
UInt,
Poly,
Void,
}
impl FromStr for TypeKind {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bfloat" => Ok(Self::BFloat),
"float" => Ok(Self::Float),
"int" => Ok(Self::Int),
"poly" => Ok(Self::Poly),
"uint" | "unsigned" => Ok(Self::UInt),
"void" => Ok(Self::Void),
_ => Err(format!("Impossible to parse argument kind {s}")),
}
}
}
impl fmt::Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::BFloat => "bfloat",
Self::Float => "float",
Self::Int => "int",
Self::UInt => "uint",
Self::Poly => "poly",
Self::Void => "void",
}
)
}
}
impl TypeKind {
/// Gets the type part of a c typedef for a type that's in the form of {type}{size}_t.
pub fn c_prefix(&self) -> &str {
match self {
Self::Float => "float",
Self::Int => "int",
Self::UInt => "uint",
Self::Poly => "poly",
_ => unreachable!("Not used: {:#?}", self),
}
}
/// Gets the rust prefix for the type kind i.e. i, u, f.
pub fn rust_prefix(&self) -> &str {
match self {
Self::Float => "f",
Self::Int => "i",
Self::UInt => "u",
Self::Poly => "u",
_ => unreachable!("Unused type kind: {:#?}", self),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum IntrinsicType {
Ptr {
constant: bool,
child: Box<IntrinsicType>,
},
Type {
constant: bool,
kind: TypeKind,
/// The bit length of this type (e.g. 32 for u32).
bit_len: Option<u32>,
/// Length of the SIMD vector (i.e. 4 for uint32x4_t), A value of `None`
/// means this is not a simd type. A `None` can be assumed to be 1,
/// although in some places a distinction is needed between `u64` and
/// `uint64x1_t` this signals that.
simd_len: Option<u32>,
/// The number of rows for SIMD matrices (i.e. 2 for uint8x8x2_t).
/// A value of `None` represents a type that does not contain any
/// rows encoded in the type (e.g. uint8x8_t).
/// A value of `None` can be assumed to be 1 though.
vec_len: Option<u32>,
target: String,
},
}
impl IntrinsicType {
/// Get the TypeKind for this type, recursing into pointers.
pub fn kind(&self) -> TypeKind {
match *self {
IntrinsicType::Ptr { ref child, .. } => child.kind(),
IntrinsicType::Type { kind, .. } => kind,
}
}
/// Get the size of a single element inside this type, recursing into
/// 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 { child, .. } => child.inner_size(),
IntrinsicType::Type {
bit_len: Some(bl), ..
} => *bl,
_ => unreachable!(""),
}
}
pub fn num_lanes(&self) -> u32 {
match *self {
IntrinsicType::Ptr { ref child, .. } => child.num_lanes(),
IntrinsicType::Type {
simd_len: Some(sl), ..
} => sl,
_ => 1,
}
}
pub fn num_vectors(&self) -> u32 {
match *self {
IntrinsicType::Ptr { ref child, .. } => child.num_vectors(),
IntrinsicType::Type {
vec_len: Some(vl), ..
} => vl,
_ => 1,
}
}
/// Determine if the type is a simd type, this will treat a type such as
/// `uint64x1` as simd.
pub fn is_simd(&self) -> bool {
match *self {
IntrinsicType::Ptr { ref child, .. } => child.is_simd(),
IntrinsicType::Type {
simd_len: None,
vec_len: None,
..
} => false,
_ => true,
}
}
pub fn is_ptr(&self) -> bool {
match *self {
IntrinsicType::Ptr { .. } => true,
IntrinsicType::Type { .. } => false,
}
}
/// Move to Argument
pub fn c_scalar_type(&self) -> String {
format!(
"{prefix}{bits}_t",
prefix = self.kind().c_prefix(),
bits = self.inner_size()
)
}
/// Move to Argument
pub fn rust_scalar_type(&self) -> String {
format!(
"{prefix}{bits}",
prefix = self.kind().rust_prefix(),
bits = self.inner_size()
)
}
impl IntrinsicTypeDefinition for ArmIntrinsicType {
/// Gets a string containing the typename for this type in C format.
///
/// ARM-specific
pub fn c_type(&self) -> String {
match self {
IntrinsicType::Ptr { child, .. } => child.c_type(),
IntrinsicType::Type {
constant,
kind,
bit_len: Some(bit_len),
simd_len: None,
vec_len: None,
..
} => format!(
"{}{}{}_t",
if *constant { "const " } else { "" },
kind.c_prefix(),
bit_len
),
IntrinsicType::Type {
kind,
bit_len: Some(bit_len),
simd_len: Some(simd_len),
vec_len: None,
..
} => format!("{}{bit_len}x{simd_len}_t", kind.c_prefix()),
IntrinsicType::Type {
kind,
bit_len: Some(bit_len),
simd_len: Some(simd_len),
vec_len: Some(vec_len),
..
} => format!("{}{bit_len}x{simd_len}x{vec_len}_t", kind.c_prefix()),
_ => todo!("{:#?}", self),
}
}
fn c_type(&self) -> String {
let prefix = self.0.kind.c_prefix();
let const_prefix = if self.0.constant { "const " } else { "" };
/// ARM-specific
pub fn c_single_vector_type(&self) -> String {
match self {
IntrinsicType::Ptr { child, .. } => child.c_single_vector_type(),
IntrinsicType::Type {
kind,
bit_len: Some(bit_len),
simd_len: Some(simd_len),
vec_len: Some(_),
..
} => format!("{}{bit_len}x{simd_len}_t", kind.c_prefix()),
_ => unreachable!("Shouldn't be called on this type"),
}
}
/// ARM-specific
pub fn rust_type(&self) -> String {
match self {
IntrinsicType::Ptr { child, .. } => child.c_type(),
IntrinsicType::Type {
kind,
bit_len: Some(bit_len),
simd_len: None,
vec_len: None,
..
} => format!("{}{bit_len}", kind.rust_prefix()),
IntrinsicType::Type {
kind,
bit_len: Some(bit_len),
simd_len: Some(simd_len),
vec_len: None,
..
} => format!("{}{bit_len}x{simd_len}_t", kind.c_prefix()),
IntrinsicType::Type {
kind,
bit_len: Some(bit_len),
simd_len: Some(simd_len),
vec_len: Some(vec_len),
..
} => format!("{}{bit_len}x{simd_len}x{vec_len}_t", kind.c_prefix()),
_ => todo!("{:#?}", self),
}
}
/// Gets a cast for this type if needs promotion.
/// This is required for 8 bit types due to printing as the 8 bit types use
/// a char and when using that in `std::cout` it will print as a character,
/// which means value of 0 will be printed as a null byte.
///
/// This is also needed for polynomial types because we want them to be
/// printed as unsigned integers to match Rust's `Debug` impl.
pub fn c_promotion(&self) -> &str {
match *self {
IntrinsicType::Type {
kind,
bit_len: Some(8),
..
} => match kind {
TypeKind::Int => "(int)",
TypeKind::UInt => "(unsigned int)",
TypeKind::Poly => "(unsigned int)(uint8_t)",
_ => "",
},
IntrinsicType::Type {
kind: TypeKind::Poly,
bit_len: Some(bit_len),
..
} => match bit_len {
8 => unreachable!("handled above"),
16 => "(uint16_t)",
32 => "(uint32_t)",
64 => "(uint64_t)",
128 => "",
_ => panic!("invalid bit_len"),
},
_ => "",
}
}
/// Generates an initialiser for an array, which can be used to initialise an argument for the
/// intrinsic call.
///
/// This is determistic based on the pass number.
///
/// * `loads`: The number of values that need to be loaded from the argument array
/// * e.g for argument type uint32x2, loads=2 results in a string representing 4 32-bit values
///
/// Returns a string such as
/// * `{0x1, 0x7F, 0xFF}` if `language` is `Language::C`
/// * `[0x1 as _, 0x7F as _, 0xFF as _]` if `language` is `Language::Rust`
pub fn populate_random(
&self,
indentation: Indentation,
loads: u32,
language: &Language,
) -> String {
match self {
IntrinsicType::Ptr { child, .. } => child.populate_random(indentation, loads, language),
IntrinsicType::Type {
bit_len: Some(bit_len @ (8 | 16 | 32 | 64)),
kind: kind @ (TypeKind::Int | TypeKind::UInt | TypeKind::Poly),
simd_len,
vec_len,
..
} => {
let (prefix, suffix) = match language {
Language::Rust => ("[", "]"),
Language::C => ("{", "}"),
};
let body_indentation = indentation.nested();
format!(
"{prefix}\n{body}\n{indentation}{suffix}",
body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1))
.format_with(",\n", |i, fmt| {
let src = value_for_array(*bit_len, i);
assert!(src == 0 || src.ilog2() < *bit_len);
if *kind == TypeKind::Int && (src >> (*bit_len - 1)) != 0 {
// `src` is a two's complement representation of a negative value.
let mask = !0u64 >> (64 - *bit_len);
let ones_compl = src ^ mask;
let twos_compl = ones_compl + 1;
if (twos_compl == src) && (language == &Language::C) {
// `src` is INT*_MIN. C requires `-0x7fffffff - 1` to avoid
// undefined literal overflow behaviour.
fmt(&format_args!("{body_indentation}-{ones_compl:#x} - 1"))
} else {
fmt(&format_args!("{body_indentation}-{twos_compl:#x}"))
}
} else {
fmt(&format_args!("{body_indentation}{src:#x}"))
}
})
)
if let (Some(bit_len), simd_len, vec_len) =
(self.0.bit_len, self.0.simd_len, self.0.vec_len)
{
match (simd_len, vec_len) {
(None, None) => format!("{}{}{}_t", const_prefix, prefix, bit_len),
(Some(simd), None) => format!("{}{bit_len}x{simd}_t", prefix),
(Some(simd), Some(vec)) => format!("{}{bit_len}x{simd}x{vec}_t", prefix),
(None, Some(_)) => todo!("{:#?}", self), // Likely an invalid case
}
IntrinsicType::Type {
kind: TypeKind::Float,
bit_len: Some(bit_len @ (16 | 32 | 64)),
simd_len,
vec_len,
..
} => {
let (prefix, cast_prefix, cast_suffix, suffix) = match (language, bit_len) {
(&Language::Rust, 16) => ("[", "f16::from_bits(", ")", "]"),
(&Language::Rust, 32) => ("[", "f32::from_bits(", ")", "]"),
(&Language::Rust, 64) => ("[", "f64::from_bits(", ")", "]"),
(&Language::C, 16) => ("{", "cast<float16_t, uint16_t>(", ")", "}"),
(&Language::C, 32) => ("{", "cast<float, uint32_t>(", ")", "}"),
(&Language::C, 64) => ("{", "cast<double, uint64_t>(", ")", "}"),
_ => unreachable!(),
};
format!(
"{prefix}\n{body}\n{indentation}{suffix}",
body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1))
.format_with(",\n", |i, fmt| fmt(&format_args!(
"{indentation}{cast_prefix}{src:#x}{cast_suffix}",
indentation = indentation.nested(),
src = value_for_array(*bit_len, i)
)))
)
} else {
todo!("{:#?}", self)
}
}
fn c_single_vector_type(&self) -> String {
if let (Some(bit_len), Some(simd_len)) = (self.0.bit_len, self.0.simd_len) {
let prefix = self.0.kind.c_prefix();
format!("{}{bit_len}x{simd_len}_t", prefix)
} else {
unreachable!("Shouldn't be called on this type")
}
}
fn rust_type(&self) -> String {
let rust_prefix = self.0.kind.rust_prefix();
let c_prefix = self.0.kind.rust_prefix();
if self.0.ptr_constant {
self.c_type()
} else if let (Some(bit_len), simd_len, vec_len) =
(self.0.bit_len, self.0.simd_len, self.0.vec_len)
{
match (simd_len, vec_len) {
(None, None) => format!("{}{bit_len}", rust_prefix),
(Some(simd), None) => format!("{}{bit_len}x{simd}_t", c_prefix),
(Some(simd), Some(vec)) => format!("{}{bit_len}x{simd}x{vec}_t", c_prefix),
(None, Some(_)) => todo!("{:#?}", self), // Likely an invalid case
}
_ => unimplemented!("populate random: {:#?}", self),
} else {
todo!("{:#?}", self)
}
}
/// Determines the load function for this type.
///
/// ARM-specific
fn get_load_function(&self, language: Language) -> String {
match self {
IntrinsicType::Ptr { child, .. } => child.get_load_function(language),
IntrinsicType::Type {
kind: k,
bit_len: Some(bl),
simd_len,
vec_len,
target,
..
} => {
let quad = if simd_len.unwrap_or(1) * bl > 64 {
"q"
} else {
""
};
if let IntrinsicType {
kind: k,
bit_len: Some(bl),
simd_len,
vec_len,
target,
..
} = &self.0
{
let quad = if simd_len.unwrap_or(1) * bl > 64 {
"q"
} else {
""
};
let choose_workaround = language == Language::C && target.contains("v7");
format!(
"vld{len}{quad}_{type}{size}",
type = match k {
TypeKind::UInt => "u",
TypeKind::Int => "s",
TypeKind::Float => "f",
// The ACLE doesn't support 64-bit polynomial loads on Armv7
// if armv7 and bl == 64, use "s", else "p"
TypeKind::Poly => if choose_workaround && *bl == 64 {"s"} else {"p"},
x => todo!("get_load_function TypeKind: {:#?}", x),
},
size = bl,
quad = quad,
len = vec_len.unwrap_or(1),
)
}
_ => todo!("get_load_function IntrinsicType: {:#?}", self),
let choose_workaround = language == Language::C && target.contains("v7");
format!(
"vld{len}{quad}_{type}{size}",
type = match k {
TypeKind::UInt => "u",
TypeKind::Int => "s",
TypeKind::Float => "f",
// The ACLE doesn't support 64-bit polynomial loads on Armv7
// if armv7 and bl == 64, use "s", else "p"
TypeKind::Poly => if choose_workaround && *bl == 64 {"s"} else {"p"},
x => todo!("get_load_function TypeKind: {:#?}", x),
},
size = bl,
quad = quad,
len = vec_len.unwrap_or(1),
)
} else {
todo!("get_load_function IntrinsicType: {:#?}", self)
}
}
pub fn get_load_function_c(&self) -> String {
self.get_load_function(Language::C)
}
pub fn get_load_function_rust(&self) -> String {
self.get_load_function(Language::Rust)
}
/// Determines the get lane function for this type.
///
/// ARM-specific
pub fn get_lane_function(&self) -> String {
match self {
IntrinsicType::Ptr { child, .. } => child.get_lane_function(),
IntrinsicType::Type {
kind: k,
bit_len: Some(bl),
simd_len,
..
} => {
let quad = if (simd_len.unwrap_or(1) * bl) > 64 {
"q"
} else {
""
};
format!(
"vget{quad}_lane_{type}{size}",
type = match k {
TypeKind::UInt => "u",
TypeKind::Int => "s",
TypeKind::Float => "f",
TypeKind::Poly => "p",
x => todo!("get_load_function TypeKind: {:#?}", x),
},
size = bl,
quad = quad,
)
}
_ => todo!("get_lane_function IntrinsicType: {:#?}", self),
fn get_lane_function(&self) -> String {
if let IntrinsicType {
kind: k,
bit_len: Some(bl),
simd_len,
..
} = &self.0
{
let quad = if (simd_len.unwrap_or(1) * bl) > 64 {
"q"
} else {
""
};
format!(
"vget{quad}_lane_{type}{size}",
type = match k {
TypeKind::UInt => "u",
TypeKind::Int => "s",
TypeKind::Float => "f",
TypeKind::Poly => "p",
x => todo!("get_load_function TypeKind: {:#?}", x),
},
size = bl,
quad = quad,
)
} else {
todo!("get_lane_function IntrinsicType: {:#?}", self)
}
}
/// ARM-specific
pub fn from_c(s: &str, target: &String) -> Result<IntrinsicType, String> {
fn from_c(s: &str, target: &String) -> Result<Box<Self>, String> {
const CONST_STR: &str = "const";
if let Some(s) = s.strip_suffix('*') {
let (s, constant) = match s.trim().strip_suffix(CONST_STR) {
@ -476,9 +127,12 @@ impl IntrinsicType {
None => (s, false),
};
let s = s.trim_end();
Ok(IntrinsicType::Ptr {
constant,
child: Box::new(IntrinsicType::from_c(s, target)?),
let temp_return = ArmIntrinsicType::from_c(s, target);
temp_return.and_then(|mut op| {
let edited = op.as_mut();
edited.0.ptr = true;
edited.0.ptr_constant = constant;
Ok(op)
})
} else {
// [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t]
@ -507,28 +161,32 @@ impl IntrinsicType {
),
None => None,
};
Ok(IntrinsicType::Type {
Ok(Box::new(ArmIntrinsicType(IntrinsicType {
ptr: false,
ptr_constant: false,
constant,
kind: arg_kind,
bit_len: Some(bit_len),
simd_len,
vec_len,
target: target.to_string(),
})
})))
} else {
let kind = start.parse::<TypeKind>()?;
let bit_len = match kind {
TypeKind::Int => Some(32),
_ => None,
};
Ok(IntrinsicType::Type {
Ok(Box::new(ArmIntrinsicType(IntrinsicType {
ptr: false,
ptr_constant: false,
constant,
kind: start.parse::<TypeKind>()?,
bit_len,
simd_len: None,
vec_len: None,
target: target.to_string(),
})
})))
}
}
}

View file

@ -1,67 +1,35 @@
use super::format::Indentation;
use super::json_parser::ArgPrep;
use super::types::{IntrinsicType, TypeKind};
use crate::common::format::Indentation;
use crate::common::intrinsic_types::IntrinsicTypeDefinition;
use crate::common::types::Language;
use serde::Deserialize;
use serde_json::Value;
use std::collections::HashMap;
use std::ops::Range;
/// An argument for the intrinsic.
#[derive(Debug, PartialEq, Clone)]
pub struct Argument {
pub struct Argument<T: IntrinsicTypeDefinition, M: MetadataDefinition> {
/// The argument's index in the intrinsic function call.
pub pos: usize,
/// The argument name.
pub name: String,
/// The type of the argument.
pub ty: IntrinsicType,
pub ty: T,
/// Any constraints that are on this argument
pub constraints: Vec<Constraint>,
pub metadata: Vec<M>,
}
#[derive(Debug, PartialEq, Clone, Deserialize)]
pub enum Constraint {
Equal(i64),
Range(Range<i64>),
pub trait MetadataDefinition {
fn from_metadata(metadata: Option<Value>) -> Vec<Box<Self>>;
}
/// ARM-specific
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 {
Constraint::Equal(eq) => *eq..*eq + 1,
Constraint::Range(range) => range.clone(),
}
}
}
impl Argument {
fn to_c_type(&self) -> String {
impl<T, M> Argument<T, M>
where
T: IntrinsicTypeDefinition,
M: MetadataDefinition,
{
pub fn to_c_type(&self) -> String {
self.ty.c_type()
}
fn is_simd(&self) -> bool {
pub fn is_simd(&self) -> bool {
self.ty.is_simd()
}
@ -70,7 +38,7 @@ impl Argument {
}
pub fn has_constraint(&self) -> bool {
!self.constraints.is_empty()
!self.metadata.is_empty()
}
pub fn type_and_name_from_c(arg: &str) -> (&str, &str) {
@ -81,53 +49,9 @@ impl Argument {
(arg[..split_index + 1].trim_end(), &arg[split_index + 1..])
}
// ARM-specific
pub fn from_c(
pos: usize,
arg: &str,
target: &String,
metadata: Option<&mut HashMap<String, Value>>,
) -> Argument {
let (ty, var_name) = Self::type_and_name_from_c(arg);
let ty = IntrinsicType::from_c(ty, target)
.unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'"));
let arg_name = Argument::type_and_name_from_c(&arg).1;
let arg = metadata.and_then(|a| a.remove(arg_name));
let arg_prep: Option<ArgPrep> = arg.and_then(|a| {
if let Value::Object(_) = a {
a.try_into().ok()
} else {
None
}
});
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]),
}
}
fn is_rust_vals_array_const(&self) -> bool {
use TypeKind::*;
match self.ty {
// Floats have to be loaded at runtime for stable NaN conversion.
IntrinsicType::Type { kind: Float, .. } => false,
IntrinsicType::Type {
kind: Int | UInt | Poly,
..
} => true,
_ => unimplemented!(),
}
}
/// The binding keyword (e.g. "const" or "let") for the array of possible test inputs.
pub fn rust_vals_array_binding(&self) -> impl std::fmt::Display {
if self.is_rust_vals_array_const() {
fn rust_vals_array_binding(&self) -> impl std::fmt::Display {
if self.ty.is_rust_vals_array_const() {
"const"
} else {
"let"
@ -135,32 +59,55 @@ impl Argument {
}
/// The name (e.g. "A_VALS" or "a_vals") for the array of possible test inputs.
pub fn rust_vals_array_name(&self) -> impl std::fmt::Display {
if self.is_rust_vals_array_const() {
fn rust_vals_array_name(&self) -> impl std::fmt::Display {
if self.ty.is_rust_vals_array_const() {
format!("{}_VALS", self.name.to_uppercase())
} else {
format!("{}_vals", self.name.to_lowercase())
}
}
pub fn from_c(
pos: usize,
arg: &str,
target: &String,
metadata: Option<Value>,
) -> Argument<T, M> {
let (ty, var_name) = Self::type_and_name_from_c(arg);
let ty =
T::from_c(ty, target).unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'"));
let metadata: Vec<M> = M::from_metadata(metadata).into_iter().map(|b| *b).collect();
Argument {
pos,
name: String::from(var_name),
ty: *ty,
metadata,
}
}
fn as_call_param_c(&self) -> String {
self.ty.as_call_param_c(&self.name)
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ArgumentList {
pub args: Vec<Argument>,
pub struct ArgumentList<T: IntrinsicTypeDefinition, M: MetadataDefinition> {
pub args: Vec<Argument<T, M>>,
}
impl ArgumentList {
impl<T, M> ArgumentList<T, M>
where
T: IntrinsicTypeDefinition,
M: MetadataDefinition,
{
/// Converts the argument list into the call parameters for a C function call.
/// e.g. this would generate something like `a, &b, c`
pub fn as_call_param_c(&self) -> String {
self.args
.iter()
.map(|arg| match arg.ty {
IntrinsicType::Ptr { .. } => {
format!("&{}", arg.name)
}
IntrinsicType::Type { .. } => arg.name.clone(),
})
self.iter()
.map(|arg| arg.as_call_param_c())
.collect::<Vec<String>>()
.join(", ")
}
@ -168,8 +115,7 @@ impl ArgumentList {
/// Converts the argument list into the call parameters for a Rust function.
/// e.g. this would generate something like `a, b, c`
pub fn as_call_param_rust(&self) -> String {
self.args
.iter()
self.iter()
.filter(|a| !a.has_constraint())
.map(|arg| arg.name.clone())
.collect::<Vec<String>>()
@ -177,8 +123,7 @@ impl ArgumentList {
}
pub fn as_constraint_parameters_rust(&self) -> String {
self.args
.iter()
self.iter()
.filter(|a| a.has_constraint())
.map(|arg| arg.name.clone())
.collect::<Vec<String>>()
@ -241,7 +186,7 @@ impl ArgumentList {
ty = arg.to_c_type(),
name = arg.name,
load = if arg.is_simd() {
arg.ty.get_load_function_c()
arg.ty.get_load_function(Language::C)
} else {
"*".to_string()
}
@ -263,7 +208,7 @@ impl ArgumentList {
name = arg.name,
vals_name = arg.rust_vals_array_name(),
load = if arg.is_simd() {
arg.ty.get_load_function_rust()
arg.ty.get_load_function(Language::Rust)
} else {
"*".to_string()
},
@ -273,7 +218,7 @@ impl ArgumentList {
.collect()
}
pub fn iter(&self) -> std::slice::Iter<'_, Argument> {
pub fn iter(&self) -> std::slice::Iter<'_, Argument<T, M>> {
self.args.iter()
}
}

View file

@ -0,0 +1,91 @@
use crate::common::argument::ArgumentList;
use crate::common::format::Indentation;
use crate::common::intrinsic_types::IntrinsicTypeDefinition;
use super::argument::MetadataDefinition;
/// An intrinsic
#[derive(Debug, PartialEq, Clone)]
pub struct Intrinsic<T: IntrinsicTypeDefinition, M: MetadataDefinition> {
/// The function name of this intrinsic.
pub name: String,
/// Any arguments for this intrinsic.
pub arguments: ArgumentList<T, M>,
/// The return type of this intrinsic.
pub results: T,
/// Whether this intrinsic is only available on A64.
pub a64_only: bool,
}
pub trait IntrinsicDefinition<T, M>
where
T: IntrinsicTypeDefinition,
M: MetadataDefinition,
{
fn arguments(&self) -> ArgumentList<T, M>;
fn results(&self) -> T;
fn name(&self) -> String;
/// Generates a std::cout for the intrinsics results that will match the
/// rust debug output format for the return type. The generated line assumes
/// there is an int i in scope which is the current pass number.
fn print_result_c(&self, _indentation: Indentation, _additional: &str) -> String {
unimplemented!("Architectures need to implement print_result_c!")
}
fn generate_loop_c(
&self,
indentation: Indentation,
additional: &str,
passes: u32,
_target: &str,
) -> String {
let body_indentation = indentation.nested();
format!(
"{indentation}for (int i=0; i<{passes}; i++) {{\n\
{loaded_args}\
{body_indentation}auto __return_value = {intrinsic_call}({args});\n\
{print_result}\n\
{indentation}}}",
loaded_args = self.arguments().load_values_c(body_indentation),
intrinsic_call = self.name(),
args = self.arguments().as_call_param_c(),
print_result = self.print_result_c(body_indentation, additional)
)
}
fn generate_loop_rust(
&self,
indentation: Indentation,
additional: &str,
passes: u32,
) -> String {
let constraints = self.arguments().as_constraint_parameters_rust();
let constraints = if !constraints.is_empty() {
format!("::<{constraints}>")
} else {
constraints
};
let indentation2 = indentation.nested();
let indentation3 = indentation2.nested();
format!(
"{indentation}for i in 0..{passes} {{\n\
{indentation2}unsafe {{\n\
{loaded_args}\
{indentation3}let __return_value = {intrinsic_call}{const}({args});\n\
{indentation3}println!(\"Result {additional}-{{}}: {{:.150?}}\", i + 1, __return_value);\n\
{indentation2}}}\n\
{indentation}}}",
loaded_args = self.arguments().load_values_rust(indentation3),
intrinsic_call = self.name(),
const = constraints,
args = self.arguments().as_call_param_rust(),
)
}
}

View file

@ -0,0 +1,352 @@
use std::fmt;
use std::str::FromStr;
use itertools::Itertools as _;
use crate::common::format::Indentation;
use crate::common::types::Language;
use crate::common::values::value_for_array;
#[derive(Debug, PartialEq, Copy, Clone)]
pub enum TypeKind {
BFloat,
Float,
Int,
UInt,
Poly,
Void,
}
impl FromStr for TypeKind {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bfloat" => Ok(Self::BFloat),
"float" => Ok(Self::Float),
"int" => Ok(Self::Int),
"poly" => Ok(Self::Poly),
"uint" | "unsigned" => Ok(Self::UInt),
"void" => Ok(Self::Void),
_ => Err(format!("Impossible to parse argument kind {s}")),
}
}
}
impl fmt::Display for TypeKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
match self {
Self::BFloat => "bfloat",
Self::Float => "float",
Self::Int => "int",
Self::UInt => "uint",
Self::Poly => "poly",
Self::Void => "void",
}
)
}
}
impl TypeKind {
/// Gets the type part of a c typedef for a type that's in the form of {type}{size}_t.
pub fn c_prefix(&self) -> &str {
match self {
Self::Float => "float",
Self::Int => "int",
Self::UInt => "uint",
Self::Poly => "poly",
_ => unreachable!("Not used: {:#?}", self),
}
}
/// Gets the rust prefix for the type kind i.e. i, u, f.
pub fn rust_prefix(&self) -> &str {
match self {
Self::Float => "f",
Self::Int => "i",
Self::UInt => "u",
Self::Poly => "u",
_ => unreachable!("Unused type kind: {:#?}", self),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct IntrinsicType {
pub constant: bool,
/// whether this object is a const pointer
pub ptr_constant: bool,
pub ptr: bool,
pub kind: TypeKind,
/// The bit length of this type (e.g. 32 for u32).
pub bit_len: Option<u32>,
/// Length of the SIMD vector (i.e. 4 for uint32x4_t), A value of `None`
/// means this is not a simd type. A `None` can be assumed to be 1,
/// although in some places a distinction is needed between `u64` and
/// `uint64x1_t` this signals that.
pub simd_len: Option<u32>,
/// The number of rows for SIMD matrices (i.e. 2 for uint8x8x2_t).
/// A value of `None` represents a type that does not contain any
/// rows encoded in the type (e.g. uint8x8_t).
/// A value of `None` can be assumed to be 1 though.
pub vec_len: Option<u32>,
pub target: String,
}
pub trait BaseIntrinsicTypeDefinition {
/// Get the TypeKind for this type, recursing into pointers.
fn kind(&self) -> TypeKind;
/// Get the size of a single element inside this type, recursing into
/// pointers, i.e. a pointer to a u16 would be 16 rather than the size
/// of a pointer.
fn inner_size(&self) -> u32;
fn num_lanes(&self) -> u32;
fn num_vectors(&self) -> u32;
/// Determine if the type is a simd type, this will treat a type such as
/// `uint64x1` as simd.
fn is_simd(&self) -> bool;
fn is_ptr(&self) -> bool;
fn c_scalar_type(&self) -> String;
fn rust_scalar_type(&self) -> String;
/// Gets a cast for this type if needs promotion.
/// This is required for 8 bit types due to printing as the 8 bit types use
/// a char and when using that in `std::cout` it will print as a character,
/// which means value of 0 will be printed as a null byte.
///
/// This is also needed for polynomial types because we want them to be
/// printed as unsigned integers to match Rust's `Debug` impl.
fn c_promotion(&self) -> &str;
/// Generates an initialiser for an array, which can be used to initialise an argument for the
/// intrinsic call.
///
/// This is determistic based on the pass number.
///
/// * `loads`: The number of values that need to be loaded from the argument array
/// * e.g for argument type uint32x2, loads=2 results in a string representing 4 32-bit values
///
/// Returns a string such as
/// * `{0x1, 0x7F, 0xFF}` if `language` is `Language::C`
/// * `[0x1 as _, 0x7F as _, 0xFF as _]` if `language` is `Language::Rust`
fn populate_random(&self, indentation: Indentation, loads: u32, language: &Language) -> String;
fn is_rust_vals_array_const(&self) -> bool;
fn as_call_param_c(&self, name: &String) -> String;
}
impl BaseIntrinsicTypeDefinition for IntrinsicType {
fn kind(&self) -> TypeKind {
self.kind
}
fn inner_size(&self) -> u32 {
if let Some(bl) = self.bit_len {
bl
} else {
unreachable!("")
}
}
fn num_lanes(&self) -> u32 {
if let Some(sl) = self.simd_len { sl } else { 1 }
}
fn num_vectors(&self) -> u32 {
if let Some(vl) = self.vec_len { vl } else { 1 }
}
fn is_simd(&self) -> bool {
self.simd_len.is_some() || self.vec_len.is_some()
}
fn is_ptr(&self) -> bool {
self.ptr
}
fn c_scalar_type(&self) -> String {
format!(
"{prefix}{bits}_t",
prefix = self.kind().c_prefix(),
bits = self.inner_size()
)
}
fn rust_scalar_type(&self) -> String {
format!(
"{prefix}{bits}",
prefix = self.kind().rust_prefix(),
bits = self.inner_size()
)
}
fn c_promotion(&self) -> &str {
match *self {
IntrinsicType {
kind,
bit_len: Some(8),
..
} => match kind {
TypeKind::Int => "(int)",
TypeKind::UInt => "(unsigned int)",
TypeKind::Poly => "(unsigned int)(uint8_t)",
_ => "",
},
IntrinsicType {
kind: TypeKind::Poly,
bit_len: Some(bit_len),
..
} => match bit_len {
8 => unreachable!("handled above"),
16 => "(uint16_t)",
32 => "(uint32_t)",
64 => "(uint64_t)",
128 => "",
_ => panic!("invalid bit_len"),
},
_ => "",
}
}
fn populate_random(&self, indentation: Indentation, loads: u32, language: &Language) -> String {
match self {
IntrinsicType {
bit_len: Some(bit_len @ (8 | 16 | 32 | 64)),
kind: kind @ (TypeKind::Int | TypeKind::UInt | TypeKind::Poly),
simd_len,
vec_len,
..
} => {
let (prefix, suffix) = match language {
Language::Rust => ("[", "]"),
Language::C => ("{", "}"),
};
let body_indentation = indentation.nested();
format!(
"{prefix}\n{body}\n{indentation}{suffix}",
body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1))
.format_with(",\n", |i, fmt| {
let src = value_for_array(*bit_len, i);
assert!(src == 0 || src.ilog2() < *bit_len);
if *kind == TypeKind::Int && (src >> (*bit_len - 1)) != 0 {
// `src` is a two's complement representation of a negative value.
let mask = !0u64 >> (64 - *bit_len);
let ones_compl = src ^ mask;
let twos_compl = ones_compl + 1;
if (twos_compl == src) && (language == &Language::C) {
// `src` is INT*_MIN. C requires `-0x7fffffff - 1` to avoid
// undefined literal overflow behaviour.
fmt(&format_args!("{body_indentation}-{ones_compl:#x} - 1"))
} else {
fmt(&format_args!("{body_indentation}-{twos_compl:#x}"))
}
} else {
fmt(&format_args!("{body_indentation}{src:#x}"))
}
})
)
}
IntrinsicType {
kind: TypeKind::Float,
bit_len: Some(bit_len @ (16 | 32 | 64)),
simd_len,
vec_len,
..
} => {
let (prefix, cast_prefix, cast_suffix, suffix) = match (language, bit_len) {
(&Language::Rust, 16) => ("[", "f16::from_bits(", ")", "]"),
(&Language::Rust, 32) => ("[", "f32::from_bits(", ")", "]"),
(&Language::Rust, 64) => ("[", "f64::from_bits(", ")", "]"),
(&Language::C, 16) => ("{", "cast<float16_t, uint16_t>(", ")", "}"),
(&Language::C, 32) => ("{", "cast<float, uint32_t>(", ")", "}"),
(&Language::C, 64) => ("{", "cast<double, uint64_t>(", ")", "}"),
_ => unreachable!(),
};
format!(
"{prefix}\n{body}\n{indentation}{suffix}",
body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1))
.format_with(",\n", |i, fmt| fmt(&format_args!(
"{indentation}{cast_prefix}{src:#x}{cast_suffix}",
indentation = indentation.nested(),
src = value_for_array(*bit_len, i)
)))
)
}
_ => unimplemented!("populate random: {:#?}", self),
}
}
fn is_rust_vals_array_const(&self) -> bool {
match self {
// Floats have to be loaded at runtime for stable NaN conversion.
IntrinsicType {
kind: TypeKind::Float,
..
} => false,
IntrinsicType {
kind: TypeKind::Int | TypeKind::UInt | TypeKind::Poly,
..
} => true,
_ => unimplemented!(),
}
}
fn as_call_param_c(&self, name: &String) -> String {
if self.ptr {
format!("&{}", name)
} else {
name.clone()
}
}
}
pub trait IntrinsicTypeDefinition: BaseIntrinsicTypeDefinition {
/// Determines the load function for this type.
/// can be implemented in an `impl` block
fn get_load_function(&self, _language: Language) -> String {
unimplemented!("Different architectures must implement get_load_function!")
}
/// can be implemented in an `impl` block
fn get_lane_function(&self) -> String {
unimplemented!("Different architectures must implement get_lane_function!")
}
/// can be implemented in an `impl` block
fn from_c(_s: &str, _target: &String) -> Result<Box<Self>, String> {
unimplemented!("Different architectures must implement from_c!")
}
/// Gets a string containing the typename for this type in C format.
/// can be directly defined in `impl` blocks
fn c_type(&self) -> String {
unimplemented!("Different architectures must implement c_type!")
}
/// can be directly defined in `impl` blocks
fn c_single_vector_type(&self) -> String {
unimplemented!("Different architectures must implement c_single_vector_type!")
}
/// can be defined in `impl` blocks
fn rust_type(&self) -> String {
unimplemented!("Different architectures must implement rust_type!")
}
}

View file

@ -2,9 +2,13 @@ use crate::common::types::ProcessedCli;
use std::fs::File;
use std::io::Write;
pub mod argument;
pub mod compare;
pub mod format;
pub mod gen_c;
pub mod gen_rust;
pub mod intrinsic;
pub mod intrinsic_types;
pub mod types;
pub mod values;