use pliron::{
builtin::{
attributes::VecAttr,
types::{IntegerType, Signedness},
},
context::Context,
r#type::{Type, TypeHandle},
};
use pliron_mlir_builtin::types::{
ElementType, IndexType, TensorType, UnrankedTensorType, VectorType, primitive_element,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Container {
Scalar,
Tensor {
dimensions: Vec<i64>,
encoding: VecAttr,
},
UnrankedTensor,
Vector {
dimensions: Vec<i64>,
scalable: Vec<bool>,
},
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Shaped {
pub element: TypeHandle,
pub container: Container,
}
impl Shaped {
pub fn same_shape(&self, other: &Self) -> bool {
match (&self.container, &other.container) {
(Container::Scalar, Container::Scalar)
| (Container::UnrankedTensor, Container::UnrankedTensor) => true,
(Container::Tensor { dimensions: a, .. }, Container::Tensor { dimensions: b, .. }) => {
a == b
}
(
Container::Vector {
dimensions: a,
scalable: sa,
},
Container::Vector {
dimensions: b,
scalable: sb,
},
) => a == b && sa == sb,
_ => false,
}
}
}
pub fn is_scalar(ctx: &Context, ty: TypeHandle) -> bool {
primitive_element(ctx, ty)
.is_some_and(|e| e == ElementType::Index || e.is_integer() || e.is_float())
}
pub fn classify(ctx: &Context, ty: TypeHandle) -> Option<Shaped> {
let (element, container) = {
let r = ty.deref(ctx);
if let Some(t) = r.downcast_ref::<TensorType>() {
(
t.element,
Container::Tensor {
dimensions: t.dimensions.clone(),
encoding: t.encoding.clone(),
},
)
} else if let Some(t) = r.downcast_ref::<UnrankedTensorType>() {
(t.element, Container::UnrankedTensor)
} else if let Some(v) = r.downcast_ref::<VectorType>() {
(
v.element,
Container::Vector {
dimensions: v.dimensions.clone(),
scalable: v.scalable.clone(),
},
)
} else {
(ty, Container::Scalar)
}
};
is_scalar(ctx, element).then_some(Shaped { element, container })
}
pub fn element_type(ctx: &Context, ty: TypeHandle) -> Option<TypeHandle> {
classify(ctx, ty).map(|s| s.element)
}
pub fn with_element(ctx: &Context, shaped: &Shaped, element: TypeHandle) -> TypeHandle {
match &shaped.container {
Container::Scalar => element,
Container::Tensor {
dimensions,
encoding,
} => <TensorType as Type>::instantiate(
TensorType {
dimensions: dimensions.clone(),
element,
encoding: encoding.clone(),
},
ctx,
)
.into(),
Container::UnrankedTensor => UnrankedTensorType::get(ctx, element).into(),
Container::Vector {
dimensions,
scalable,
} => VectorType::get(ctx, element, dimensions.clone(), scalable.clone()).into(),
}
}
pub fn i1(ctx: &Context) -> TypeHandle {
IntegerType::get(ctx, 1, Signedness::Signless).into()
}
pub fn index(ctx: &Context) -> TypeHandle {
ElementType::Index.native(ctx)
}
pub fn is_float(ctx: &Context, element: TypeHandle) -> bool {
primitive_element(ctx, element).is_some_and(ElementType::is_float)
}
pub fn is_index(ctx: &Context, element: TypeHandle) -> bool {
element.deref(ctx).downcast_ref::<IndexType>().is_some()
}
pub fn signless_width(ctx: &Context, element: TypeHandle) -> Option<u32> {
element
.deref(ctx)
.downcast_ref::<IntegerType>()
.filter(|i| i.signedness() == Signedness::Signless)
.map(|i| i.width())
}
pub fn is_signless_int_or_index(ctx: &Context, element: TypeHandle) -> bool {
signless_width(ctx, element).is_some() || is_index(ctx, element)
}
pub fn float_width(ctx: &Context, element: TypeHandle) -> Option<u32> {
primitive_element(ctx, element)
.filter(|e| e.is_float())
.and_then(ElementType::bit_width)
}
pub fn scalar_width(ctx: &Context, element: TypeHandle) -> Option<u32> {
signless_width(ctx, element).or_else(|| float_width(ctx, element))
}