use cubecl_ir::{
interfaces::{HasElementType, TypedExt},
prelude::*,
rewrite::visit_all_values,
types::{
ArrayType, AtomicType, Fp8Format, RuntimeArrayType, VectorType,
scalar::{
BoolType, Float8E4M3Type, Float8E5M2Type, Float16Type, Float32Type, Float64Type,
FloatFlex32Type, IndexType,
},
},
};
use pliron::{
builtin::types::IntegerType, identifier::Identifier, input_err_noloc, operation::Operation,
result::Result, r#type::type_cast,
};
use thiserror::Error;
use crate::compiler::wgsl::to_wgsl::{TypeExtWgsl, TypeToWgsl};
macro_rules! scalar_ty {
($ty: ty, $wgsl: literal) => {
#[type_interface_impl]
impl TypeToWgsl for $ty {
fn to_wgsl(&self, _ctx: &Context) -> String {
$wgsl.into()
}
}
};
}
scalar_ty!(Float16Type, "f16");
scalar_ty!(Float32Type, "f32");
scalar_ty!(FloatFlex32Type, "f32");
scalar_ty!(Float64Type, "f64");
scalar_ty!(BoolType, "bool");
#[type_interface_impl]
impl TypeToWgsl for IndexType {
fn to_wgsl(&self, ctx: &Context) -> String {
ctx.address_type().unsigned_type().to_type(ctx).to_wgsl(ctx)
}
}
#[type_interface_impl]
impl TypeToWgsl for IntegerType {
fn to_wgsl(&self, _ctx: &Context) -> String {
match self.is_signed() {
true => format!("i{}", self.width()),
false => format!("u{}", self.width()),
}
}
}
const FP8_LANES_PER_WORD: usize = 4;
#[derive(Debug, Error)]
#[error(
"fp8 on WGSL is packed {FP8_LANES_PER_WORD} lanes to a u32, a {0} has no representation: use \
vectors of {FP8_LANES_PER_WORD}, 8 or 16 lanes"
)]
pub struct Fp8Unsupported(String);
pub fn check_fp8_lanes(ctx: &Context, module: Ptr<Operation>) -> Result<()> {
let mut bad = None;
visit_all_values(ctx, &mut bad, module, |ctx, bad, value| {
if bad.is_none()
&& let Some(lanes) = fp8_lanes(ctx, value.get_type(ctx))
&& !lanes.is_multiple_of(FP8_LANES_PER_WORD)
{
*bad = Some(lanes);
}
});
match bad {
Some(lanes) => input_err_noloc!(Fp8Unsupported(describe(lanes))),
None => Ok(()),
}
}
fn fp8_lanes(ctx: &Context, ty: TypeHandle) -> Option<usize> {
let deref = ty.deref(ctx);
if let Some(vector) = deref.downcast_ref::<VectorType>() {
return Fp8Format::of_type(ctx, vector.inner).map(|_| vector.vectorization);
}
if Fp8Format::of_type(ctx, ty).is_some() {
return Some(1);
}
let elem = type_cast::<dyn HasElementType>(&*deref)?.element_type(ctx)?;
(elem != ty).then(|| fp8_lanes(ctx, elem)).flatten()
}
fn describe(lanes: usize) -> String {
match lanes {
1 => "scalar".to_string(),
lanes => format!("vector of {lanes} lanes"),
}
}
fn fp8_unsupported(what: &str) -> ! {
panic!("{}", Fp8Unsupported(what.to_string()))
}
macro_rules! fp8_scalar_ty {
($ty: ty) => {
#[type_interface_impl]
impl TypeToWgsl for $ty {
fn to_wgsl(&self, _ctx: &Context) -> String {
fp8_unsupported("scalar")
}
}
};
}
fp8_scalar_ty!(Float8E4M3Type);
fp8_scalar_ty!(Float8E5M2Type);
#[type_interface_impl]
impl TypeToWgsl for VectorType {
fn to_wgsl(&self, ctx: &Context) -> String {
if Fp8Format::of_type(ctx, self.inner.scalar_ty(ctx)).is_some() {
if !self.vectorization.is_multiple_of(FP8_LANES_PER_WORD) {
fp8_unsupported(&describe(self.vectorization));
}
let words = self.vectorization / FP8_LANES_PER_WORD;
return match words {
1 => "u32".to_string(),
words => format!("vec{words}<u32>"),
};
}
format!("vec{}<{}>", self.vectorization, self.inner.to_wgsl(ctx))
}
}
#[type_interface_impl]
impl TypeToWgsl for AtomicType {
fn to_wgsl(&self, ctx: &Context) -> String {
format!("atomic<{}>", self.inner.to_wgsl(ctx))
}
}
#[type_interface_impl]
impl TypeToWgsl for ArrayType {
fn to_wgsl(&self, ctx: &Context) -> String {
format!("array<{}, {}>", self.inner.to_wgsl(ctx), self.length)
}
}
#[type_interface_impl]
impl TypeToWgsl for RuntimeArrayType {
fn to_wgsl(&self, ctx: &Context) -> String {
format!("array<{}>", self.inner.to_wgsl(ctx))
}
}
#[pliron_type(
name = "wgsl.struct",
format = "$name",
generate_get = true,
verifier = "succ"
)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct StructType {
name: Identifier,
}
#[type_interface_impl]
impl TypeToWgsl for StructType {
fn to_wgsl(&self, _ctx: &Context) -> String {
format!("{}", self.name)
}
}