use cubecl::prelude::*;
use crate::*;
#[allow(clippy::large_enum_variant)]
#[expect(
dead_code,
reason = "built through the expand type's generated constructors"
)]
#[derive(CubeType, Clone)]
#[expand(derive(Clone))]
pub(crate) enum AxisSlicesKind<E: Float> {
Registers(RegisterData<E>),
Window(Memory<E>),
Fragments(PlanePartition<E>),
Fragment(PlaneTile<E>),
}
#[derive(CubeType)]
pub struct AxisSlices<E: Float> {
kind: AxisSlicesKind<E>,
#[cube(comptime)]
space: Space,
#[cube(comptime)]
axis: Axis,
}
#[cube]
impl<E: Float> Tile<E> {
pub fn along(&self, #[comptime] axis: Axis) -> AxisSlices<E> {
let holder = comptime!(self.place.holder());
let space = comptime!(self.place.space.clone());
comptime!(assert!(
space.axis_at(space.rank() - 1) == axis,
"Tile::along: a slice runs along the tile's innermost axis, and {axis:?} is not the \
innermost of {space:?}"
));
let kind = match &self.kind {
TileKind::PlanePartition(partition) => partition.slices(),
TileKind::PlaneTile(tile) => tile.slices(),
TileKind::Memory(window) => {
comptime!(assert!(
holder == ComputeScope::Plane,
"Tile::along: a window of shared memory is read slice by slice by the plane \
that holds it; this one is held by {holder:?}"
));
AxisSlicesKind::new_Window(window.clone())
}
TileKind::TmaGmem(_) | TileKind::Procedural(_) | TileKind::Lines(_) => panic!(
"Tile::along: a register block, a plane's fragments, or a plane's window holds \
slices"
),
};
AxisSlices::<E> { kind, space, axis }
}
}
#[cube]
impl<E: Float> Tile<E> {
pub(crate) fn cells(&self, #[comptime] count: usize) -> Array<E> {
comptime!(assert!(
self.place.holder() == ComputeScope::Plane && self.place.space.cells() == count,
"Tile::cells: a plane's window of {count} cells, not {:?} held by {:?}",
self.place.space,
self.place.holder()
));
match &self.kind {
TileKind::Memory(window) => window.cells(count),
TileKind::PlanePartition(_)
| TileKind::PlaneTile(_)
| TileKind::TmaGmem(_)
| TileKind::Procedural(_)
| TileKind::Lines(_) => Tile::<E>::refuse_cells(),
}
}
pub(crate) fn set_cells(&mut self, values: &Array<E>, #[comptime] count: usize) {
comptime!(assert!(
self.place.holder() == ComputeScope::Plane && self.place.space.cells() == count,
"Tile::cells: a plane's window of {count} cells, not {:?} held by {:?}",
self.place.space,
self.place.holder()
));
match &mut self.kind {
TileKind::Memory(window) => window.set_cells(values, count),
TileKind::PlanePartition(_)
| TileKind::PlaneTile(_)
| TileKind::TmaGmem(_)
| TileKind::Procedural(_)
| TileKind::Lines(_) => Tile::<E>::refuse_cells(),
}
}
fn refuse_cells() -> ! {
panic!("Tile::cells: a plane's window of memory holds the cells every unit of it reads")
}
}
#[cube]
impl<E: Float> PlanePartition<E> {
pub(crate) fn slices(&self) -> AxisSlicesKind<E> {
match self.at(0usize, 0usize) {
PlaneTile::Registers(block) => {
comptime!(assert!(
self.m_tiles * self.n_tiles == 1,
"Tile::along: a unit's slices are one register block"
));
AxisSlicesKind::new_Registers(block)
}
PlaneTile::Cmma(_) | PlaneTile::Mma(_) => AxisSlicesKind::new_Fragments(self.clone()),
}
}
}
#[cube]
impl<E: Float> PlaneTile<E> {
pub(crate) fn slices(&self) -> AxisSlicesKind<E> {
match self {
PlaneTile::Registers(block) => AxisSlicesKind::new_Registers(block.clone()),
PlaneTile::Cmma(_) | PlaneTile::Mma(_) => AxisSlicesKind::new_Fragment(self.clone()),
}
}
}
#[cube]
impl<E: Float> AxisSlices<E> {
pub(crate) fn scale_add(&mut self, scale: E, bias: &Tile<E>) {
let recipe = bias.recipe();
match &mut self.kind {
AxisSlicesKind::Registers(block) => {
block.scale_add_along(scale, recipe, self.space.clone())
}
AxisSlicesKind::Window(window) => {
window.scale_add_along(scale, recipe, self.space.clone(), self.axis)
}
AxisSlicesKind::Fragments(_) | AxisSlicesKind::Fragment(_) => {
AxisSlices::<E>::refuse_reading()
}
}
}
pub(crate) fn maxima(&self, seed: &Array<E>) -> Array<E> {
match &self.kind {
AxisSlicesKind::Registers(block) => block.maxima_along(seed),
AxisSlicesKind::Window(window) => {
window.maxima_along(seed, self.space.clone(), self.axis)
}
AxisSlicesKind::Fragments(_) | AxisSlicesKind::Fragment(_) => {
AxisSlices::<E>::refuse_reading()
}
}
}
pub(crate) fn exp_minus(&mut self, slices: &Array<E>) {
match &mut self.kind {
AxisSlicesKind::Registers(block) => block.exp_minus_along(slices),
AxisSlicesKind::Window(window) => {
window.exp_minus_along(slices, self.space.clone(), self.axis)
}
AxisSlicesKind::Fragments(_) | AxisSlicesKind::Fragment(_) => {
AxisSlices::<E>::refuse_reading()
}
}
}
pub(crate) fn sums(&self) -> Array<E> {
match &self.kind {
AxisSlicesKind::Registers(block) => block.sums_along(),
AxisSlicesKind::Window(window) => window.sums_along(self.space.clone(), self.axis),
AxisSlicesKind::Fragments(_) | AxisSlicesKind::Fragment(_) => {
AxisSlices::<E>::refuse_reading()
}
}
}
pub fn mul(&mut self, factors: &Array<E>) {
match &mut self.kind {
AxisSlicesKind::Registers(block) => block.mul_along(factors, 0usize),
AxisSlicesKind::Window(window) => {
window.mul_along(factors, self.space.clone(), self.axis)
}
AxisSlicesKind::Fragments(partition) => partition.mul_along(factors),
AxisSlicesKind::Fragment(tile) => tile.mul_along(factors, 0usize),
}
}
pub(crate) fn exp_minus_cell(value: E, slice: E) -> E {
let live = slice > E::min_value();
select(live, (value - slice).exp(), E::from_int(0))
}
fn refuse_reading() -> ! {
panic!("AxisSlices: a fragment's slices are read after draining it into its plane's window")
}
}