introduced generic types and code refactor
This commit is contained in:
parent
a993b4427c
commit
57c357591e
11 changed files with 812 additions and 644 deletions
60
library/stdarch/crates/intrinsic-test/src/arm/constraint.rs
Normal file
60
library/stdarch/crates/intrinsic-test/src/arm/constraint.rs
Normal 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(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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>,
|
||||
|
|
|
|||
|
|
@ -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("")
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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()],
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
})
|
||||
})))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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!")
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue