use cubecl::{
cmma::{MatrixIdent, MatrixLayout, MmaDefinition},
e2m1x2, e4m3,
prelude::*,
};
use super::block_scaled::{E2M1_PER_WORD, E2M1_SCALE_BLOCK};
use super::lane_runs::{instruction_depth, lays_out_lane_runs};
use crate::*;
const SCALES_PER_WORD: usize = 4;
#[cube]
pub(crate) fn steps_refusal<T: Numeric>(
src: &Tile,
#[comptime] ident: MatrixIdent,
#[comptime] layout: MatrixLayout,
#[comptime] shape: (usize, usize, usize),
) -> comptime_type!(Option<&'static str>) {
let (m, n, k) = comptime!(shape);
let depth = instruction_depth::<T>(m, n, k);
let laid_out = lays_out_lane_runs();
let bytes = T::size().comptime();
let packing = src.packing();
let levels = src.factor_levels();
let words = src.innermost_factor().holds_words();
let e2m1_weight = comptime!(
ident == MatrixIdent::B
&& layout == MatrixLayout::ColMajor
&& matches!(packing, Packing::Packed { field } if field.holds_e2m1())
);
comptime!(if k <= depth {
Some("the fragment is one instruction deep, which loads as one")
} else if packing == Packing::Plain && levels == 0 {
None
} else if (m, n, depth) != (16, 8, 16) || bytes != 2 || !laid_out {
Some("a step's registers are read off the 16-bit m16n8k16 layout, which this is not")
} else if !e2m1_weight || levels != 1 || !words {
Some(
"a packed window loads a step at a time only as a B of e2m1 codes along `k` under \
words of e4m3 block scales",
)
} else if !k.is_multiple_of(E2M1_SCALE_BLOCK * SCALES_PER_WORD) {
Some("a window under words of block scales is a whole number of words deep")
} else {
None
})
}
#[cube]
pub(crate) fn load_in_steps<T: Numeric, N: Size>(
src: &Tile,
fragment: &mut Array<Vector<T, N>>,
#[comptime] ident: MatrixIdent,
#[comptime] layout: MatrixLayout,
#[comptime] io: MmaIo,
#[comptime] shape: (usize, usize, usize),
) {
let refusal = steps_refusal::<T>(src, ident, layout, shape);
comptime!(if let Some(why) = refusal {
panic!(
"MmaData::load: a fragment deeper than its instruction loads a step at a time: {why}"
)
});
let (m, n, k) = comptime!(shape);
let depth = instruction_depth::<T>(m, n, k);
let def = MmaDefinition::<T, T, T>::new(m, n, depth);
let per_step = def.vectors_per_lane(ident);
let edges = comptime!(match ident {
MatrixIdent::A => (m, k),
_ => (k, n),
});
let packing = src.packing();
match comptime!(packing) {
Packing::Plain =>
{
#[unroll]
for s in 0..comptime!(k / depth) {
let mut step = Array::<Vector<T, N>>::new(per_step);
let offset = comptime!(s * depth);
load_fragment(src, &mut step, &def, ident, layout, io, edges, offset);
#[unroll]
for i in 0..per_step {
fragment[comptime!(s * per_step + i)] = step[i];
}
}
}
Packing::Packed { .. } => load_e2m1_weight::<T, N>(src, fragment, &def, k, n),
}
}
#[cube]
fn load_e2m1_weight<T: Numeric, N: Size>(
src: &Tile,
fragment: &mut Array<Vector<T, N>>,
def: &MmaDefinition<T, T, T>,
#[comptime] k: usize,
#[comptime] n: usize,
) {
let space = comptime!(src.place.space.clone());
let axes = comptime!(MatrixAxes::new(&space, n, k).unwrap_or_else(|no| {
panic!("MmaData::load: a stepped weight is an {n} x {k} matrix: {no:?}")
}));
let load = src.vector_tile();
let values = comptime!(load.values());
comptime!(assert!(
values.is_multiple_of(E2M1_PER_WORD) && k.is_multiple_of(values),
"MmaData::load: a stepped weight is read in whole words, whole lines of its {k} of `k`, \
and this one {values} values a load"
));
let per_line = comptime!(values / E2M1_PER_WORD);
let size!(WP) = per_line;
let words = src.nd_words::<WP>(comptime!(Guard::Checked));
let factor = src.innermost_factor();
let (first, column) = def.position_of_nth(UNIT_POS_PLANE, 0u32, MatrixIdent::B);
let shift = first / 2u32 * 8u32;
let mut codes = Array::<Vector<u32, WP>>::new(comptime!(k / values));
#[unroll]
for l in 0..comptime!(k / values) {
let at = TileMatrix::value_coords(
column,
comptime!((l * values) as u32),
0usize,
&space,
axes,
1usize,
);
codes[l] = words.read(load.index(&at, &space));
}
let scale_words = comptime!(k / (E2M1_SCALE_BLOCK * SCALES_PER_WORD));
let mut scales = Array::<Vector<T, Const<4>>>::new(scale_words);
#[unroll]
for w in 0..scale_words {
let at = TileMatrix::value_coords(
column,
comptime!((w * E2M1_SCALE_BLOCK * SCALES_PER_WORD) as u32),
0usize,
&space,
axes,
1usize,
);
let word = factor.word_at(&at, comptime!(space.clone()));
scales[w] = Vector::<T, Const<4>>::cast_from(Vector::<e4m3, Const<4>>::reinterpret(word));
}
#[unroll]
for s in 0..comptime!(k / E2M1_SCALE_BLOCK) {
let scale = scales[comptime!(s / SCALES_PER_WORD)].extract(comptime!(s % SCALES_PER_WORD));
#[unroll]
for i in 0..2usize {
let word = comptime!(2 * s + i);
let line = codes[comptime!(word / per_line)].extract(comptime!(word % per_line));
let pair = Vector::<e2m1x2, Const<4>>::reinterpret(line >> shift).extract(0usize);
let decoded = Vector::<T, Const<2>>::cast_from(Vector::<e2m1x2, Const<1>>::new(pair))
* Vector::<T, Const<2>>::new(scale);
let mut cells = Vector::<T, N>::empty();
cells.insert(0usize, decoded.extract(0usize));
cells.insert(1usize, decoded.extract(1usize));
fragment[comptime!(2 * s + i)] = cells;
}
}
}