blob: 9b8eb8e8b9bfe7ddfb24678d4439939a62121ea6 [file] [log] [blame]
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::{
expression::Expression,
input::{InputSet, InputType},
intrinsic::{Constraint, Intrinsic, Signature},
matching::SizeMatchable,
predicate_forms::PredicateForm,
typekinds::{ToRepr, TypeKind},
wildcards::Wildcard,
wildstring::WildString,
};
/// Maximum SVE vector size
const SVE_VECTOR_MAX_SIZE: u32 = 2048;
/// Vector register size
const VECTOR_REG_SIZE: u32 = 128;
/// Generator result
pub type Result<T = ()> = std::result::Result<T, String>;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ArchitectureSettings {
#[serde(alias = "arch")]
pub arch_name: String,
pub target_feature: Vec<String>,
#[serde(alias = "llvm_prefix")]
pub llvm_link_prefix: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalContext {
pub arch_cfgs: Vec<ArchitectureSettings>,
#[serde(default)]
pub uses_neon_types: bool,
/// Should the yaml file automagically generate big endian shuffling
#[serde(default)]
pub auto_big_endian: Option<bool>,
/// Should all LLVM wrappers convert their arguments to a signed type
#[serde(default)]
pub auto_llvm_sign_conversion: bool,
}
/// Context of an intrinsic group
#[derive(Debug, Clone, Default)]
pub struct GroupContext {
/// LLVM links to target input sets
pub links: HashMap<String, InputSet>,
}
#[derive(Debug, Clone, Copy)]
pub enum VariableType {
Argument,
Internal,
}
#[derive(Debug, Clone)]
pub struct LocalContext {
pub signature: Signature,
pub input: InputSet,
pub substitutions: HashMap<Wildcard, String>,
pub variables: HashMap<String, (TypeKind, VariableType)>,
}
impl LocalContext {
pub fn new(input: InputSet, original: &Intrinsic) -> LocalContext {
LocalContext {
signature: original.signature.clone(),
input,
substitutions: HashMap::new(),
variables: HashMap::new(),
}
}
pub fn provide_type_wildcard(&self, wildcard: &Wildcard) -> Result<TypeKind> {
let err = || {
format!(
"provide_type_wildcard() wildcard {{{wildcard}}} not found for {}",
&self.signature.name.to_string()
)
};
/* If the type is already a vector then we can just return the vector */
let make_neon = |tuple_size| {
move |ty| match ty {
TypeKind::Vector(_) => Ok(ty),
_ => TypeKind::make_vector(ty, false, tuple_size),
}
};
let make_sve = |tuple_size| move |ty| TypeKind::make_vector(ty, true, tuple_size);
match wildcard {
Wildcard::Type(idx) => self.input.typekind(*idx).ok_or_else(err),
Wildcard::NEONType(idx, tuple_size, _) => self
.input
.typekind(*idx)
.ok_or_else(|| {
dbg!("{:?}", &self);
err()
})
.and_then(make_neon(*tuple_size)),
Wildcard::SVEType(idx, tuple_size) => self
.input
.typekind(*idx)
.ok_or_else(err)
.and_then(make_sve(*tuple_size)),
Wildcard::Predicate(idx) => self.input.typekind(*idx).map_or_else(
|| {
if idx.is_none() && self.input.types_len() == 1 {
Err(err())
} else {
Err(format!(
"there is no type at index {} to infer the predicate from",
idx.unwrap_or(0)
))
}
},
|ref ty| TypeKind::make_predicate_from(ty),
),
Wildcard::MaxPredicate => self
.input
.iter()
.filter_map(|arg| arg.typekind())
.max_by(|x, y| {
x.base_type()
.and_then(|bt| bt.get_size().ok())
.unwrap_or(0)
.cmp(&y.base_type().and_then(|bt| bt.get_size().ok()).unwrap_or(0))
})
.map_or_else(
|| Err("there are no types available to infer the predicate from".to_string()),
TypeKind::make_predicate_from,
),
Wildcard::Scale(w, as_ty) => {
let mut ty = self.provide_type_wildcard(w)?;
if let Some(vty) = ty.vector_mut() {
let base_ty = if let Some(w) = as_ty.wildcard() {
*self.provide_type_wildcard(w)?.base_type().unwrap()
} else {
*as_ty.base_type().unwrap()
};
vty.cast_base_type_as(base_ty)
}
Ok(ty)
}
_ => Err(err()),
}
}
pub fn provide_substitution_wildcard(&self, wildcard: &Wildcard) -> Result<String> {
let err = || Err(format!("wildcard {{{wildcard}}} not found"));
match wildcard {
Wildcard::SizeLiteral(idx) => self.input.typekind(*idx)
.map_or_else(err, |ty| Ok(ty.size_literal())),
Wildcard::Size(idx) => self.input.typekind(*idx)
.map_or_else(err, |ty| Ok(ty.size())),
Wildcard::SizeMinusOne(idx) => self.input.typekind(*idx)
.map_or_else(err, |ty| Ok((ty.size().parse::<i32>().unwrap()-1).to_string())),
Wildcard::SizeInBytesLog2(idx) => self.input.typekind(*idx)
.map_or_else(err, |ty| Ok(ty.size_in_bytes_log2())),
Wildcard::NVariant if !self.substitutions.contains_key(wildcard) => Ok(String::new()),
Wildcard::TypeKind(idx, opts) => {
self.input.typekind(*idx)
.map_or_else(err, |ty| {
let literal = if let Some(opts) = opts {
#[allow(clippy::obfuscated_if_else)]
opts.contains(ty.base_type().map(|bt| *bt.kind()).ok_or_else(|| {
format!("cannot retrieve a type literal out of {ty}")
})?)
.then(|| ty.type_kind())
.unwrap_or_default()
} else {
ty.type_kind()
};
Ok(literal)
})
}
Wildcard::PredicateForms(_) => self
.input
.iter()
.find_map(|arg| {
if let InputType::PredicateForm(pf) = arg {
Some(pf.get_suffix().to_string())
} else {
None
}
})
.ok_or_else(|| unreachable!("attempting to render a predicate form wildcard, but no predicate form was compiled for it")),
_ => self
.substitutions
.get(wildcard)
.map_or_else(err, |s| Ok(s.clone())),
}
}
pub fn make_assertion_from_constraint(&self, constraint: &Constraint) -> Result<Expression> {
match constraint {
Constraint::AnyI32 {
variable,
any_values,
} => {
let where_ex = any_values
.iter()
.map(|value| format!("{variable} == {value}"))
.join(" || ");
Ok(Expression::MacroCall("static_assert".to_string(), where_ex))
}
Constraint::RangeI32 {
variable,
range: SizeMatchable::Matched(range),
} => Ok(Expression::MacroCall(
"static_assert_range".to_string(),
format!(
"{variable}, {min}, {max}",
min = range.start(),
max = range.end()
),
)),
Constraint::SVEMaxElems {
variable,
sve_max_elems_type: ty,
}
| Constraint::VecMaxElems {
variable,
vec_max_elems_type: ty,
} => {
if !self.input.is_empty() {
let higher_limit = match constraint {
Constraint::SVEMaxElems { .. } => SVE_VECTOR_MAX_SIZE,
Constraint::VecMaxElems { .. } => VECTOR_REG_SIZE,
_ => unreachable!(),
};
let max = ty.base_type()
.map(|ty| ty.get_size())
.transpose()?
.map_or_else(
|| Err(format!("can't make an assertion out of constraint {self:?}: no valid type is present")),
|bitsize| Ok(higher_limit / bitsize - 1))?;
Ok(Expression::MacroCall(
"static_assert_range".to_string(),
format!("{variable}, 0, {max}"),
))
} else {
Err(format!(
"can't make an assertion out of constraint {self:?}: no types are being used"
))
}
}
_ => unreachable!("constraints were not built successfully!"),
}
}
pub fn predicate_form(&self) -> Option<&PredicateForm> {
self.input.iter().find_map(|arg| arg.predicate_form())
}
pub fn n_variant_op(&self) -> Option<&WildString> {
self.input.iter().find_map(|arg| arg.n_variant_op())
}
}
pub struct Context<'ctx> {
pub local: &'ctx mut LocalContext,
pub group: &'ctx mut GroupContext,
pub global: &'ctx GlobalContext,
}