use core::marker::PhantomData;
use cubecl::ir::{ExpandValue, VectorSize};
use cubecl::prelude::*;
use cubecl::std::tensor::{
ErasedTensor, ErasedTensorExpand, ErasedTensorOperationsExpand, ReadsLines, WriteOnly,
WritesLines,
};
use cubecl::unexpanded;
use crate::*;
#[derive(CubeType)]
pub struct SmemAccumulation<A: Numeric, T: Numeric> {
pub sink: Tile<A>,
pub source: Tile<T>,
}
#[cube]
impl<T: Numeric> Tile<T> {
pub fn smem_accumulation<A: Numeric>(&self) -> SmemAccumulation<A, T> {
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::<A>::smem_backed(
comptime!(space.clone()),
1usize,
comptime!(FillUnits::cube(units)),
Backing::<A>::new_WriteCall(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),
);
let source = Memory::<T>::smem_backed(
space,
1usize,
comptime!(FillUnits::cube(units)),
Backing::<T>::new_ReadCall(ErasedTensor::<T, ReadOnly>::of_smem_load::<A>(&values)),
comptime!(Packing::Plain),
comptime!(form.clone()),
RuntimeMap::integral(comptime!(form.physical_rank())),
ComptimeOption::new_None(),
comptime!(Write::Replace),
);
SmemAccumulation::<A, T> {
sink: sink.nested_like::<T>(self),
source: source.nested_like::<T>(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()))
}