use core::marker::PhantomData;
use cubecl::ir::{ExpandValue, VectorSize};
use cubecl::prelude::*;
use cubecl::unexpanded;
use cubecl_std::tensor::{
ErasedTensor, ErasedTensorExpand, ErasedTensorOperationsExpand, ReadsLines, WriteOnly,
WritesLines,
};
use crate::*;
#[derive(CubeType)]
pub struct SmemAccumulation {
pub sink: Tile,
pub source: Tile,
}
#[cube]
impl Tile {
pub fn smem_accumulation<A: Numeric>(&self) -> SmemAccumulation {
let define!(Served) = self.element;
let space = comptime!(self.place.space.clone());
let elem_bytes = A::size().comptime();
let form = comptime!(StageForm::dense(
&space,
1,
StageStorage::Strided,
LineBytes(elem_bytes)
));
let cells = comptime!(form.cells());
let values = Shared::<[Atomic<A>]>::new_slice(cells);
sync_cube();
let mut cell = UNIT_POS as usize;
while cell < cells {
values[cell].store(A::from_int(0));
cell += CUBE_DIM as usize;
}
sync_cube();
let units = self.units();
let sink = Memory::smem_backed::<A>(
comptime!(space.clone()),
1usize,
comptime!(FillUnits::cube(units)),
Backing::new_WriteCall(RawCall::of::<A>(
ErasedTensor::<A, WriteOnly>::of_smem_accumulate(&values),
)),
comptime!(Packing::Plain),
comptime!(form.clone()),
RuntimeMap::integral(comptime!(form.physical_rank())),
ComptimeOption::new_None(),
comptime!(Write::Accumulate),
comptime!(Overhang::Masked),
);
let source = Memory::smem_backed::<Served>(
space,
1usize,
comptime!(FillUnits::cube(units)),
Backing::new_ReadCall(RawCall::of::<Served>(
ErasedTensor::<Served, ReadOnly>::of_smem_load::<A>(&values),
)),
comptime!(Packing::Plain),
comptime!(form.clone()),
RuntimeMap::integral(comptime!(form.physical_rank())),
ComptimeOption::new_None(),
comptime!(Write::Replace),
comptime!(Overhang::Never),
);
SmemAccumulation {
sink: sink.nested_like(self),
source: source.nested_like(self),
}
}
}
pub(crate) trait SmemAccumulateSink<E: Numeric> {
fn of_smem_accumulate(_values: &Shared<[Atomic<E>]>) -> ErasedTensor<E, WriteOnly> {
unexpanded!()
}
fn __expand_of_smem_accumulate(
_scope: &Scope,
values: &<Shared<[Atomic<E>]> as CubeType>::ExpandType,
) -> ErasedTensorExpand<E, WriteOnly> {
ErasedTensorExpand::new(SmemAccumulate::<E> {
values: ExpandTypeClone::clone_unchecked(values),
_e: PhantomData,
})
}
}
impl<E: Numeric> SmemAccumulateSink<E> for ErasedTensor<E, WriteOnly> {}
pub(crate) trait SmemLoadSource<T: Numeric> {
fn of_smem_load<A: Numeric>(_values: &Shared<[Atomic<A>]>) -> ErasedTensor<T, ReadOnly> {
unexpanded!()
}
fn __expand_of_smem_load<A: Numeric>(
_scope: &Scope,
values: &<Shared<[Atomic<A>]> as CubeType>::ExpandType,
) -> ErasedTensorExpand<T, ReadOnly> {
ErasedTensorExpand::new(SmemLoad::<A, T> {
values: ExpandTypeClone::clone_unchecked(values),
_t: PhantomData,
})
}
}
impl<T: Numeric> SmemLoadSource<T> for ErasedTensor<T, ReadOnly> {}
struct SmemAccumulate<E: Numeric> {
values: <Shared<[Atomic<E>]> as CubeType>::ExpandType,
_e: PhantomData<E>,
}
impl<E: Numeric> ErasedTensorOperationsExpand<E> for SmemAccumulate<E> {
fn __expand_vector_size_method(&self, _scope: &Scope) -> VectorSize {
1
}
fn __expand_lines_method(&self, scope: &Scope) -> NativeExpand<usize> {
self.values.__expand_len_method(scope)
}
fn __expand_write_line_method(
&mut self,
scope: &Scope,
index: NativeExpand<usize>,
value: ExpandValue,
) {
accumulate_cell::expand::<E>(scope, &self.values, index, value.into());
}
}
impl<E: Numeric> WritesLines<E> for SmemAccumulate<E> {}
struct SmemLoad<A: Numeric, T: Numeric> {
values: <Shared<[Atomic<A>]> as CubeType>::ExpandType,
_t: PhantomData<T>,
}
impl<A: Numeric, T: Numeric> ErasedTensorOperationsExpand<T> for SmemLoad<A, T> {
fn __expand_vector_size_method(&self, _scope: &Scope) -> VectorSize {
1
}
fn __expand_lines_method(&self, scope: &Scope) -> NativeExpand<usize> {
self.values.__expand_len_method(scope)
}
fn __expand_read_line_method(&self, scope: &Scope, index: NativeExpand<usize>) -> ExpandValue {
load_cell::expand::<A, T>(scope, &self.values, index).expand
}
}
impl<A: Numeric, T: Numeric> ReadsLines<T> for SmemLoad<A, T> {}
#[cube]
fn accumulate_cell<E: Numeric>(
values: &Shared<[Atomic<E>]>,
index: usize,
value: Vector<E, Const<1>>,
) {
values[index].fetch_add(value.extract(0usize));
}
#[cube]
fn load_cell<A: Numeric, T: Numeric>(
values: &Shared<[Atomic<A>]>,
index: usize,
) -> Vector<T, Const<1>> {
Vector::new(T::cast_from(values[index].load()))
}