use cubecl::prelude::*;
use super::leaf::memory;
use crate::tile::base::witnessed_space;
use crate::*;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub(crate) enum Descent {
Here,
Steps,
Cells(Instruction),
}
impl Descent {
pub(crate) fn of(grid: Option<Instruction>, acc: &Placement, lhs: &Space, rhs: &Space) -> Self {
let Some(instruction) = grid else {
return Descent::Here;
};
let below = acc.below().first().unwrap_or_else(|| {
panic!("mma: a grid of fragments contracts at a level with its cells below it")
});
let operands = Space::merge(&[lhs, rhs]);
let steps_contraction = below
.axes()
.iter()
.any(|&axis| operands.contains(axis) && !acc.space.contains(axis));
match steps_contraction {
true => Descent::Steps,
false => Descent::Cells(instruction),
}
}
}
#[cube]
impl Descent {
pub(crate) fn of_tile<E: Numeric, Lhs: Numeric, Rhs: Numeric>(
acc: &Tile<E>,
lhs: &Tile<Lhs>,
rhs: &Tile<Rhs>,
) -> comptime_type!(Descent) {
match &acc.kind {
TileKind::PlanePartition(p) => {
let grid = p.grid();
comptime!(Descent::of(
grid,
&acc.place,
&lhs.place.space,
&rhs.place.space
))
}
TileKind::PlaneTile(_)
| TileKind::Memory(_)
| TileKind::TmaGmem(_)
| TileKind::Procedural(_)
| TileKind::Lines(_) => comptime!(Descent::Here),
}
}
pub(crate) fn contract<E: Numeric, Lhs: Numeric, Rhs: Numeric>(
acc: &mut Tile<E>,
lhs: &Tile<Lhs>,
rhs: &Tile<Rhs>,
) {
let grid = acc.clone();
let descent = Self::of_tile::<E, Lhs, Rhs>(&grid, lhs, rhs);
match comptime!(descent) {
Descent::Here => Self::here::<E, Lhs, Rhs>(acc, lhs, rhs),
Descent::Steps => Self::steps::<E, Lhs, Rhs>(&grid, lhs, rhs),
Descent::Cells(instruction) => Self::cells::<E, Lhs, Rhs>(&grid, lhs, rhs, instruction),
}
}
fn steps<E: Numeric, Lhs: Numeric, Rhs: Numeric>(
acc: &Tile<E>,
lhs: &Tile<Lhs>,
rhs: &Tile<Rhs>,
) {
let operands = comptime!(Space::merge(&[
&acc.place.space,
&lhs.place.space,
&rhs.place.space
]));
let space = witnessed_space(operands, acc, lhs, rhs);
let walk = Region::rooted(
&space,
comptime!(acc.place.levels.clone()),
comptime!(acc.place.depth),
)
.walk();
for region in walk.unrolled() {
let mut acc_region = acc.at(®ion);
Self::contract::<E, Lhs, Rhs>(&mut acc_region, &lhs.at(®ion), &rhs.at(®ion));
}
}
fn cells<E: Numeric, Lhs: Numeric, Rhs: Numeric>(
acc: &Tile<E>,
lhs: &Tile<Lhs>,
rhs: &Tile<Rhs>,
#[comptime] instruction: Instruction,
) {
let lhs = PlanePartition::<Lhs>::operand(lhs, acc, instruction);
let rhs = PlanePartition::<Rhs>::operand(rhs, acc, instruction);
for cell in acc.walk().unrolled() {
let mut acc_cell = acc.at(&cell);
Self::contract::<E, Lhs, Rhs>(&mut acc_cell, &lhs.at(&cell), &rhs.at(&cell));
}
}
fn here<E: Numeric, Lhs: Numeric, Rhs: Numeric>(
acc: &mut Tile<E>,
lhs: &Tile<Lhs>,
rhs: &Tile<Rhs>,
) {
let space = comptime!(acc.place.space.clone());
let tile_kind = &mut acc.kind;
match tile_kind {
TileKind::PlaneTile(t) => t.mma(lhs, rhs, space),
TileKind::PlanePartition(p) => {
let mut t = p.at(0usize, 0usize);
t.mma(lhs, rhs, space)
}
TileKind::Memory(g) => {
let contraction = g.stated_contraction();
memory::contract::<E, Lhs, Rhs>(
g,
lhs,
rhs,
space,
contraction.block,
contraction.semiring,
)
}
TileKind::TmaGmem(_) => panic!("mma: a tma source is not an accumulator sink"),
TileKind::Procedural(_) | TileKind::Lines(_) => {
panic!("mma: a procedural tile and the plane's units are not an accumulator sink")
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
const M: Axis = Axis(0);
const N: Axis = Axis(1);
const K: Axis = Axis(2);
fn grid_over(below: Level) -> Placement {
Placement::new(Space::new(&[(M, 16), (N, 16)]), 0, vec![below])
}
fn descent(grid: Option<Instruction>, below: Level) -> Descent {
let lhs = Space::new(&[(M, 16), (K, 16)]);
let rhs = Space::new(&[(K, 16), (N, 16)]);
Descent::of(grid, &grid_over(below), &lhs, &rhs)
}
#[test]
fn one_fragment_contracts_here() {
let below = Levels::leaf(&[(M, 8), (N, 8)])
.walk(&[(M, 2), (N, 2)])
.level();
assert_eq!(descent(None, below), Descent::Here);
}
#[test]
fn a_grid_over_its_cells_loads_its_operands_once() {
let below = Levels::leaf(&[(M, 8), (N, 8)])
.walk(&[(M, 2), (N, 2)])
.level();
assert_eq!(
descent(Some(Instruction::Cmma), below),
Descent::Cells(Instruction::Cmma)
);
}
#[test]
fn a_grid_over_the_contraction_steps_it() {
let below = Levels::leaf(&[(M, 8), (N, 8), (K, 8)])
.walk(&[(M, 2), (N, 2), (K, 2)])
.level();
assert_eq!(descent(Some(Instruction::Cmma), below), Descent::Steps);
}
}