use cubecl::ir::Scope;
use cubecl::prelude::*;
use cubecl::unexpanded;
use super::payload::base::{Payload, PayloadExpand, StageSpec};
use super::payload::pair::OperandPair;
use super::plan::StagePlan;
use super::schedule::Prefetch;
use crate::*;
#[derive(CubeType, CubeTypeMut, IntoRuntime)]
#[cube(runtime_variants)]
pub enum Role {
Fill,
Compute,
}
#[derive(CubeType)]
pub struct Stages<T: CubeType> {
pub(crate) slots: Sequence<Slot<T>>,
pub(crate) sources: T,
#[allow(dead_code)]
#[cube(comptime)]
pub(crate) depth: usize,
#[cube(comptime)]
pub(crate) fillers: usize,
}
#[cube]
impl<T: CubeType> Stages<T> {
pub(crate) fn wrap(
slots: Sequence<Slot<T>>,
sources: T,
#[comptime] depth: usize,
#[comptime] fillers: usize,
) -> Stages<T> {
comptime!(assert!(
depth > 0,
"Stages: a pipeline needs at least one slot"
));
Stages::<T> {
slots,
sources,
depth,
fillers,
}
}
pub fn role(&self) -> Role {
if comptime!(self.fillers == 0) {
Role::new_Compute()
} else if UNIT_POS >= Meeting::consumers(comptime!(self.fillers)) {
Role::new_Fill()
} else {
Role::new_Compute()
}
}
pub fn slot_mut(&mut self, #[comptime] index: usize) -> &mut Slot<T> {
self.slots.index_mut(index)
}
}
#[cube]
impl<P: Payload<P> + Clone + CubeType<ExpandType: Clone>> Stages<P> {
#[allow(dead_code)]
pub(crate) fn new(
walk: &Walk,
sources: &P,
#[comptime] storage: StageStorage,
#[comptime] width: Option<usize>,
#[comptime] depth: usize,
#[comptime] decoded: Option<ElemType>,
) -> Stages<P> {
let operands = sources.operands();
let owner = walk.stage_owner();
let plan = comptime!(StagePlan::new(&operands, &walk.space, &walk.level, owner));
let spec = comptime!(StageSpec {
level: walk.level.clone(),
depth: walk.depth(),
storage,
width,
decoded,
owner: plan.owner(),
});
let first = sources.staged(
sources,
comptime!(spec.clone()),
comptime!(plan.refills(FIRST_SLOT)),
);
let mut slots = Sequence::<Slot<P>>::new();
#[unroll]
for slot in 0..depth {
let data = if comptime!(slot == FIRST_SLOT) {
first.clone()
} else {
sources.staged(
&first,
comptime!(spec.clone()),
comptime!(plan.refills(slot)),
)
};
slots.push(Slot::wrap(
data,
Meeting::new(
comptime!(plan.sync()),
comptime!(plan.collective_full()),
comptime!(plan.commits()),
comptime!(plan.fences()),
comptime!(plan.fillers()),
),
comptime!(plan.refills(slot)),
));
}
Stages::wrap(slots, sources.clone(), depth, comptime!(plan.fillers()))
}
pub fn free(&self) {
#[unroll]
for slot in 0..comptime!(self.depth) {
self.slots.index(slot).data.free();
}
}
pub fn fill(&mut self, #[comptime] slot: usize, region: &Region) {
let sources = self.sources.clone();
self.slot_mut(slot)
.refill(&sources, region, comptime!(Refill::EveryRegion));
}
}
#[cube]
impl Stages<OperandPair> {
pub fn smem(
walk: &Walk,
lhs: &Tile,
rhs: &Tile,
#[comptime] storage: StageStorage,
#[comptime] depth: usize,
#[comptime] decoded: Option<ElemType>,
) -> Stages<OperandPair> {
let sources = OperandPair {
lhs: lhs.clone(),
rhs: rhs.clone(),
};
Stages::new(walk, &sources, storage, comptime!(None), depth, decoded)
}
}
#[cube]
impl Stages<Tile> {
pub fn smem_single(
walk: &Walk,
input: &Tile,
#[comptime] storage: StageStorage,
#[comptime] depth: usize,
#[comptime] decoded: Option<ElemType>,
) -> Stages<Tile> {
Stages::new(walk, input, storage, comptime!(None), depth, decoded)
}
pub fn smem_single_at(
walk: &Walk,
input: &Tile,
#[comptime] storage: StageStorage,
#[comptime] width: Option<usize>,
#[comptime] depth: usize,
#[comptime] decoded: Option<ElemType>,
) -> Stages<Tile> {
Stages::new(walk, input, storage, width, depth, decoded)
}
}
define_size!(pub(crate) FL);
define_size!(pub(crate) FR);
pub(crate) type FetchBuffers<Lhs, Rhs> = (Array<Vector<Lhs, FL>>, Array<Vector<Rhs, FR>>);
#[cube]
fn register_fetched_widths(#[comptime] lhs: usize, #[comptime] rhs: usize) {
intrinsic!(|scope| {
scope.register_size::<FL>(lhs);
scope.register_size::<FR>(rhs);
});
}
#[cube]
impl Stages<OperandPair> {
#[allow(dead_code)] pub(crate) fn fetch_buffers<Lhs: Numeric, Rhs: Numeric>(&self) -> FetchBuffers<Lhs, Rhs> {
let staged = self.slots.index(FIRST_SLOT);
let lhs = staged.data.lhs.fetched_scalars();
let rhs = staged.data.rhs.fetched_scalars();
comptime!(assert!(
Prefetch::fits(lhs + rhs),
"Stages: a fill fetched into registers holds {lhs} + {rhs} scalars a unit, past the \
{MOST_FETCHED_SCALARS} a unit keeps beside its contraction"
));
let lhs_width = staged.data.lhs.vector_size();
let rhs_width = staged.data.rhs.vector_size();
register_fetched_widths(lhs_width, rhs_width);
(
Array::<Vector<Lhs, FL>>::new(comptime!(lhs / lhs_width)),
Array::<Vector<Rhs, FR>>::new(comptime!(rhs / rhs_width)),
)
}
#[allow(dead_code)] pub(crate) fn fetch<Lhs: Numeric, Rhs: Numeric>(
&self,
#[comptime] slot: usize,
sources: &OperandPair,
region: &Region,
lhs: &mut Array<Vector<Lhs, FL>>,
rhs: &mut Array<Vector<Rhs, FR>>,
) {
let staged = self.slots.index(slot);
staged.data.lhs.fetch_from(&sources.lhs.at(region), lhs);
staged.data.rhs.fetch_from(&sources.rhs.at(region), rhs);
}
#[allow(dead_code)] pub(crate) fn store<Lhs: Numeric, Rhs: Numeric>(
&mut self,
#[comptime] slot: usize,
lhs: &Array<Vector<Lhs, FL>>,
rhs: &Array<Vector<Rhs, FR>>,
) {
let slot = self.slot_mut(slot);
slot.data.lhs.store_fetched(lhs);
slot.data.rhs.store_fetched(rhs);
}
#[allow(dead_code)] pub(crate) fn assert_copied_by_every_unit(&self) {
let lhs = self.sources.lhs.delivery();
let rhs = self.sources.rhs.delivery();
comptime!(assert!(
self.fillers == 0 && lhs == Delivery::SyncPerUnit && rhs == Delivery::SyncPerUnit,
"Stages: a register-staged schedule fills its slots with every unit's own copy of \
memory; these stages are filled by {} plane(s) of their own, or their sources are \
delivered {lhs:?} and {rhs:?}",
self.fillers
));
}
}
impl Stages<OperandPair> {
pub fn consume(&mut self, _slot: usize, _compute: impl FnOnce(&Tile, &Tile)) {
unexpanded!()
}
}
impl StagesExpand<OperandPair> {
pub fn __expand_consume_method<F>(&mut self, scope: &Scope, slot: usize, compute: F)
where
F: FnOnce(&Scope, &TileExpand, &TileExpand),
{
self.__expand_slot_mut_method(scope, slot)
.__expand_consume_method(scope, compute);
}
}
impl Stages<Tile> {
pub fn consume(&mut self, _slot: usize, _compute: impl FnOnce(&Tile)) {
unexpanded!()
}
}
impl StagesExpand<Tile> {
pub fn __expand_consume_method<F>(&mut self, scope: &Scope, slot: usize, compute: F)
where
F: FnOnce(&Scope, &TileExpand),
{
self.__expand_slot_mut_method(scope, slot)
.__expand_consume_method(scope, compute);
}
}